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