Loading packages/SystemUI/src/com/android/systemui/Dependency.java +7 −1 Original line number Diff line number Diff line Loading @@ -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() { Loading Loading @@ -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 Loading @@ -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)); Loading packages/SystemUI/src/com/android/systemui/DumpController.kt 0 → 100644 +86 −0 Original line number Diff line number Diff line /* * 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) } } } } packages/SystemUI/tests/src/com/android/systemui/DumpControllerTest.kt 0 → 100644 +90 −0 Original line number Diff line number Diff line /* * 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) } } Loading
packages/SystemUI/src/com/android/systemui/Dependency.java +7 −1 Original line number Diff line number Diff line Loading @@ -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() { Loading Loading @@ -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 Loading @@ -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)); Loading
packages/SystemUI/src/com/android/systemui/DumpController.kt 0 → 100644 +86 −0 Original line number Diff line number Diff line /* * 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) } } } }
packages/SystemUI/tests/src/com/android/systemui/DumpControllerTest.kt 0 → 100644 +90 −0 Original line number Diff line number Diff line /* * 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) } }