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

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

Merge "Separate mic + camera from location" into rvc-qpr-dev

parents 7a355ff7 e534fb77
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -123,10 +123,15 @@ public final class SystemUiDeviceConfigFlags {
    // Flag related to Privacy Indicators

    /**
     * Whether the Permissions Hub is showing.
     * Whether to show the complete ongoing app ops chip.
     */
    public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_2_enabled";

    /**
     * Whether to show app ops chip for just microphone + camera.
     */
    public static final String PROPERTY_MIC_CAMERA_ENABLED = "camera_mic_icons_enabled";

    // Flags related to Assistant

    /**
+5 −3
Original line number Diff line number Diff line
@@ -279,9 +279,11 @@ public class AppOpsControllerImpl implements AppOpsController,
     * @return {@code true} iff the app-op for should be shown to the user
     */
    private boolean isUserVisible(int appOpCode, int uid, String packageName) {
        // currently OP_SYSTEM_ALERT_WINDOW does not correspond to a platform permission
        // which may be user senstive, so for now always show it to the user.
        if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) {
        // currently OP_SYSTEM_ALERT_WINDOW and OP_MONITOR_HIGH_POWER_LOCATION
        // does not correspond to a platform permission
        // which may be user sensitive, so for now always show it to the user.
        if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW
                || appOpCode == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) {
            return true;
        }

+56 −21
Original line number Diff line number Diff line
@@ -45,27 +45,32 @@ import javax.inject.Singleton

@Singleton
class PrivacyItemController @Inject constructor(
    context: Context,
    private val appOpsController: AppOpsController,
    @Main uiExecutor: DelayableExecutor,
    @Background private val bgExecutor: Executor,
    private val broadcastDispatcher: BroadcastDispatcher,
    private val deviceConfigProxy: DeviceConfigProxy,
    private val userManager: UserManager,
    dumpManager: DumpManager
) : Dumpable {

    @VisibleForTesting
    internal companion object {
        val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
                AppOpsManager.OP_RECORD_AUDIO,
        val OPS_MIC_CAMERA = intArrayOf(AppOpsManager.OP_CAMERA,
                AppOpsManager.OP_RECORD_AUDIO)
        val OPS_LOCATION = intArrayOf(
                AppOpsManager.OP_COARSE_LOCATION,
                AppOpsManager.OP_FINE_LOCATION)
        val OPS = OPS_MIC_CAMERA + OPS_LOCATION
        val intentFilter = IntentFilter().apply {
            addAction(Intent.ACTION_USER_SWITCHED)
            addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
            addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
        }
        const val TAG = "PrivacyItemController"
        private const val ALL_INDICATORS =
                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
        private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
    }

    @VisibleForTesting
@@ -73,12 +78,16 @@ class PrivacyItemController @Inject constructor(
        @Synchronized get() = field.toList() // Returns a shallow copy of the list
        @Synchronized set

    fun isPermissionsHubEnabled(): Boolean {
    fun isAllIndicatorsEnabled(): Boolean {
        return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
                ALL_INDICATORS, false)
    }

    private fun isMicCameraEnabled(): Boolean {
        return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
                MIC_CAMERA, false)
    }

    private val userManager = context.getSystemService(UserManager::class.java)
    private var currentUserIds = emptyList<Int>()
    private var listening = false
    private val callbacks = mutableListOf<WeakReference<Callback>>()
@@ -86,7 +95,7 @@ class PrivacyItemController @Inject constructor(

    private val notifyChanges = Runnable {
        val list = privacyList
        callbacks.forEach { it.get()?.privacyChanged(list) }
        callbacks.forEach { it.get()?.onPrivacyItemsChanged(list) }
    }

    private val updateListAndNotifyChanges = Runnable {
@@ -94,16 +103,28 @@ class PrivacyItemController @Inject constructor(
        uiExecutor.execute(notifyChanges)
    }

    private var indicatorsAvailable = isPermissionsHubEnabled()
    @VisibleForTesting
    internal val devicePropertiesChangedListener =
    var allIndicatorsAvailable = isAllIndicatorsEnabled()
        private set
    var micCameraAvailable = isMicCameraEnabled()
        private set

    private val devicePropertiesChangedListener =
            object : DeviceConfig.OnPropertiesChangedListener {
        override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
                if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) &&
                    properties.getKeyset().contains(
                    SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED)) {
                indicatorsAvailable = properties.getBoolean(
                        SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
                        (properties.keyset.contains(ALL_INDICATORS) ||
                                properties.keyset.contains(MIC_CAMERA))) {

                    // Running on the ui executor so can iterate on callbacks
                    if (properties.keyset.contains(ALL_INDICATORS)) {
                        allIndicatorsAvailable = properties.getBoolean(ALL_INDICATORS, false)
                        callbacks.forEach { it.get()?.onFlagAllChanged(allIndicatorsAvailable) }
                    }

                    if (properties.keyset.contains(MIC_CAMERA)) {
                        micCameraAvailable = properties.getBoolean(MIC_CAMERA, false)
                        callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) }
                    }
                    internalUiExecutor.updateListeningState()
                }
            }
@@ -116,6 +137,10 @@ class PrivacyItemController @Inject constructor(
            packageName: String,
            active: Boolean
        ) {
            // Check if we care about this code right now
            if (!allIndicatorsAvailable && code in OPS_LOCATION) {
                return
            }
            val userId = UserHandle.getUserId(uid)
            if (userId in currentUserIds) {
                update(false)
@@ -159,13 +184,16 @@ class PrivacyItemController @Inject constructor(
    }

    /**
     * Updates listening status based on whether there are callbacks and the indicators are enabled
     * Updates listening status based on whether there are callbacks and the indicators are enabled.
     *
     * Always listen to all OPS so we don't have to figure out what we should be listening to. We
     * still have to filter anyway. Updates are filtered in the callback.
     *
     * This is only called from private (add/remove)Callback and from the config listener, all in
     * main thread.
     */
    private fun setListeningState() {
        val listen = !callbacks.isEmpty() and indicatorsAvailable
        val listen = !callbacks.isEmpty() and (allIndicatorsAvailable || micCameraAvailable)
        if (listening == listen) return
        listening = listen
        if (listening) {
@@ -226,13 +254,20 @@ class PrivacyItemController @Inject constructor(
            AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
            else -> return null
        }
        if (type == PrivacyType.TYPE_LOCATION && !allIndicatorsAvailable) return null
        val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
        return PrivacyItem(type, app)
    }

    // Used by containing class to get notified of changes
    interface Callback {
        fun privacyChanged(privacyItems: List<PrivacyItem>)
        fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>)

        @JvmDefault
        fun onFlagAllChanged(flag: Boolean) {}

        @JvmDefault
        fun onFlagMicCameraChanged(flag: Boolean) {}
    }

    internal inner class Receiver : BroadcastReceiver() {
@@ -248,7 +283,7 @@ class PrivacyItemController @Inject constructor(
        private val list: List<PrivacyItem>
    ) : Runnable {
        override fun run() {
            callback?.privacyChanged(list)
            callback?.onPrivacyItemsChanged(list)
        }
    }

+43 −31
Original line number Diff line number Diff line
@@ -33,7 +33,6 @@ import android.media.AudioManager;
import android.os.Handler;
import android.os.Looper;
import android.provider.AlarmClock;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.service.notification.ZenModeConfig;
import android.text.format.DateUtils;
@@ -59,7 +58,6 @@ import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;

import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
@@ -154,7 +152,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements
    private Space mSpace;
    private BatteryMeterView mBatteryRemainingIcon;
    private RingerModeTracker mRingerModeTracker;
    private boolean mPermissionsHubEnabled;
    private boolean mAllIndicatorsEnabled;
    private boolean mMicCameraIndicatorsEnabled;

    private PrivacyItemController mPrivacyItemController;
    private final UiEventLogger mUiEventLogger;
@@ -173,26 +172,33 @@ public class QuickStatusBarHeader extends RelativeLayout implements
    private float mKeyguardExpansionFraction;
    private boolean mPrivacyChipLogged = false;

    private final DeviceConfig.OnPropertiesChangedListener mPropertiesListener =
            new DeviceConfig.OnPropertiesChangedListener() {
    private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
        @Override
                public void onPropertiesChanged(DeviceConfig.Properties properties) {
                    if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace())
                            && properties.getKeyset()
                            .contains(SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED)) {
                        mPermissionsHubEnabled = properties.getBoolean(
                                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false);
                        StatusIconContainer iconContainer = findViewById(R.id.statusIcons);
                        iconContainer.setIgnoredSlots(getIgnoredIconSlots());
        public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) {
            mPrivacyChip.setPrivacyList(privacyItems);
            setChipVisibility(!privacyItems.isEmpty());
        }

        @Override
        public void onFlagAllChanged(boolean flag) {
            if (mAllIndicatorsEnabled != flag) {
                mAllIndicatorsEnabled = flag;
                update();
            }
        }
            };

    private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
        @Override
        public void privacyChanged(List<PrivacyItem> privacyItems) {
            mPrivacyChip.setPrivacyList(privacyItems);
            setChipVisibility(!privacyItems.isEmpty());
        public void onFlagMicCameraChanged(boolean flag) {
            if (mMicCameraIndicatorsEnabled != flag) {
                mMicCameraIndicatorsEnabled = flag;
                update();
            }
        }

        private void update() {
            StatusIconContainer iconContainer = requireViewById(R.id.statusIcons);
            iconContainer.setIgnoredSlots(getIgnoredIconSlots());
            setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty());
        }
    };

@@ -276,7 +282,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements
        mRingerModeTextView.setSelected(true);
        mNextAlarmTextView.setSelected(true);

        mPermissionsHubEnabled = mPrivacyItemController.isPermissionsHubEnabled();
        mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
        mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
    }

    public QuickQSPanel getHeaderQsPanel() {
@@ -285,14 +292,16 @@ public class QuickStatusBarHeader extends RelativeLayout implements

    private List<String> getIgnoredIconSlots() {
        ArrayList<String> ignored = new ArrayList<>();
        if (getChipEnabled()) {
            ignored.add(mContext.getResources().getString(
                    com.android.internal.R.string.status_bar_camera));
            ignored.add(mContext.getResources().getString(
                    com.android.internal.R.string.status_bar_microphone));
        if (mPermissionsHubEnabled) {
            if (mAllIndicatorsEnabled) {
                ignored.add(mContext.getResources().getString(
                        com.android.internal.R.string.status_bar_location));
            }
        }

        return ignored;
    }
@@ -309,7 +318,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
    }

    private void setChipVisibility(boolean chipVisible) {
        if (chipVisible && mPermissionsHubEnabled) {
        if (chipVisible && getChipEnabled()) {
            mPrivacyChip.setVisibility(View.VISIBLE);
            // 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
@@ -519,9 +528,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements
        });
        mStatusBarIconController.addIconGroup(mIconManager);
        requestApplyInsets();
        // Change the ignored slots when DeviceConfig flag changes
        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_PRIVACY,
                mContext.getMainExecutor(), mPropertiesListener);
    }

    @Override
@@ -601,7 +607,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements
        setListening(false);
        mRingerModeTracker.getRingerModeInternal().removeObservers(this);
        mStatusBarIconController.removeIconGroup(mIconManager);
        DeviceConfig.removeOnPropertiesChangedListener(mPropertiesListener);
        super.onDetachedFromWindow();
    }

