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

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

Merge "Change privacy indicator holding policy" into sc-dev

parents 2d9cd59e 8e68c334
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -24,15 +24,15 @@ public class AppOpItem {
    private int mCode;
    private int mUid;
    private String mPackageName;
    private long mTimeStarted;
    private long mTimeStartedElapsed;
    private StringBuilder mState;
    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.mUid = uid;
        this.mPackageName = packageName;
        this.mTimeStarted = timeStarted;
        this.mTimeStartedElapsed = timeStartedElapsed;
        mState = new StringBuilder()
                .append("AppOpItem(")
                .append("Op code=").append(code).append(", ")
@@ -53,8 +53,8 @@ public class AppOpItem {
        return mPackageName;
    }

    public long getTimeStarted() {
        return mTimeStarted;
    public long getTimeStartedElapsed() {
        return mTimeStartedElapsed;
    }

    public void setDisabled(boolean misDisabled) {
+7 −5
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import com.android.systemui.util.Assert;
import com.android.systemui.util.time.SystemClock;

import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -83,8 +84,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
    private final AppOpsManager mAppOps;
    private final AudioManager mAudioManager;
    private final LocationManager mLocationManager;
    private final PackageManager mPackageManager;
    private final IndividualSensorPrivacyController mSensorPrivacyController;
    private final SystemClock mClock;

    // mLocationProviderPackages are cached and updated only occasionally
    private static final long LOCATION_PROVIDER_UPDATE_FREQUENCY_MS = 30000;
@@ -126,7 +127,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
            PermissionFlagsCache cache,
            AudioManager audioManager,
            IndividualSensorPrivacyController sensorPrivacyController,
            BroadcastDispatcher dispatcher
            BroadcastDispatcher dispatcher,
            SystemClock clock
    ) {
        mDispatcher = dispatcher;
        mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
@@ -142,8 +144,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
                || mSensorPrivacyController.isSensorBlocked(MICROPHONE);
        mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA);
        mLocationManager = context.getSystemService(LocationManager.class);
        mPackageManager = context.getPackageManager();
        mContext = context;
        mClock = clock;
        dumpManager.registerDumpable(TAG, this);
    }

@@ -252,7 +254,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
        synchronized (mActiveItems) {
            AppOpItem item = getAppOpItemLocked(mActiveItems, code, uid, packageName);
            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) {
                    item.setDisabled(isAnyRecordingPausedLocked(uid));
                } else if (code == AppOpsManager.OP_CAMERA) {
@@ -294,7 +296,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
        synchronized (mNotedItems) {
            item = getAppOpItemLocked(mNotedItems, code, uid, packageName);
            if (item == null) {
                item = new AppOpItem(code, uid, packageName, System.currentTimeMillis());
                item = new AppOpItem(code, uid, packageName, mClock.elapsedRealtime());
                mNotedItems.add(item);
                if (DEBUG) Log.w(TAG, "Added item: " + item.toString());
                createdNew = true;
+8 −2
Original line number Diff line number Diff line
@@ -50,8 +50,14 @@ enum class PrivacyType(
    fun getIcon(context: Context) = context.resources.getDrawable(iconId, context.theme)
}

data class PrivacyItem(val privacyType: PrivacyType, val application: PrivacyApplication) {
    fun toLog(): String = "(${privacyType.logName}, ${application.packageName}(${application.uid}))"
private const val UNKNOWN_TIMESTAMP = -1L
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)
+34 −55
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ 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
import com.android.systemui.util.time.SystemClock
import java.io.FileDescriptor
import java.io.PrintWriter
import java.lang.ref.WeakReference
@@ -50,6 +51,7 @@ class PrivacyItemController @Inject constructor(
    private val deviceConfigProxy: DeviceConfigProxy,
    private val userTracker: UserTracker,
    private val logger: PrivacyLogger,
    private val systemClock: SystemClock,
    dumpManager: DumpManager
) : Dumpable {

@@ -70,10 +72,9 @@ class PrivacyItemController @Inject constructor(
        const val TAG = "PrivacyItemController"
        private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_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_LOCATION = false
        const val TIME_TO_HOLD_INDICATORS = 5000L
        @VisibleForTesting const val TIME_TO_HOLD_INDICATORS = 5000L
    }

    @VisibleForTesting
@@ -95,8 +96,8 @@ class PrivacyItemController @Inject constructor(
    private var listening = false
    private val callbacks = mutableListOf<WeakReference<Callback>>()
    private val internalUiExecutor = MyExecutor(uiExecutor)
    private var holdingIndicators = false
    private var holdIndicatorsCancelled: Runnable? = null

    private var holdingRunnableCanceler: Runnable? = null

    private val notifyChanges = Runnable {
        val list = privacyList
@@ -108,11 +109,6 @@ class PrivacyItemController @Inject constructor(
        uiExecutor.execute(notifyChanges)
    }

    private val stopHoldingAndNotifyChanges = Runnable {
        updatePrivacyList(true)
        uiExecutor.execute(notifyChanges)
    }

    var micCameraAvailable = isMicCameraEnabled()
        private set
    var locationAvailable = isLocationEnabled()
@@ -189,14 +185,6 @@ class PrivacyItemController @Inject constructor(
        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) {
        bgExecutor.execute {
            if (updateUsers) {
@@ -261,14 +249,12 @@ class PrivacyItemController @Inject constructor(
        removeCallback(WeakReference(callback))
    }

    private fun updatePrivacyList(stopHolding: Boolean = false) {
    private fun updatePrivacyList() {
        holdingRunnableCanceler?.run()?.also {
            holdingRunnableCanceler = null
        }
        if (!listening) {
            privacyList = emptyList()
            if (holdingIndicators) {
                holdIndicatorsCancelled?.run()
                logger.cancelIndicatorsHold()
                holdingIndicators = false
            }
            return
        }
        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_CAMERA
        }.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
     * [TIME_TO_HOLD_INDICATORS] has passed since it received an empty list from [AppOpsController].
     * Figure out which items have not been around for long enough and put them back in the list.
     *
     * Also schedule when we should check again to remove expired items. Because we always retrieve
     * the current list, we have the latest info.
     *
     * If holding the last list (in the [TIME_TO_HOLD_INDICATORS] period) and a new non-empty list
     * is retrieved from [AppOpsController], it will stop holding and notify about the new list.
     * @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) {
        if (list.isNotEmpty()) {
            // 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.
            if (holdingIndicators) {
                holdIndicatorsCancelled?.run()
                logger.cancelIndicatorsHold()
                holdingIndicators = false
            }
            logger.logUpdatedPrivacyItemsList(
                    list.joinToString(separator = ", ", transform = PrivacyItem::toLog))
            privacyList = list
        } else if (holdingIndicators && stopHolding) {
            // We are holding indicators, received an empty list and were told to stop holding.
            logger.finishIndicatorsHold()
            logger.logUpdatedPrivacyItemsList("")
            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.
    private fun processNewList(list: List<PrivacyItem>): List<PrivacyItem> {
        logger.logRetrievedPrivacyItemsList(list)

        // Anything earlier than this timestamp can be removed
        val removeBeforeTime = systemClock.elapsedRealtime() - TIME_TO_HOLD_INDICATORS
        val mustKeep = privacyList.filter { it.timeStampElapsed > removeBeforeTime && it !in list }

        // There are items we must keep because they haven't been around for enough time.
        if (mustKeep.isNotEmpty()) {
            logger.logPrivacyItemsToHold(mustKeep)
            val earliestTime = mustKeep.minByOrNull { it.timeStampElapsed }!!.timeStampElapsed

            // Update the list again when the earliest item should be removed.
            val delay = earliestTime - removeBeforeTime
            logger.logPrivacyItemsUpdateScheduled(delay)
            holdingRunnableCanceler = bgExecutor.executeDelayed(updateListAndNotifyChanges, delay)
        }
        return list + mustKeep
    }

    private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
@@ -329,7 +308,7 @@ class PrivacyItemController @Inject constructor(
            return null
        }
        val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
        return PrivacyItem(type, app)
        return PrivacyItem(type, app, appOpItem.timeStartedElapsed)
    }

    interface Callback {
+21 −16
Original line number Diff line number Diff line
@@ -22,10 +22,13 @@ import com.android.systemui.log.LogLevel
import com.android.systemui.log.LogMessage
import com.android.systemui.log.dagger.PrivacyLog
import com.android.systemui.privacy.PrivacyDialog
import com.android.systemui.privacy.PrivacyItem
import java.text.SimpleDateFormat
import java.util.Locale
import javax.inject.Inject

private const val TAG = "PrivacyLog"

private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
class PrivacyLogger @Inject constructor(
    @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, {
            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, {
            int1 = time.toInt() / 1000
            str1 = listToString(list)
        }, {
            "Starting privacy indicators hold for $int1 seconds"
        })
    }

    fun cancelIndicatorsHold() {
        log(LogLevel.VERBOSE, {}, {
            "Cancel privacy indicators hold"
            "Holding items: $str1"
        })
    }

    fun finishIndicatorsHold() {
        log(LogLevel.DEBUG, {}, {
            "Finish privacy indicators hold"
    fun logPrivacyItemsUpdateScheduled(delay: Long) {
        log(LogLevel.INFO, {
            val scheduledFor = System.currentTimeMillis() + delay
            val formattedTimestamp = DATE_FORMAT.format(scheduledFor)
            str1 = formattedTimestamp
        }, {
            "Updating items scheduled for $str1"
        })
    }

@@ -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(
        logLevel: LogLevel,
        initializer: LogMessage.() -> Unit,
Loading