1 package com.android.systemui.statusbar.notification.collection.notifcollection
2 
3 import android.os.Handler
4 import android.util.ArrayMap
5 import android.util.Log
6 import com.android.systemui.Dumpable
7 import com.android.systemui.statusbar.notification.collection.NotificationEntry
8 import java.io.PrintWriter
9 
10 /**
11  * A helpful class that implements the core contract of the lifetime extender internally,
12  * making it easier for coordinators to interact with them
13  */
14 abstract class SelfTrackingLifetimeExtender(
15     private val tag: String,
16     private val name: String,
17     private val debug: Boolean,
18     private val mainHandler: Handler
19 ) : NotifLifetimeExtender, Dumpable {
20     private lateinit var mCallback: NotifLifetimeExtender.OnEndLifetimeExtensionCallback
21     protected val mEntriesExtended = ArrayMap<String, NotificationEntry>()
22     private var mEnding = false
23 
24     /**
25      * When debugging, warn if the call is happening during and "end lifetime extension" call.
26      *
27      * Note: this will warn a lot! The pipeline explicitly re-invokes all lifetime extenders
28      * whenever one ends, giving all of them a chance to re-up their lifetime extension.
29      */
30     private fun warnIfEnding() {
31         if (debug && mEnding) Log.w(tag, "reentrant code while ending a lifetime extension")
32     }
33 
34     fun endAllLifetimeExtensions() {
35         // clear the map before iterating over a copy of the items, because the pipeline will
36         // always give us another chance to extend the lifetime again, and we don't want
37         // concurrent modification
38         val entries = mEntriesExtended.values.toList()
39         if (debug) Log.d(tag, "$name.endAllLifetimeExtensions() entries=$entries")
40         mEntriesExtended.clear()
41         warnIfEnding()
42         mEnding = true
43         entries.forEach { mCallback.onEndLifetimeExtension(this, it) }
44         mEnding = false
45     }
46 
47     fun endLifetimeExtensionAfterDelay(key: String, delayMillis: Long) {
48         if (debug) {
49             Log.d(tag, "$name.endLifetimeExtensionAfterDelay" +
50                     "(key=$key, delayMillis=$delayMillis)" +
51                     " isExtending=${isExtending(key)}")
52         }
53         if (isExtending(key)) {
54             mainHandler.postDelayed({ endLifetimeExtension(key) }, delayMillis)
55         }
56     }
57 
58     fun endLifetimeExtension(key: String) {
59         if (debug) {
60             Log.d(tag, "$name.endLifetimeExtension(key=$key)" +
61                     " isExtending=${isExtending(key)}")
62         }
63         warnIfEnding()
64         mEnding = true
65         mEntriesExtended.remove(key)?.let { removedEntry ->
66             mCallback.onEndLifetimeExtension(this, removedEntry)
67         }
68         mEnding = false
69     }
70 
71     fun isExtending(key: String) = mEntriesExtended.contains(key)
72 
73     final override fun getName(): String = name
74 
75     final override fun maybeExtendLifetime(entry: NotificationEntry, reason: Int): Boolean {
76         val shouldExtend = queryShouldExtendLifetime(entry)
77         if (debug) {
78             Log.d(tag, "$name.shouldExtendLifetime(key=${entry.key}, reason=$reason)" +
79                     " isExtending=${isExtending(entry.key)}" +
80                     " shouldExtend=$shouldExtend")
81         }
82         warnIfEnding()
83         if (shouldExtend && mEntriesExtended.put(entry.key, entry) == null) {
84             onStartedLifetimeExtension(entry)
85         }
86         return shouldExtend
87     }
88 
89     final override fun cancelLifetimeExtension(entry: NotificationEntry) {
90         if (debug) {
91             Log.d(tag, "$name.cancelLifetimeExtension(key=${entry.key})" +
92                     " isExtending=${isExtending(entry.key)}")
93         }
94         warnIfEnding()
95         mEntriesExtended.remove(entry.key)
96         onCanceledLifetimeExtension(entry)
97     }
98 
99     abstract fun queryShouldExtendLifetime(entry: NotificationEntry): Boolean
100     open fun onStartedLifetimeExtension(entry: NotificationEntry) {}
101     open fun onCanceledLifetimeExtension(entry: NotificationEntry) {}
102 
103     final override fun setCallback(callback: NotifLifetimeExtender.OnEndLifetimeExtensionCallback) {
104         mCallback = callback
105     }
106 
107     final override fun dump(pw: PrintWriter, args: Array<out String>) {
108         pw.println("LifetimeExtender: $name:")
109         pw.println("  mEntriesExtended: ${mEntriesExtended.size}")
110         mEntriesExtended.forEach { pw.println("  * ${it.key}") }
111     }
112 }