diff options
| author | 2019-04-04 09:43:45 -0400 | |
|---|---|---|
| committer | 2019-04-10 17:28:31 -0400 | |
| commit | d67cffdba02aab11dceed20c2d00832fb0e9ae4c (patch) | |
| tree | e45d9e6e21299f52a35dc3ff478348b167818c2d | |
| parent | 9dfc48d06e0156c52df748ff64aa3bd2e09cd211 (diff) | |
Adds a DumpController to SystemUI Dependencies
The DumpController allows any SystemUI Dumpable class to subscribe to it
and be dumped during dumpsys of Dependency.
Test: manual (dumpsys Dependency shows "DumpController state)
Test: atest DumpControllerTest
Bug: 129544734
Change-Id: If8d3ec667d99a523e5ae25db84173d9b04b6829c
3 files changed, 183 insertions, 1 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 70f2ccee7d60..a4219402259e 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -293,6 +293,7 @@ public class Dependency extends SystemUI { @Inject Lazy<DevicePolicyManagerWrapper> mDevicePolicyManagerWrapper; @Inject Lazy<PackageManagerWrapper> mPackageManagerWrapper; @Inject Lazy<SensorPrivacyController> mSensorPrivacyController; + @Inject Lazy<DumpController> mDumpController; @Inject public Dependency() { @@ -464,7 +465,7 @@ public class Dependency extends SystemUI { mProviders.put(DevicePolicyManagerWrapper.class, mDevicePolicyManagerWrapper::get); mProviders.put(PackageManagerWrapper.class, mPackageManagerWrapper::get); mProviders.put(SensorPrivacyController.class, mSensorPrivacyController::get); - + mProviders.put(DumpController.class, mDumpController::get); // TODO(b/118592525): to support multi-display , we start to add something which is // per-display, while others may be global. I think it's time to add @@ -478,6 +479,11 @@ public class Dependency extends SystemUI { @Override public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { super.dump(fd, pw, args); + + // Make sure that the DumpController gets added to mDependencies, as they are only added + // with Dependency#get. + getDependency(DumpController.class); + pw.println("Dumping existing controllers:"); mDependencies.values().stream().filter(obj -> obj instanceof Dumpable) .forEach(o -> ((Dumpable) o).dump(fd, pw, args)); diff --git a/packages/SystemUI/src/com/android/systemui/DumpController.kt b/packages/SystemUI/src/com/android/systemui/DumpController.kt new file mode 100644 index 000000000000..646abb5c2c5a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/DumpController.kt @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui + +import android.util.Log +import androidx.annotation.GuardedBy +import com.android.internal.util.Preconditions +import java.io.FileDescriptor +import java.io.PrintWriter +import java.lang.ref.WeakReference +import javax.inject.Inject +import javax.inject.Singleton + +// TODO: Move all Dumpable dependencies to use DumpController +/** + * Controller that allows any [Dumpable] to subscribe and be dumped along with other SystemUI + * dependencies. + */ +@Singleton +class DumpController @Inject constructor() : Dumpable { + + companion object { + private const val TAG = "DumpController" + private const val DEBUG = false + } + + @GuardedBy("listeners") + private val listeners = mutableListOf<WeakReference<Dumpable>>() + val numListeners: Int + get() = listeners.size + + /** + * Adds a [Dumpable] listener to be dumped. It will only be added if it is not already tracked. + * + * @param listener the [Dumpable] to be added + */ + fun addListener(listener: Dumpable) { + Preconditions.checkNotNull(listener, "The listener to be added cannot be null") + if (DEBUG) Log.v(TAG, "*** register callback for $listener") + synchronized<Unit>(listeners) { + if (listeners.any { it.get() == listener }) { + if (DEBUG) { + Log.e(TAG, "Object tried to add another callback") + } + } else { + listeners.add(WeakReference(listener)) + } + } + } + + /** + * Removes a listener from the list of elements to be dumped. + * + * @param listener the [Dumpable] to be removed. + */ + fun removeListener(listener: Dumpable) { + if (DEBUG) Log.v(TAG, "*** unregister callback for $listener") + synchronized(listeners) { + listeners.removeAll { it.get() == listener || it.get() == null } + } + } + + /** + * Dump all the [Dumpable] registered with the controller + */ + override fun dump(fd: FileDescriptor?, pw: PrintWriter, args: Array<String>?) { + pw.println("DumpController state:") + synchronized(listeners) { + listeners.forEach { it.get()?.dump(fd, pw, args) } + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/DumpControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/DumpControllerTest.kt new file mode 100644 index 000000000000..cca35ca39606 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/DumpControllerTest.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any +import org.mockito.Mock +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations +import java.io.FileDescriptor +import java.io.PrintWriter + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class DumpControllerTest : SysuiTestCase() { + + private lateinit var controller: DumpController + @Mock private lateinit var callback1: Dumpable + @Mock private lateinit var callback2: Dumpable + @Mock private lateinit var fd: FileDescriptor + @Mock private lateinit var pw: PrintWriter + private val args = emptyArray<String>() + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + controller = DumpController() +// Debug.waitForDebugger() + } + + @Test + fun testListenerOnlyAddedOnce() { + controller.apply { + addListener(callback1) + addListener(callback1) + } + assertEquals(1, controller.numListeners) + + controller.dump(fd, pw, args) + verify(callback1 /* only once */).dump(fd, pw, args) + } + + @Test + fun testListenersCalledOnDump() { + controller.apply { + addListener(callback1) + addListener(callback2) + } + + controller.dump(fd, pw, args) + + verify(callback1 /* only once */).dump(fd, pw, args) + verify(callback2 /* only once */).dump(fd, pw, args) + } + + @Test + fun testRemoveListener() { + controller.apply { + addListener(callback1) + addListener(callback2) + removeListener(callback1) + } + + controller.dump(fd, pw, args) + + verify(callback1, never()).dump(any(), any(), any()) + verify(callback2 /* only once */).dump(fd, pw, args) + } +} |