1 package com.android.systemui.broadcast
2 
3 import android.annotation.AnyThread
4 import android.content.Context
5 import android.content.Intent
6 import android.os.Bundle
7 import android.os.UserHandle
8 import com.android.systemui.dagger.SysUISingleton
9 import com.android.systemui.dagger.qualifiers.Background
10 import com.android.systemui.util.wakelock.WakeLock
11 import java.util.concurrent.Executor
12 import javax.inject.Inject
13 
14 /**
15  * SystemUI master Broadcast sender
16  *
17  * This class dispatches broadcasts on background thread to avoid synchronous call to binder. Use
18  * this class instead of calling [Context.sendBroadcast] directly.
19  */
20 @SysUISingleton
21 class BroadcastSender @Inject constructor(
22     private val context: Context,
23     private val wakeLockBuilder: WakeLock.Builder,
24     @Background private val bgExecutor: Executor
25 ) {
26 
27     /**
28      * Sends broadcast via [Context.sendBroadcast] on background thread to avoid blocking
29      * synchronous binder call.
30      */
31     @AnyThread
sendBroadcastnull32     fun sendBroadcast(intent: Intent) {
33         sendInBackground("$intent") {
34             context.sendBroadcast(intent)
35         }
36     }
37 
38     /**
39      * Sends broadcast via [Context.sendBroadcast] on background thread to avoid blocking
40      * synchronous binder call.
41      */
42     @AnyThread
sendBroadcastnull43     fun sendBroadcast(intent: Intent, receiverPermission: String?) {
44         sendInBackground("$intent") {
45             context.sendBroadcast(intent, receiverPermission)
46         }
47     }
48 
49     /**
50      * Sends broadcast via [Context.sendBroadcastAsUser] on background thread to avoid blocking
51      * synchronous binder call.
52      */
53     @AnyThread
sendBroadcastAsUsernull54     fun sendBroadcastAsUser(intent: Intent, userHandle: UserHandle) {
55         sendInBackground("$intent") {
56             context.sendBroadcastAsUser(intent, userHandle)
57         }
58     }
59 
60     /**
61      * Sends broadcast via [Context.sendBroadcastAsUser] on background thread to avoid blocking
62      * synchronous binder call.
63      */
64     @AnyThread
sendBroadcastAsUsernull65     fun sendBroadcastAsUser(intent: Intent, userHandle: UserHandle, receiverPermission: String?) {
66         sendInBackground("$intent") {
67             context.sendBroadcastAsUser(intent, userHandle, receiverPermission)
68         }
69     }
70 
71     /**
72      * Sends broadcast via [Context.sendBroadcastAsUser] on background thread to avoid blocking
73      * synchronous binder call.
74      */
75     @AnyThread
sendBroadcastAsUsernull76     fun sendBroadcastAsUser(
77         intent: Intent,
78         userHandle: UserHandle,
79         receiverPermission: String?,
80         options: Bundle?
81     ) {
82         sendInBackground("$intent") {
83             context.sendBroadcastAsUser(intent, userHandle, receiverPermission, options)
84         }
85     }
86 
87     /**
88      * Sends broadcast via [Context.sendBroadcastAsUser] on background thread to avoid blocking
89      * synchronous binder call.
90      */
91     @AnyThread
sendBroadcastAsUsernull92     fun sendBroadcastAsUser(
93         intent: Intent,
94         userHandle: UserHandle,
95         receiverPermission: String?,
96         appOp: Int
97     ) {
98         sendInBackground("$intent") {
99             context.sendBroadcastAsUser(intent, userHandle, receiverPermission, appOp)
100         }
101     }
102 
103     /**
104      * Sends [Intent.ACTION_CLOSE_SYSTEM_DIALOGS] broadcast to the system.
105      */
106     @AnyThread
closeSystemDialogsnull107     fun closeSystemDialogs() {
108         sendInBackground("closeSystemDialogs") {
109             context.closeSystemDialogs()
110         }
111     }
112 
113     /**
114      * Dispatches parameter on background executor while holding a wakelock.
115      */
sendInBackgroundnull116     private fun sendInBackground(reason: String, callable: () -> Unit) {
117         val broadcastWakelock = wakeLockBuilder.setTag(WAKE_LOCK_TAG)
118                                 .setMaxTimeout(5000)
119                                 .build()
120         broadcastWakelock.acquire(reason)
121         bgExecutor.execute {
122             try {
123                 callable.invoke()
124             } finally {
125                 broadcastWakelock.release(reason)
126             }
127         }
128     }
129 
130     companion object {
131         private const val WAKE_LOCK_TAG = "SysUI:BroadcastSender"
132     }
133 }