@@ -619,6 +624,9 @@ public class QuickStatusBarHeader extends RelativeLayout implements
            mZenController.addCallback(this);
            mAlarmController.addCallback(this);
            mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
            // Get the most up to date info
            mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
            mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
            mPrivacyItemController.addCallback(mPICCallback);
        } else {
            mZenController.removeCallback(this);
@@ -760,4 +768,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements
            updateHeaderTextContainerAlphaAnimator();
        }
    }

    private boolean getChipEnabled() {
        return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled;
    }
}
+6 −4
Original line number Diff line number Diff line
@@ -631,7 +631,7 @@ public class PhoneStatusBarPolicy
    }

    @Override  // PrivacyItemController.Callback
    public void privacyChanged(List<PrivacyItem> privacyItems) {
    public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) {
        updatePrivacyItems(privacyItems);
    }

@@ -664,16 +664,18 @@ public class PhoneStatusBarPolicy

        mIconController.setIconVisibility(mSlotCamera, showCamera);
        mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
        if (mPrivacyItemController.getAllIndicatorsAvailable()) {
            mIconController.setIconVisibility(mSlotLocation, showLocation);
        }
    }

    @Override
    public void onLocationActiveChanged(boolean active) {
        if (!mPrivacyItemController.isPermissionsHubEnabled()) updateLocation();
        if (!mPrivacyItemController.getAllIndicatorsAvailable()) updateLocationFromController();
    }

    // Updates the status view based on the current state of location requests.
    private void updateLocation() {
    private void updateLocationFromController() {
        if (mLocationController.isLocationActive()) {
            mIconController.setIconVisibility(mSlotLocation, true);
        } else {
Loading