Add logging for privacy indicators
Test: manual Test: atest PrivacyItemControllerTest Change-Id: I1e1d638584315bc4461d414291d362d5c72cc026
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Int>) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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<QuickStatusBarHeader
|
||||
private final StatusIconContainer mIconContainer;
|
||||
private final StatusBarIconController.TintedIconManager mIconManager;
|
||||
private final DemoMode mDemoModeReceiver;
|
||||
private final PrivacyLogger mPrivacyLogger;
|
||||
|
||||
private boolean mListening;
|
||||
private AlarmClockInfo mNextAlarm;
|
||||
@@ -213,7 +215,8 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
|
||||
QSTileHost qsTileHost, StatusBarIconController statusBarIconController,
|
||||
CommandQueue commandQueue, DemoModeController demoModeController,
|
||||
UserTracker userTracker, QuickQSPanelController quickQSPanelController,
|
||||
QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder) {
|
||||
QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder,
|
||||
PrivacyLogger privacyLogger) {
|
||||
super(view);
|
||||
mZenModeController = zenModeController;
|
||||
mNextAlarmController = nextAlarmController;
|
||||
@@ -228,6 +231,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
|
||||
mUserTracker = userTracker;
|
||||
mLifecycle = new LifecycleRegistry(mLifecycleOwner);
|
||||
mHeaderQsPanelController = quickQSPanelController;
|
||||
mPrivacyLogger = privacyLogger;
|
||||
|
||||
mQSCarrierGroupController = qsCarrierGroupControllerBuilder
|
||||
.setQSCarrierGroup(mView.findViewById(R.id.carrier_group))
|
||||
@@ -323,6 +327,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
|
||||
private void setChipVisibility(boolean chipVisible) {
|
||||
if (chipVisible && getChipEnabled()) {
|
||||
mPrivacyChip.setVisibility(View.VISIBLE);
|
||||
mPrivacyLogger.logChipVisible(true);
|
||||
// Makes sure that the chip is logged as viewed at most once each time QS is opened
|
||||
// mListening makes sure that the callback didn't return after the user closed QS
|
||||
if (!mPrivacyChipLogged && mListening) {
|
||||
@@ -330,6 +335,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
|
||||
mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_VIEW);
|
||||
}
|
||||
} else {
|
||||
mPrivacyLogger.logChipVisible(false);
|
||||
mPrivacyChip.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ import com.android.systemui.dagger.qualifiers.UiBackground;
|
||||
import com.android.systemui.privacy.PrivacyItem;
|
||||
import com.android.systemui.privacy.PrivacyItemController;
|
||||
import com.android.systemui.privacy.PrivacyType;
|
||||
import com.android.systemui.privacy.logging.PrivacyLogger;
|
||||
import com.android.systemui.qs.tiles.DndTile;
|
||||
import com.android.systemui.qs.tiles.RotationLockTile;
|
||||
import com.android.systemui.screenrecord.RecordingController;
|
||||
@@ -145,6 +146,7 @@ public class PhoneStatusBarPolicy
|
||||
private final SensorPrivacyController mSensorPrivacyController;
|
||||
private final RecordingController mRecordingController;
|
||||
private final RingerModeTracker mRingerModeTracker;
|
||||
private final PrivacyLogger mPrivacyLogger;
|
||||
|
||||
private boolean mZenVisible;
|
||||
private boolean mVolumeVisible;
|
||||
@@ -172,7 +174,8 @@ public class PhoneStatusBarPolicy
|
||||
@Nullable TelecomManager telecomManager, @DisplayId int displayId,
|
||||
@Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil,
|
||||
RingerModeTracker ringerModeTracker,
|
||||
PrivacyItemController privacyItemController) {
|
||||
PrivacyItemController privacyItemController,
|
||||
PrivacyLogger privacyLogger) {
|
||||
mIconController = iconController;
|
||||
mCommandQueue = commandQueue;
|
||||
mBroadcastDispatcher = broadcastDispatcher;
|
||||
@@ -197,6 +200,7 @@ public class PhoneStatusBarPolicy
|
||||
mUiBgExecutor = uiBgExecutor;
|
||||
mTelecomManager = telecomManager;
|
||||
mRingerModeTracker = ringerModeTracker;
|
||||
mPrivacyLogger = privacyLogger;
|
||||
|
||||
mSlotCast = resources.getString(com.android.internal.R.string.status_bar_cast);
|
||||
mSlotHotspot = resources.getString(com.android.internal.R.string.status_bar_hotspot);
|
||||
@@ -675,6 +679,7 @@ public class PhoneStatusBarPolicy
|
||||
|| mPrivacyItemController.getLocationAvailable()) {
|
||||
mIconController.setIconVisibility(mSlotLocation, showLocation);
|
||||
}
|
||||
mPrivacyLogger.logStatusBarIconsVisible(showCamera, showMicrophone, showLocation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.appops.AppOpsController
|
||||
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.DeviceConfigProxyFake
|
||||
@@ -65,6 +66,8 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
|
||||
private lateinit var dumpManager: DumpManager
|
||||
@Mock
|
||||
private lateinit var userTracker: UserTracker
|
||||
@Mock
|
||||
private lateinit var logger: PrivacyLogger
|
||||
|
||||
private lateinit var privacyItemController: PrivacyItemController
|
||||
private lateinit var executor: FakeExecutor
|
||||
@@ -77,8 +80,8 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
|
||||
executor,
|
||||
deviceConfigProxy,
|
||||
userTracker,
|
||||
dumpManager
|
||||
)
|
||||
logger,
|
||||
dumpManager)
|
||||
}
|
||||
|
||||
@Before
|
||||
|
||||
@@ -29,10 +29,12 @@ import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.appops.AppOpItem
|
||||
import com.android.systemui.appops.AppOpsController
|
||||
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.DeviceConfigProxyFake
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.mockito.argumentCaptor
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import org.hamcrest.Matchers.hasItem
|
||||
import org.hamcrest.Matchers.not
|
||||
@@ -86,6 +88,8 @@ class PrivacyItemControllerTest : SysuiTestCase() {
|
||||
private lateinit var userTracker: UserTracker
|
||||
@Mock
|
||||
private lateinit var dumpManager: DumpManager
|
||||
@Mock
|
||||
private lateinit var logger: PrivacyLogger
|
||||
@Captor
|
||||
private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>>
|
||||
@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<String>()
|
||||
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)
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user