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 }