Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 736b899a authored by Fabian Kozynski's avatar Fabian Kozynski Committed by Android (Google) Code Review
Browse files

Merge "Adds a DumpController to SystemUI Dependencies" into qt-dev

parents 6dc03884 d67cffdb
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -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));
+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) }
        }
    }
}
+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)
    }
}