• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 package com.android.launcher3.util
17 
18 import android.os.Handler
19 import android.os.HandlerThread
20 import android.os.Looper
21 import android.os.Process
22 import android.os.Process.THREAD_PRIORITY_FOREGROUND
23 import androidx.annotation.IntDef
24 import java.util.concurrent.AbstractExecutorService
25 import java.util.concurrent.TimeUnit
26 import kotlin.annotation.AnnotationRetention.SOURCE
27 
28 /** Extension of [AbstractExecutorService] which executed on a provided looper. */
29 class LooperExecutor(looper: Looper, private val defaultPriority: Int) : AbstractExecutorService() {
30     val handler: Handler = Handler(looper)
31 
32     @JvmOverloads
33     constructor(
34         name: String,
35         defaultPriority: Int = Process.THREAD_PRIORITY_DEFAULT,
36     ) : this(createAndStartNewLooper(name, defaultPriority), defaultPriority)
37 
38     /** Returns the thread for this executor */
39     val thread: Thread
40         get() = handler.looper.thread
41 
42     /** Returns the looper for this executor */
43     val looper: Looper
44         get() = handler.looper
45 
46     @ElevationCaller private var elevationFlags: Int = 0
47 
executenull48     override fun execute(runnable: Runnable) {
49         if (handler.looper == Looper.myLooper()) {
50             runnable.run()
51         } else {
52             handler.post(runnable)
53         }
54     }
55 
56     /** Same as execute, but never runs the action inline. */
postnull57     fun post(runnable: Runnable) {
58         handler.post(runnable)
59     }
60 
61     @Deprecated("Not supported and throws an exception when used")
shutdownnull62     override fun shutdown() {
63         throw UnsupportedOperationException()
64     }
65 
66     @Deprecated("Not supported and throws an exception when used.")
shutdownNownull67     override fun shutdownNow(): List<Runnable> {
68         throw UnsupportedOperationException()
69     }
70 
isShutdownnull71     override fun isShutdown() = false
72 
73     override fun isTerminated() = false
74 
75     @Deprecated("Not supported and throws an exception when used.")
76     override fun awaitTermination(l: Long, timeUnit: TimeUnit): Boolean {
77         throw UnsupportedOperationException()
78     }
79 
80     /**
81      * Increases the priority of the thread for the [caller]. Multiple calls with same caller are
82      * ignored. The priority is reset once wall callers have restored priority
83      */
elevatePrioritynull84     fun elevatePriority(@ElevationCaller caller: Int) {
85         val wasElevated = elevationFlags != 0
86         elevationFlags = elevationFlags.or(caller)
87         if (elevationFlags != 0 && !wasElevated)
88             Process.setThreadPriority(
89                 (thread as HandlerThread).threadId,
90                 THREAD_PRIORITY_FOREGROUND,
91             )
92     }
93 
94     /** Restores to default priority if it was previously elevated */
restorePrioritynull95     fun restorePriority(@ElevationCaller caller: Int) {
96         val wasElevated = elevationFlags != 0
97         elevationFlags = elevationFlags.and(caller.inv())
98         if (elevationFlags == 0 && wasElevated)
99             Process.setThreadPriority((thread as HandlerThread).threadId, defaultPriority)
100     }
101 
102     @Retention(SOURCE)
103     @IntDef(value = [CALLER_LOADER_TASK, CALLER_ICON_CACHE], flag = true)
104     annotation class ElevationCaller
105 
106     companion object {
107         /** Utility method to get a started handler thread statically with the provided priority */
108         @JvmOverloads
109         @JvmStatic
createAndStartNewLoopernull110         fun createAndStartNewLooper(
111             name: String,
112             priority: Int = Process.THREAD_PRIORITY_DEFAULT,
113         ): Looper = HandlerThread(name, priority).apply { start() }.looper
114 
115         const val CALLER_LOADER_TASK = 1 shl 0
116         const val CALLER_ICON_CACHE = 1 shl 1
117     }
118 }
119