Merge "Change privacy indicator holding policy" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
e617b31111
@@ -24,15 +24,15 @@ public class AppOpItem {
|
|||||||
private int mCode;
|
private int mCode;
|
||||||
private int mUid;
|
private int mUid;
|
||||||
private String mPackageName;
|
private String mPackageName;
|
||||||
private long mTimeStarted;
|
private long mTimeStartedElapsed;
|
||||||
private StringBuilder mState;
|
private StringBuilder mState;
|
||||||
private boolean mIsDisabled;
|
private boolean mIsDisabled;
|
||||||
|
|
||||||
public AppOpItem(int code, int uid, String packageName, long timeStarted) {
|
public AppOpItem(int code, int uid, String packageName, long timeStartedElapsed) {
|
||||||
this.mCode = code;
|
this.mCode = code;
|
||||||
this.mUid = uid;
|
this.mUid = uid;
|
||||||
this.mPackageName = packageName;
|
this.mPackageName = packageName;
|
||||||
this.mTimeStarted = timeStarted;
|
this.mTimeStartedElapsed = timeStartedElapsed;
|
||||||
mState = new StringBuilder()
|
mState = new StringBuilder()
|
||||||
.append("AppOpItem(")
|
.append("AppOpItem(")
|
||||||
.append("Op code=").append(code).append(", ")
|
.append("Op code=").append(code).append(", ")
|
||||||
@@ -53,8 +53,8 @@ public class AppOpItem {
|
|||||||
return mPackageName;
|
return mPackageName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTimeStarted() {
|
public long getTimeStartedElapsed() {
|
||||||
return mTimeStarted;
|
return mTimeStartedElapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDisabled(boolean misDisabled) {
|
public void setDisabled(boolean misDisabled) {
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import com.android.systemui.dagger.qualifiers.Background;
|
|||||||
import com.android.systemui.dump.DumpManager;
|
import com.android.systemui.dump.DumpManager;
|
||||||
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
|
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
|
||||||
import com.android.systemui.util.Assert;
|
import com.android.systemui.util.Assert;
|
||||||
|
import com.android.systemui.util.time.SystemClock;
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
@@ -83,8 +84,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
|
|||||||
private final AppOpsManager mAppOps;
|
private final AppOpsManager mAppOps;
|
||||||
private final AudioManager mAudioManager;
|
private final AudioManager mAudioManager;
|
||||||
private final LocationManager mLocationManager;
|
private final LocationManager mLocationManager;
|
||||||
private final PackageManager mPackageManager;
|
|
||||||
private final IndividualSensorPrivacyController mSensorPrivacyController;
|
private final IndividualSensorPrivacyController mSensorPrivacyController;
|
||||||
|
private final SystemClock mClock;
|
||||||
|
|
||||||
// mLocationProviderPackages are cached and updated only occasionally
|
// mLocationProviderPackages are cached and updated only occasionally
|
||||||
private static final long LOCATION_PROVIDER_UPDATE_FREQUENCY_MS = 30000;
|
private static final long LOCATION_PROVIDER_UPDATE_FREQUENCY_MS = 30000;
|
||||||
@@ -126,7 +127,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
|
|||||||
PermissionFlagsCache cache,
|
PermissionFlagsCache cache,
|
||||||
AudioManager audioManager,
|
AudioManager audioManager,
|
||||||
IndividualSensorPrivacyController sensorPrivacyController,
|
IndividualSensorPrivacyController sensorPrivacyController,
|
||||||
BroadcastDispatcher dispatcher
|
BroadcastDispatcher dispatcher,
|
||||||
|
SystemClock clock
|
||||||
) {
|
) {
|
||||||
mDispatcher = dispatcher;
|
mDispatcher = dispatcher;
|
||||||
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
||||||
@@ -142,8 +144,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
|
|||||||
|| mSensorPrivacyController.isSensorBlocked(MICROPHONE);
|
|| mSensorPrivacyController.isSensorBlocked(MICROPHONE);
|
||||||
mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA);
|
mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA);
|
||||||
mLocationManager = context.getSystemService(LocationManager.class);
|
mLocationManager = context.getSystemService(LocationManager.class);
|
||||||
mPackageManager = context.getPackageManager();
|
|
||||||
mContext = context;
|
mContext = context;
|
||||||
|
mClock = clock;
|
||||||
dumpManager.registerDumpable(TAG, this);
|
dumpManager.registerDumpable(TAG, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,7 +254,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
|
|||||||
synchronized (mActiveItems) {
|
synchronized (mActiveItems) {
|
||||||
AppOpItem item = getAppOpItemLocked(mActiveItems, code, uid, packageName);
|
AppOpItem item = getAppOpItemLocked(mActiveItems, code, uid, packageName);
|
||||||
if (item == null && active) {
|
if (item == null && active) {
|
||||||
item = new AppOpItem(code, uid, packageName, System.currentTimeMillis());
|
item = new AppOpItem(code, uid, packageName, mClock.elapsedRealtime());
|
||||||
if (code == AppOpsManager.OP_RECORD_AUDIO) {
|
if (code == AppOpsManager.OP_RECORD_AUDIO) {
|
||||||
item.setDisabled(isAnyRecordingPausedLocked(uid));
|
item.setDisabled(isAnyRecordingPausedLocked(uid));
|
||||||
} else if (code == AppOpsManager.OP_CAMERA) {
|
} else if (code == AppOpsManager.OP_CAMERA) {
|
||||||
@@ -294,7 +296,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
|
|||||||
synchronized (mNotedItems) {
|
synchronized (mNotedItems) {
|
||||||
item = getAppOpItemLocked(mNotedItems, code, uid, packageName);
|
item = getAppOpItemLocked(mNotedItems, code, uid, packageName);
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
item = new AppOpItem(code, uid, packageName, System.currentTimeMillis());
|
item = new AppOpItem(code, uid, packageName, mClock.elapsedRealtime());
|
||||||
mNotedItems.add(item);
|
mNotedItems.add(item);
|
||||||
if (DEBUG) Log.w(TAG, "Added item: " + item.toString());
|
if (DEBUG) Log.w(TAG, "Added item: " + item.toString());
|
||||||
createdNew = true;
|
createdNew = true;
|
||||||
|
|||||||
@@ -50,8 +50,14 @@ enum class PrivacyType(
|
|||||||
fun getIcon(context: Context) = context.resources.getDrawable(iconId, context.theme)
|
fun getIcon(context: Context) = context.resources.getDrawable(iconId, context.theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class PrivacyItem(val privacyType: PrivacyType, val application: PrivacyApplication) {
|
private const val UNKNOWN_TIMESTAMP = -1L
|
||||||
fun toLog(): String = "(${privacyType.logName}, ${application.packageName}(${application.uid}))"
|
data class PrivacyItem(
|
||||||
|
val privacyType: PrivacyType,
|
||||||
|
val application: PrivacyApplication,
|
||||||
|
val timeStampElapsed: Long = UNKNOWN_TIMESTAMP
|
||||||
|
) {
|
||||||
|
val log = "(${privacyType.logName}, ${application.packageName}(${application.uid}), " +
|
||||||
|
"$timeStampElapsed)"
|
||||||
}
|
}
|
||||||
|
|
||||||
data class PrivacyApplication(val packageName: String, val uid: Int)
|
data class PrivacyApplication(val packageName: String, val uid: Int)
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import com.android.systemui.privacy.logging.PrivacyLogger
|
|||||||
import com.android.systemui.settings.UserTracker
|
import com.android.systemui.settings.UserTracker
|
||||||
import com.android.systemui.util.DeviceConfigProxy
|
import com.android.systemui.util.DeviceConfigProxy
|
||||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||||
|
import com.android.systemui.util.time.SystemClock
|
||||||
import java.io.FileDescriptor
|
import java.io.FileDescriptor
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
@@ -50,6 +51,7 @@ class PrivacyItemController @Inject constructor(
|
|||||||
private val deviceConfigProxy: DeviceConfigProxy,
|
private val deviceConfigProxy: DeviceConfigProxy,
|
||||||
private val userTracker: UserTracker,
|
private val userTracker: UserTracker,
|
||||||
private val logger: PrivacyLogger,
|
private val logger: PrivacyLogger,
|
||||||
|
private val systemClock: SystemClock,
|
||||||
dumpManager: DumpManager
|
dumpManager: DumpManager
|
||||||
) : Dumpable {
|
) : Dumpable {
|
||||||
|
|
||||||
@@ -70,10 +72,9 @@ class PrivacyItemController @Inject constructor(
|
|||||||
const val TAG = "PrivacyItemController"
|
const val TAG = "PrivacyItemController"
|
||||||
private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
|
private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
|
||||||
private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
|
private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
|
||||||
private const val DEFAULT_ALL_INDICATORS = false
|
|
||||||
private const val DEFAULT_MIC_CAMERA = true
|
private const val DEFAULT_MIC_CAMERA = true
|
||||||
private const val DEFAULT_LOCATION = false
|
private const val DEFAULT_LOCATION = false
|
||||||
const val TIME_TO_HOLD_INDICATORS = 5000L
|
@VisibleForTesting const val TIME_TO_HOLD_INDICATORS = 5000L
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -95,8 +96,8 @@ class PrivacyItemController @Inject constructor(
|
|||||||
private var listening = false
|
private var listening = false
|
||||||
private val callbacks = mutableListOf<WeakReference<Callback>>()
|
private val callbacks = mutableListOf<WeakReference<Callback>>()
|
||||||
private val internalUiExecutor = MyExecutor(uiExecutor)
|
private val internalUiExecutor = MyExecutor(uiExecutor)
|
||||||
private var holdingIndicators = false
|
|
||||||
private var holdIndicatorsCancelled: Runnable? = null
|
private var holdingRunnableCanceler: Runnable? = null
|
||||||
|
|
||||||
private val notifyChanges = Runnable {
|
private val notifyChanges = Runnable {
|
||||||
val list = privacyList
|
val list = privacyList
|
||||||
@@ -108,11 +109,6 @@ class PrivacyItemController @Inject constructor(
|
|||||||
uiExecutor.execute(notifyChanges)
|
uiExecutor.execute(notifyChanges)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val stopHoldingAndNotifyChanges = Runnable {
|
|
||||||
updatePrivacyList(true)
|
|
||||||
uiExecutor.execute(notifyChanges)
|
|
||||||
}
|
|
||||||
|
|
||||||
var micCameraAvailable = isMicCameraEnabled()
|
var micCameraAvailable = isMicCameraEnabled()
|
||||||
private set
|
private set
|
||||||
var locationAvailable = isLocationEnabled()
|
var locationAvailable = isLocationEnabled()
|
||||||
@@ -189,14 +185,6 @@ class PrivacyItemController @Inject constructor(
|
|||||||
userTracker.addCallback(userTrackerCallback, bgExecutor)
|
userTracker.addCallback(userTrackerCallback, bgExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setHoldTimer() {
|
|
||||||
holdIndicatorsCancelled?.run()
|
|
||||||
holdingIndicators = true
|
|
||||||
holdIndicatorsCancelled = bgExecutor.executeDelayed({
|
|
||||||
stopHoldingAndNotifyChanges.run()
|
|
||||||
}, TIME_TO_HOLD_INDICATORS)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun update(updateUsers: Boolean) {
|
private fun update(updateUsers: Boolean) {
|
||||||
bgExecutor.execute {
|
bgExecutor.execute {
|
||||||
if (updateUsers) {
|
if (updateUsers) {
|
||||||
@@ -261,14 +249,12 @@ class PrivacyItemController @Inject constructor(
|
|||||||
removeCallback(WeakReference(callback))
|
removeCallback(WeakReference(callback))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updatePrivacyList(stopHolding: Boolean = false) {
|
private fun updatePrivacyList() {
|
||||||
|
holdingRunnableCanceler?.run()?.also {
|
||||||
|
holdingRunnableCanceler = null
|
||||||
|
}
|
||||||
if (!listening) {
|
if (!listening) {
|
||||||
privacyList = emptyList()
|
privacyList = emptyList()
|
||||||
if (holdingIndicators) {
|
|
||||||
holdIndicatorsCancelled?.run()
|
|
||||||
logger.cancelIndicatorsHold()
|
|
||||||
holdingIndicators = false
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val list = appOpsController.getActiveAppOpsForUser(UserHandle.USER_ALL).filter {
|
val list = appOpsController.getActiveAppOpsForUser(UserHandle.USER_ALL).filter {
|
||||||
@@ -276,43 +262,36 @@ class PrivacyItemController @Inject constructor(
|
|||||||
it.code == AppOpsManager.OP_PHONE_CALL_MICROPHONE ||
|
it.code == AppOpsManager.OP_PHONE_CALL_MICROPHONE ||
|
||||||
it.code == AppOpsManager.OP_PHONE_CALL_CAMERA
|
it.code == AppOpsManager.OP_PHONE_CALL_CAMERA
|
||||||
}.mapNotNull { toPrivacyItem(it) }.distinct()
|
}.mapNotNull { toPrivacyItem(it) }.distinct()
|
||||||
processNewList(list, stopHolding)
|
privacyList = processNewList(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The controller will only go from indicators to no indicators (and notify its listeners), if
|
* Figure out which items have not been around for long enough and put them back in the list.
|
||||||
* [TIME_TO_HOLD_INDICATORS] has passed since it received an empty list from [AppOpsController].
|
|
||||||
*
|
*
|
||||||
* If holding the last list (in the [TIME_TO_HOLD_INDICATORS] period) and a new non-empty list
|
* Also schedule when we should check again to remove expired items. Because we always retrieve
|
||||||
* is retrieved from [AppOpsController], it will stop holding and notify about the new list.
|
* the current list, we have the latest info.
|
||||||
|
*
|
||||||
|
* @param list map of list retrieved from [AppOpsController].
|
||||||
|
* @return a list that may have added items that should be kept for some time.
|
||||||
*/
|
*/
|
||||||
private fun processNewList(list: List<PrivacyItem>, stopHolding: Boolean) {
|
private fun processNewList(list: List<PrivacyItem>): List<PrivacyItem> {
|
||||||
if (list.isNotEmpty()) {
|
logger.logRetrievedPrivacyItemsList(list)
|
||||||
// The new elements is not empty, so regardless of whether we are holding or not, we
|
|
||||||
// clear the holding flag and cancel the delayed runnable.
|
// Anything earlier than this timestamp can be removed
|
||||||
if (holdingIndicators) {
|
val removeBeforeTime = systemClock.elapsedRealtime() - TIME_TO_HOLD_INDICATORS
|
||||||
holdIndicatorsCancelled?.run()
|
val mustKeep = privacyList.filter { it.timeStampElapsed > removeBeforeTime && it !in list }
|
||||||
logger.cancelIndicatorsHold()
|
|
||||||
holdingIndicators = false
|
// There are items we must keep because they haven't been around for enough time.
|
||||||
}
|
if (mustKeep.isNotEmpty()) {
|
||||||
logger.logUpdatedPrivacyItemsList(
|
logger.logPrivacyItemsToHold(mustKeep)
|
||||||
list.joinToString(separator = ", ", transform = PrivacyItem::toLog))
|
val earliestTime = mustKeep.minByOrNull { it.timeStampElapsed }!!.timeStampElapsed
|
||||||
privacyList = list
|
|
||||||
} else if (holdingIndicators && stopHolding) {
|
// Update the list again when the earliest item should be removed.
|
||||||
// We are holding indicators, received an empty list and were told to stop holding.
|
val delay = earliestTime - removeBeforeTime
|
||||||
logger.finishIndicatorsHold()
|
logger.logPrivacyItemsUpdateScheduled(delay)
|
||||||
logger.logUpdatedPrivacyItemsList("")
|
holdingRunnableCanceler = bgExecutor.executeDelayed(updateListAndNotifyChanges, delay)
|
||||||
holdingIndicators = false
|
|
||||||
privacyList = list
|
|
||||||
} else if (holdingIndicators && !stopHolding) {
|
|
||||||
// Empty list while we are holding. Ignore
|
|
||||||
} else if (!holdingIndicators && privacyList.isNotEmpty()) {
|
|
||||||
// We are not holding, we were showing some indicators but now we should show nothing.
|
|
||||||
// Start holding.
|
|
||||||
logger.startIndicatorsHold(TIME_TO_HOLD_INDICATORS)
|
|
||||||
setHoldTimer()
|
|
||||||
}
|
}
|
||||||
// Else. We are not holding, we were not showing anything and the new list is empty. Ignore.
|
return list + mustKeep
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
|
private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
|
||||||
@@ -329,7 +308,7 @@ class PrivacyItemController @Inject constructor(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
|
val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
|
||||||
return PrivacyItem(type, app)
|
return PrivacyItem(type, app, appOpItem.timeStartedElapsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Callback {
|
interface Callback {
|
||||||
|
|||||||
@@ -22,10 +22,13 @@ import com.android.systemui.log.LogLevel
|
|||||||
import com.android.systemui.log.LogMessage
|
import com.android.systemui.log.LogMessage
|
||||||
import com.android.systemui.log.dagger.PrivacyLog
|
import com.android.systemui.log.dagger.PrivacyLog
|
||||||
import com.android.systemui.privacy.PrivacyDialog
|
import com.android.systemui.privacy.PrivacyDialog
|
||||||
|
import com.android.systemui.privacy.PrivacyItem
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
private const val TAG = "PrivacyLog"
|
private const val TAG = "PrivacyLog"
|
||||||
|
private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
|
||||||
class PrivacyLogger @Inject constructor(
|
class PrivacyLogger @Inject constructor(
|
||||||
@PrivacyLog private val buffer: LogBuffer
|
@PrivacyLog private val buffer: LogBuffer
|
||||||
) {
|
) {
|
||||||
@@ -41,31 +44,29 @@ class PrivacyLogger @Inject constructor(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun logUpdatedPrivacyItemsList(listAsString: String) {
|
fun logRetrievedPrivacyItemsList(list: List<PrivacyItem>) {
|
||||||
log(LogLevel.INFO, {
|
log(LogLevel.INFO, {
|
||||||
str1 = listAsString
|
str1 = listToString(list)
|
||||||
}, {
|
}, {
|
||||||
"Updated list: $str1"
|
"Retrieved list to process: $str1"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startIndicatorsHold(time: Long) {
|
fun logPrivacyItemsToHold(list: List<PrivacyItem>) {
|
||||||
log(LogLevel.DEBUG, {
|
log(LogLevel.DEBUG, {
|
||||||
int1 = time.toInt() / 1000
|
str1 = listToString(list)
|
||||||
}, {
|
}, {
|
||||||
"Starting privacy indicators hold for $int1 seconds"
|
"Holding items: $str1"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cancelIndicatorsHold() {
|
fun logPrivacyItemsUpdateScheduled(delay: Long) {
|
||||||
log(LogLevel.VERBOSE, {}, {
|
log(LogLevel.INFO, {
|
||||||
"Cancel privacy indicators hold"
|
val scheduledFor = System.currentTimeMillis() + delay
|
||||||
})
|
val formattedTimestamp = DATE_FORMAT.format(scheduledFor)
|
||||||
}
|
str1 = formattedTimestamp
|
||||||
|
}, {
|
||||||
fun finishIndicatorsHold() {
|
"Updating items scheduled for $str1"
|
||||||
log(LogLevel.DEBUG, {}, {
|
|
||||||
"Finish privacy indicators hold"
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,6 +131,10 @@ class PrivacyLogger @Inject constructor(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun listToString(list: List<PrivacyItem>): String {
|
||||||
|
return list.joinToString(separator = ", ", transform = PrivacyItem::log)
|
||||||
|
}
|
||||||
|
|
||||||
private inline fun log(
|
private inline fun log(
|
||||||
logLevel: LogLevel,
|
logLevel: LogLevel,
|
||||||
initializer: LogMessage.() -> Unit,
|
initializer: LogMessage.() -> Unit,
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import com.android.systemui.SysuiTestCase;
|
|||||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||||
import com.android.systemui.dump.DumpManager;
|
import com.android.systemui.dump.DumpManager;
|
||||||
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
|
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
|
||||||
|
import com.android.systemui.util.time.FakeSystemClock;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -137,7 +138,8 @@ public class AppOpsControllerTest extends SysuiTestCase {
|
|||||||
mFlagsCache,
|
mFlagsCache,
|
||||||
mAudioManager,
|
mAudioManager,
|
||||||
mSensorPrivacyController,
|
mSensorPrivacyController,
|
||||||
mDispatcher
|
mDispatcher,
|
||||||
|
new FakeSystemClock()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
|
|||||||
private lateinit var executor: FakeExecutor
|
private lateinit var executor: FakeExecutor
|
||||||
private lateinit var deviceConfigProxy: DeviceConfigProxy
|
private lateinit var deviceConfigProxy: DeviceConfigProxy
|
||||||
|
|
||||||
fun PrivacyItemController(): PrivacyItemController {
|
fun createPrivacyItemController(): PrivacyItemController {
|
||||||
return PrivacyItemController(
|
return PrivacyItemController(
|
||||||
appOpsController,
|
appOpsController,
|
||||||
executor,
|
executor,
|
||||||
@@ -77,6 +77,7 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
|
|||||||
deviceConfigProxy,
|
deviceConfigProxy,
|
||||||
userTracker,
|
userTracker,
|
||||||
logger,
|
logger,
|
||||||
|
FakeSystemClock(),
|
||||||
dumpManager)
|
dumpManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +87,7 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
|
|||||||
executor = FakeExecutor(FakeSystemClock())
|
executor = FakeExecutor(FakeSystemClock())
|
||||||
deviceConfigProxy = DeviceConfigProxyFake()
|
deviceConfigProxy = DeviceConfigProxyFake()
|
||||||
|
|
||||||
privacyItemController = PrivacyItemController()
|
privacyItemController = createPrivacyItemController()
|
||||||
privacyItemController.addCallback(callback)
|
privacyItemController.addCallback(callback)
|
||||||
|
|
||||||
executor.runAllReady()
|
executor.runAllReady()
|
||||||
|
|||||||
@@ -99,7 +99,10 @@ class PrivacyItemControllerTest : SysuiTestCase() {
|
|||||||
private lateinit var fakeClock: FakeSystemClock
|
private lateinit var fakeClock: FakeSystemClock
|
||||||
private lateinit var deviceConfigProxy: DeviceConfigProxy
|
private lateinit var deviceConfigProxy: DeviceConfigProxy
|
||||||
|
|
||||||
fun PrivacyItemController(): PrivacyItemController {
|
private val elapsedTime: Long
|
||||||
|
get() = fakeClock.elapsedRealtime()
|
||||||
|
|
||||||
|
fun createPrivacyItemController(): PrivacyItemController {
|
||||||
return PrivacyItemController(
|
return PrivacyItemController(
|
||||||
appOpsController,
|
appOpsController,
|
||||||
executor,
|
executor,
|
||||||
@@ -107,6 +110,7 @@ class PrivacyItemControllerTest : SysuiTestCase() {
|
|||||||
deviceConfigProxy,
|
deviceConfigProxy,
|
||||||
userTracker,
|
userTracker,
|
||||||
logger,
|
logger,
|
||||||
|
fakeClock,
|
||||||
dumpManager)
|
dumpManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +127,7 @@ class PrivacyItemControllerTest : SysuiTestCase() {
|
|||||||
|
|
||||||
`when`(userTracker.userProfiles).thenReturn(listOf(UserInfo(CURRENT_USER_ID, "", 0)))
|
`when`(userTracker.userProfiles).thenReturn(listOf(UserInfo(CURRENT_USER_ID, "", 0)))
|
||||||
|
|
||||||
privacyItemController = PrivacyItemController()
|
privacyItemController = createPrivacyItemController()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -151,7 +155,7 @@ class PrivacyItemControllerTest : SysuiTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun testDistinctItems() {
|
fun testDistinctItems() {
|
||||||
doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
|
doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
|
||||||
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1)))
|
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0)))
|
||||||
.`when`(appOpsController).getActiveAppOpsForUser(anyInt())
|
.`when`(appOpsController).getActiveAppOpsForUser(anyInt())
|
||||||
|
|
||||||
privacyItemController.addCallback(callback)
|
privacyItemController.addCallback(callback)
|
||||||
@@ -160,6 +164,18 @@ class PrivacyItemControllerTest : SysuiTestCase() {
|
|||||||
assertEquals(1, argCaptor.value.size)
|
assertEquals(1, argCaptor.value.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSimilarItemsDifferentTimeStamp() {
|
||||||
|
doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
|
||||||
|
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1)))
|
||||||
|
.`when`(appOpsController).getActiveAppOpsForUser(anyInt())
|
||||||
|
|
||||||
|
privacyItemController.addCallback(callback)
|
||||||
|
executor.runAllReady()
|
||||||
|
verify(callback).onPrivacyItemsChanged(capture(argCaptor))
|
||||||
|
assertEquals(2, argCaptor.value.size)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testRegisterCallback() {
|
fun testRegisterCallback() {
|
||||||
privacyItemController.addCallback(callback)
|
privacyItemController.addCallback(callback)
|
||||||
@@ -251,7 +267,7 @@ class PrivacyItemControllerTest : SysuiTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun testListShouldBeCopy() {
|
fun testListShouldBeCopy() {
|
||||||
val list = listOf(PrivacyItem(PrivacyType.TYPE_CAMERA,
|
val list = listOf(PrivacyItem(PrivacyType.TYPE_CAMERA,
|
||||||
PrivacyApplication("", TEST_UID)))
|
PrivacyApplication("", TEST_UID), 0))
|
||||||
privacyItemController.privacyList = list
|
privacyItemController.privacyList = list
|
||||||
val privacyList = privacyItemController.privacyList
|
val privacyList = privacyItemController.privacyList
|
||||||
assertEquals(list, privacyList)
|
assertEquals(list, privacyList)
|
||||||
@@ -334,14 +350,15 @@ class PrivacyItemControllerTest : SysuiTestCase() {
|
|||||||
|
|
||||||
val expected = PrivacyItem(
|
val expected = PrivacyItem(
|
||||||
PrivacyType.TYPE_LOCATION,
|
PrivacyType.TYPE_LOCATION,
|
||||||
PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID)
|
PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID),
|
||||||
|
0
|
||||||
)
|
)
|
||||||
|
|
||||||
val captor = argumentCaptor<String>()
|
val captor = argumentCaptor<List<PrivacyItem>>()
|
||||||
verify(logger, atLeastOnce()).logUpdatedPrivacyItemsList(capture(captor))
|
verify(logger, atLeastOnce()).logRetrievedPrivacyItemsList(capture(captor))
|
||||||
// Let's look at the last log
|
// Let's look at the last log
|
||||||
val values = captor.allValues
|
val values = captor.allValues
|
||||||
assertTrue(values[values.size - 1].contains(expected.toLog()))
|
assertTrue(values[values.size - 1].contains(expected))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -424,7 +441,7 @@ class PrivacyItemControllerTest : SysuiTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun testPassageOfTimeDoesNotRemoveIndicators() {
|
fun testPassageOfTimeDoesNotRemoveIndicators() {
|
||||||
doReturn(listOf(
|
doReturn(listOf(
|
||||||
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0)
|
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, elapsedTime)
|
||||||
)).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
|
)).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
|
||||||
|
|
||||||
privacyItemController.addCallback(callback)
|
privacyItemController.addCallback(callback)
|
||||||
@@ -437,86 +454,75 @@ class PrivacyItemControllerTest : SysuiTestCase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testHoldingAfterEmptyBeforeTimeExpires() {
|
fun testNotHeldAfterTimeIsOff() {
|
||||||
|
// Start with some element at time 0
|
||||||
doReturn(listOf(
|
doReturn(listOf(
|
||||||
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0)
|
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, elapsedTime)
|
||||||
)).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
|
)).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
|
||||||
|
|
||||||
privacyItemController.addCallback(callback)
|
privacyItemController.addCallback(callback)
|
||||||
executor.runAllReady()
|
executor.runAllReady()
|
||||||
|
|
||||||
verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
|
// Then remove it at time HOLD + 1
|
||||||
|
doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
|
||||||
|
fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS + 1)
|
||||||
|
|
||||||
`when`(appOpsController.getActiveAppOpsForUser(anyInt())).thenReturn(emptyList())
|
verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
|
||||||
argCaptorCallback.value.onActiveStateChanged(
|
argCaptorCallback.value.onActiveStateChanged(
|
||||||
AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, false)
|
AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, false)
|
||||||
executor.runAllReady()
|
executor.runAllReady()
|
||||||
|
|
||||||
fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS / 5)
|
// See it's not there
|
||||||
executor.runAllReady()
|
|
||||||
|
|
||||||
verify(callback, never()).onPrivacyItemsChanged(emptyList())
|
|
||||||
assertTrue(privacyItemController.privacyList.isNotEmpty())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testAfterHoldingIndicatorsAreEmpty() {
|
|
||||||
doReturn(listOf(
|
|
||||||
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0)
|
|
||||||
)).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
|
|
||||||
|
|
||||||
privacyItemController.addCallback(callback)
|
|
||||||
executor.runAllReady()
|
|
||||||
|
|
||||||
verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
|
|
||||||
|
|
||||||
`when`(appOpsController.getActiveAppOpsForUser(anyInt())).thenReturn(emptyList())
|
|
||||||
argCaptorCallback.value.onActiveStateChanged(
|
|
||||||
AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, false)
|
|
||||||
executor.runAllReady()
|
|
||||||
|
|
||||||
executor.advanceClockToLast()
|
|
||||||
executor.runAllReady()
|
|
||||||
|
|
||||||
verify(callback).onPrivacyItemsChanged(emptyList())
|
verify(callback).onPrivacyItemsChanged(emptyList())
|
||||||
assertTrue(privacyItemController.privacyList.isEmpty())
|
assertTrue(privacyItemController.privacyList.isEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testHoldingStopsIfNewIndicatorsAppear() {
|
fun testElementNotRemovedBeforeHoldTime() {
|
||||||
|
// Start with some element at time 0
|
||||||
doReturn(listOf(
|
doReturn(listOf(
|
||||||
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0)
|
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, elapsedTime)
|
||||||
)).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
|
)).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
|
||||||
|
|
||||||
privacyItemController.addCallback(callback)
|
privacyItemController.addCallback(callback)
|
||||||
executor.runAllReady()
|
executor.runAllReady()
|
||||||
|
|
||||||
verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
|
// Then remove it at time HOLD - 1
|
||||||
|
doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
|
||||||
|
fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS - 1)
|
||||||
|
|
||||||
`when`(appOpsController.getActiveAppOpsForUser(anyInt())).thenReturn(emptyList())
|
verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
|
||||||
argCaptorCallback.value.onActiveStateChanged(
|
argCaptorCallback.value.onActiveStateChanged(
|
||||||
AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, false)
|
AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, false)
|
||||||
executor.runAllReady()
|
executor.runAllReady()
|
||||||
|
|
||||||
fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS / 2)
|
// See it's still there
|
||||||
executor.runAllReady()
|
|
||||||
|
|
||||||
doReturn(listOf(
|
|
||||||
AppOpItem(AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, 0)
|
|
||||||
)).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
|
|
||||||
argCaptorCallback.value.onActiveStateChanged(
|
|
||||||
AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true)
|
|
||||||
executor.runAllReady()
|
|
||||||
|
|
||||||
executor.advanceClockToLast()
|
|
||||||
executor.runAllReady()
|
|
||||||
|
|
||||||
verify(callback, never()).onPrivacyItemsChanged(emptyList())
|
verify(callback, never()).onPrivacyItemsChanged(emptyList())
|
||||||
verify(callback, atLeastOnce()).onPrivacyItemsChanged(capture(argCaptor))
|
assertTrue(privacyItemController.privacyList.isNotEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
val lastList = argCaptor.allValues.last()
|
@Test
|
||||||
assertEquals(1, lastList.size)
|
fun testElementAutoRemovedAfterHoldTime() {
|
||||||
assertEquals(PrivacyType.TYPE_MICROPHONE, lastList.single().privacyType)
|
// Start with some element at time 0
|
||||||
|
doReturn(listOf(
|
||||||
|
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, elapsedTime)
|
||||||
|
)).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
|
||||||
|
privacyItemController.addCallback(callback)
|
||||||
|
executor.runAllReady()
|
||||||
|
|
||||||
|
// Then remove it at time HOLD - 1
|
||||||
|
doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
|
||||||
|
fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS - 1)
|
||||||
|
|
||||||
|
verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
|
||||||
|
argCaptorCallback.value.onActiveStateChanged(
|
||||||
|
AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, false)
|
||||||
|
executor.runAllReady()
|
||||||
|
|
||||||
|
fakeClock.advanceTime(2L)
|
||||||
|
executor.runAllReady()
|
||||||
|
|
||||||
|
// See it was auto-removed
|
||||||
|
verify(callback).onPrivacyItemsChanged(emptyList())
|
||||||
|
assertTrue(privacyItemController.privacyList.isEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
|
private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
|
||||||
|
|||||||
Reference in New Issue
Block a user