From de6534331142aa140c787e4365c297790256bc2b Mon Sep 17 00:00:00 2001 From: Fabian Kozynski Date: Tue, 20 Oct 2020 14:52:15 -0400 Subject: [PATCH] Add logging for privacy indicators Test: manual Test: atest PrivacyItemControllerTest Change-Id: I1e1d638584315bc4461d414291d362d5c72cc026 --- .../systemui/log/dagger/LogModule.java | 12 +++ .../systemui/log/dagger/PrivacyLog.java | 33 +++++++ .../android/systemui/privacy/PrivacyItem.kt | 27 ++++-- .../systemui/privacy/PrivacyItemController.kt | 6 ++ .../systemui/privacy/logging/PrivacyLogger.kt | 87 +++++++++++++++++++ .../qs/QuickStatusBarHeaderController.java | 8 +- .../statusbar/phone/PhoneStatusBarPolicy.java | 7 +- .../privacy/PrivacyItemControllerFlagsTest.kt | 7 +- .../privacy/PrivacyItemControllerTest.kt | 47 +++++++++- .../qs/QuickStatusBarHeaderControllerTest.kt | 6 +- 10 files changed, 225 insertions(+), 15 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/log/dagger/PrivacyLog.java create mode 100644 packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index e3ee2a10821bf..fff185b99a1ec 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -120,6 +120,18 @@ public class LogModule { return buffer; } + /** Provides a logging buffer for all logs related to privacy indicators in SystemUI. */ + @Provides + @SysUISingleton + @PrivacyLog + public static LogBuffer providePrivacyLogBuffer( + LogcatEchoTracker bufferFilter, + DumpManager dumpManager) { + LogBuffer buffer = new LogBuffer(("PrivacyLog"), 100, 10, bufferFilter); + buffer.attach(dumpManager); + return buffer; + } + /** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */ @Provides @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/PrivacyLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/PrivacyLog.java new file mode 100644 index 0000000000000..e96e532f94bf3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/PrivacyLog.java @@ -0,0 +1,33 @@ +/* + * 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.log.dagger; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.android.systemui.log.LogBuffer; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +/** A {@link LogBuffer} for privacy indicator-related messages. */ +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface PrivacyLog { +} diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt index 3da1363f2a56c..7359e79b26f59 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt @@ -19,20 +19,31 @@ import com.android.systemui.R typealias Privacy = PrivacyType -enum class PrivacyType(val nameId: Int, val iconId: Int) { +enum class PrivacyType(val nameId: Int, val iconId: Int, val logName: String) { // This is uses the icons used by the corresponding permission groups in the AndroidManifest - TYPE_CAMERA(R.string.privacy_type_camera, - com.android.internal.R.drawable.perm_group_camera), - TYPE_MICROPHONE(R.string.privacy_type_microphone, - com.android.internal.R.drawable.perm_group_microphone), - TYPE_LOCATION(R.string.privacy_type_location, - com.android.internal.R.drawable.perm_group_location); + TYPE_CAMERA( + R.string.privacy_type_camera, + com.android.internal.R.drawable.perm_group_camera, + "camera" + ), + TYPE_MICROPHONE( + R.string.privacy_type_microphone, + com.android.internal.R.drawable.perm_group_microphone, + "microphone" + ), + TYPE_LOCATION( + R.string.privacy_type_location, + com.android.internal.R.drawable.perm_group_location, + "location" + ); fun getName(context: Context) = context.resources.getString(nameId) fun getIcon(context: Context) = context.resources.getDrawable(iconId, context.theme) } -data class PrivacyItem(val privacyType: PrivacyType, val application: PrivacyApplication) +data class PrivacyItem(val privacyType: PrivacyType, val application: PrivacyApplication) { + fun toLog(): String = "(${privacyType.logName}, ${application.packageName}(${application.uid}))" +} data class PrivacyApplication(val packageName: String, val uid: Int) diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt index dc5ba693a658e..87ffbd4651097 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt @@ -32,6 +32,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager +import com.android.systemui.privacy.logging.PrivacyLogger import com.android.systemui.settings.UserTracker import com.android.systemui.util.DeviceConfigProxy import com.android.systemui.util.concurrency.DelayableExecutor @@ -48,6 +49,7 @@ class PrivacyItemController @Inject constructor( @Background private val bgExecutor: Executor, private val deviceConfigProxy: DeviceConfigProxy, private val userTracker: UserTracker, + private val logger: PrivacyLogger, dumpManager: DumpManager ) : Dumpable { @@ -158,6 +160,7 @@ class PrivacyItemController @Inject constructor( } val userId = UserHandle.getUserId(uid) if (userId in currentUserIds) { + logger.logUpdatedItemFromAppOps(code, uid, packageName, active) update(false) } } @@ -194,6 +197,7 @@ class PrivacyItemController @Inject constructor( bgExecutor.execute { if (updateUsers) { currentUserIds = userTracker.userProfiles.map { it.id } + logger.logCurrentProfilesChanged(currentUserIds) } updateListAndNotifyChanges.run() } @@ -260,6 +264,8 @@ class PrivacyItemController @Inject constructor( } val list = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) } .mapNotNull { toPrivacyItem(it) }.distinct() + logger.logUpdatedPrivacyItemsList( + list.joinToString(separator = ", ", transform = PrivacyItem::toLog)) privacyList = list } diff --git a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt new file mode 100644 index 0000000000000..c88676e713b38 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2020 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.privacy.logging + +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel +import com.android.systemui.log.LogMessage +import com.android.systemui.log.dagger.PrivacyLog +import javax.inject.Inject + +private const val TAG = "PrivacyLog" + +class PrivacyLogger @Inject constructor( + @PrivacyLog private val buffer: LogBuffer +) { + + fun logUpdatedItemFromAppOps(code: Int, uid: Int, packageName: String, active: Boolean) { + log(LogLevel.INFO, { + int1 = code + int2 = uid + str1 = packageName + bool1 = active + }, { + "App Op: $int1 for $str1($int2), active=$bool1" + }) + } + + fun logUpdatedPrivacyItemsList(listAsString: String) { + log(LogLevel.INFO, { + str1 = listAsString + }, { + "Updated list: $str1" + }) + } + + fun logCurrentProfilesChanged(profiles: List) { + log(LogLevel.INFO, { + str1 = profiles.toString() + }, { + "Profiles changed: $str1" + }) + } + + fun logChipVisible(visible: Boolean) { + log(LogLevel.INFO, { + bool1 = visible + }, { + "Chip visible: $bool1" + }) + } + + fun logStatusBarIconsVisible( + showCamera: Boolean, + showMichrophone: Boolean, + showLocation: Boolean + ) { + log(LogLevel.INFO, { + bool1 = showCamera + bool2 = showMichrophone + bool3 = showLocation + }, { + "Status bar icons visible: camera=$bool1, microphone=$bool2, location=$bool3" + }) + } + + private inline fun log( + logLevel: LogLevel, + initializer: LogMessage.() -> Unit, + noinline printer: LogMessage.() -> String + ) { + buffer.log(TAG, logLevel, initializer, printer) + } +} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java index 44f847c34e2bb..32904a21cd3c5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java @@ -43,6 +43,7 @@ import com.android.systemui.privacy.OngoingPrivacyChip; import com.android.systemui.privacy.PrivacyChipEvent; import com.android.systemui.privacy.PrivacyItem; import com.android.systemui.privacy.PrivacyItemController; +import com.android.systemui.privacy.logging.PrivacyLogger; import com.android.systemui.qs.carrier.QSCarrierGroupController; import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.settings.UserTracker; @@ -90,6 +91,7 @@ class QuickStatusBarHeaderController extends ViewController> @Captor @@ -102,8 +106,8 @@ class PrivacyItemControllerTest : SysuiTestCase() { executor, deviceConfigProxy, userTracker, - dumpManager - ) + logger, + dumpManager) } @Before @@ -300,6 +304,45 @@ class PrivacyItemControllerTest : SysuiTestCase() { verify(callback, never()).onPrivacyItemsChanged(any()) } + @Test + fun testLogActiveChanged() { + privacyItemController.addCallback(callback) + executor.runAllReady() + + verify(appOpsController).addCallback(any(), capture(argCaptorCallback)) + argCaptorCallback.value.onActiveStateChanged( + AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true) + + verify(logger).logUpdatedItemFromAppOps( + AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true) + } + + @Test + fun testLogListUpdated() { + doReturn(listOf( + AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0)) + ).`when`(appOpsController).getActiveAppOpsForUser(anyInt()) + + privacyItemController.addCallback(callback) + executor.runAllReady() + + verify(appOpsController).addCallback(any(), capture(argCaptorCallback)) + argCaptorCallback.value.onActiveStateChanged( + AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true) + executor.runAllReady() + + val expected = PrivacyItem( + PrivacyType.TYPE_LOCATION, + PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID) + ) + + val captor = argumentCaptor() + verify(logger, atLeastOnce()).logUpdatedPrivacyItemsList(capture(captor)) + // Let's look at the last log + val values = captor.allValues + assertTrue(values[values.size - 1].contains(expected.toLog())) + } + private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value) private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt index 2269951988430..26b5d26387b43 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt @@ -27,6 +27,7 @@ import com.android.systemui.demomode.DemoModeController import com.android.systemui.plugins.ActivityStarter import com.android.systemui.privacy.OngoingPrivacyChip import com.android.systemui.privacy.PrivacyItemController +import com.android.systemui.privacy.logging.PrivacyLogger import com.android.systemui.qs.carrier.QSCarrierGroup import com.android.systemui.qs.carrier.QSCarrierGroupController import com.android.systemui.settings.UserTracker @@ -86,6 +87,8 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { @Mock private lateinit var qsCarrierGroupController: QSCarrierGroupController @Mock + private lateinit var privacyLogger: PrivacyLogger + @Mock private lateinit var iconContainer: StatusIconContainer @Mock private lateinit var qsCarrierGroup: QSCarrierGroup @@ -123,7 +126,8 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { demoModeController, userTracker, quickQSPanelController, - qsCarrierGroupControllerBuilder + qsCarrierGroupControllerBuilder, + privacyLogger ) }