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