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

Commit 9faaa273 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add DeviceConfig flags to Privacy Indicators" into qt-dev

parents 433b7fc8 16b26998
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -106,5 +106,12 @@ public final class SystemUiDeviceConfigFlags {
     */
    public static final String HASH_SALT_MAX_DAYS = "hash_salt_max_days";

    // Flag related to Privacy Indicators

    /**
     * Whether the Permissions Hub is showing.
     */
    public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";

    private SystemUiDeviceConfigFlags() { }
}
+83 −9
Original line number Diff line number Diff line
@@ -23,9 +23,13 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.os.UserHandle
import android.os.UserManager
import android.provider.DeviceConfig
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
import com.android.systemui.Dependency.BG_HANDLER_NAME
import com.android.systemui.Dependency.MAIN_HANDLER_NAME
import com.android.systemui.R
@@ -39,6 +43,9 @@ import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton

fun isPermissionsHubEnabled() = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)

@Singleton
class PrivacyItemController @Inject constructor(
        val context: Context,
@@ -47,7 +54,8 @@ class PrivacyItemController @Inject constructor(
        @Named(BG_HANDLER_NAME) private val bgHandler: Handler
) : Dumpable {

    companion object {
    @VisibleForTesting
    internal companion object {
        val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
                AppOpsManager.OP_RECORD_AUDIO,
                AppOpsManager.OP_COARSE_LOCATION,
@@ -57,6 +65,9 @@ class PrivacyItemController @Inject constructor(
                Intent.ACTION_MANAGED_PROFILE_REMOVED)
        const val TAG = "PrivacyItemController"
        const val SYSTEM_UID = 1000
        const val MSG_ADD_CALLBACK = 0
        const val MSG_REMOVE_CALLBACK = 1
        const val MSG_UPDATE_LISTENING_STATE = 2
    }

    @VisibleForTesting
@@ -70,6 +81,7 @@ class PrivacyItemController @Inject constructor(
    val systemApp =
            PrivacyApplication(context.getString(R.string.device_services), SYSTEM_UID, context)
    private val callbacks = mutableListOf<WeakReference<Callback>>()
    private val messageHandler = H(WeakReference(this), uiHandler.looper)

    private val notifyChanges = Runnable {
        val list = privacyList
@@ -81,6 +93,20 @@ class PrivacyItemController @Inject constructor(
        uiHandler.post(notifyChanges)
    }

    private var indicatorsAvailable = isPermissionsHubEnabled()
    @VisibleForTesting
    internal val devicePropertyChangedListener =
            object : DeviceConfig.OnPropertyChangedListener {
        override fun onPropertyChanged(namespace: String, name: String, value: String?) {
            if (DeviceConfig.NAMESPACE_PRIVACY.equals(namespace) &&
                    SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED.equals(name)) {
                indicatorsAvailable = java.lang.Boolean.parseBoolean(value)
                messageHandler.removeMessages(MSG_UPDATE_LISTENING_STATE)
                messageHandler.sendEmptyMessage(MSG_UPDATE_LISTENING_STATE)
            }
        }
    }

    private val cb = object : AppOpsController.Callback {
        override fun onActiveStateChanged(
            code: Int,
@@ -103,6 +129,11 @@ class PrivacyItemController @Inject constructor(
            registerReceiver()
        }

    init {
        DeviceConfig.addOnPropertyChangedListener(
                DeviceConfig.NAMESPACE_PRIVACY, context.mainExecutor, devicePropertyChangedListener)
    }

    private fun unregisterReceiver() {
        context.unregisterReceiver(userSwitcherReceiver)
    }
@@ -123,8 +154,14 @@ class PrivacyItemController @Inject constructor(
        bgHandler.post(updateListAndNotifyChanges)
    }

    @VisibleForTesting
    internal fun setListening(listen: Boolean) {
    /**
     * Updates listening status based on whether there are callbacks and the indicators are enabled
     *
     * 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
        if (listening == listen) return
        listening = listen
        if (listening) {
@@ -134,32 +171,44 @@ class PrivacyItemController @Inject constructor(
        } else {
            appOpsController.removeCallback(OPS, cb)
            unregisterReceiver()
            // Make sure that we remove all indicators and notify listeners if we are not
            // listening anymore due to indicators being disabled
            update(false)
        }
    }

    private fun addCallback(callback: WeakReference<Callback>) {
        callbacks.add(callback)
        if (callbacks.isNotEmpty() && !listening) setListening(true)
        if (callbacks.isNotEmpty() && !listening) {
            messageHandler.removeMessages(MSG_UPDATE_LISTENING_STATE)
            messageHandler.sendEmptyMessage(MSG_UPDATE_LISTENING_STATE)
        }
        // Notify this callback if we didn't set to listening
        else uiHandler.post(NotifyChangesToCallback(callback.get(), privacyList))
        else if (listening) uiHandler.post(NotifyChangesToCallback(callback.get(), privacyList))
    }

    private fun removeCallback(callback: WeakReference<Callback>) {
        // Removes also if the callback is null
        callbacks.removeIf { it.get()?.equals(callback.get()) ?: true }
        if (callbacks.isEmpty()) setListening(false)
        if (callbacks.isEmpty()) {
            messageHandler.removeMessages(MSG_UPDATE_LISTENING_STATE)
            messageHandler.sendEmptyMessage(MSG_UPDATE_LISTENING_STATE)
        }
    }

    fun addCallback(callback: Callback) {
        addCallback(WeakReference(callback))
        messageHandler.obtainMessage(MSG_ADD_CALLBACK, callback).sendToTarget()
    }

    fun removeCallback(callback: Callback) {
        removeCallback(WeakReference(callback))
        messageHandler.obtainMessage(MSG_REMOVE_CALLBACK, callback).sendToTarget()
    }

    private fun updatePrivacyList() {

        if (!listening) {
            privacyList = emptyList()
            return
        }
        val list = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
                .mapNotNull { toPrivacyItem(it) }.distinct()
        privacyList = list
@@ -217,4 +266,29 @@ class PrivacyItemController @Inject constructor(
            }
        }
    }

    private class H(
            private val outerClass: WeakReference<PrivacyItemController>,
            looper: Looper
    ) : Handler(looper) {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            when (msg.what) {
                MSG_UPDATE_LISTENING_STATE -> outerClass.get()?.setListeningState()

                MSG_ADD_CALLBACK -> {
                    if (msg.obj !is PrivacyItemController.Callback) return
                    outerClass.get()?.addCallback(
                            WeakReference(msg.obj as PrivacyItemController.Callback))
                }

                MSG_REMOVE_CALLBACK -> {
                    if (msg.obj !is PrivacyItemController.Callback) return
                    outerClass.get()?.removeCallback(
                            WeakReference(msg.obj as PrivacyItemController.Callback))
                }
                else -> {}
            }
        }
    }
}
 No newline at end of file
+29 −3
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ 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;
@@ -54,6 +55,7 @@ import android.widget.TextView;

import androidx.annotation.VisibleForTesting;

import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.DualToneHandler;
@@ -65,6 +67,7 @@ import com.android.systemui.privacy.OngoingPrivacyChip;
import com.android.systemui.privacy.PrivacyDialogBuilder;
import com.android.systemui.privacy.PrivacyItem;
import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.privacy.PrivacyItemControllerKt;
import com.android.systemui.qs.QSDetail.Callback;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -141,6 +144,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
    private OngoingPrivacyChip mPrivacyChip;
    private Space mSpace;
    private BatteryMeterView mBatteryRemainingIcon;
    private boolean mPermissionsHubEnabled;

    private PrivacyItemController mPrivacyItemController;

@@ -154,6 +158,20 @@ public class QuickStatusBarHeader extends RelativeLayout implements
    private boolean mHasTopCutout = false;
    private boolean mPrivacyChipLogged = false;

    private final DeviceConfig.OnPropertyChangedListener mPropertyListener =
            new DeviceConfig.OnPropertyChangedListener() {
                @Override
                public void onPropertyChanged(String namespace, String name, String value) {
                    if (DeviceConfig.NAMESPACE_PRIVACY.equals(namespace)
                            && SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED.equals(
                            name)) {
                        mPermissionsHubEnabled = Boolean.valueOf(value);
                        StatusIconContainer iconContainer = findViewById(R.id.statusIcons);
                        iconContainer.setIgnoredSlots(getIgnoredIconSlots());
                    }
                }
            };

    private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
        @Override
        public void privacyChanged(List<PrivacyItem> privacyItems) {
@@ -236,6 +254,12 @@ public class QuickStatusBarHeader extends RelativeLayout implements
        mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
        mRingerModeTextView.setSelected(true);
        mNextAlarmTextView.setSelected(true);

        mPermissionsHubEnabled = PrivacyItemControllerKt.isPermissionsHubEnabled();
        // Change the ignored slots when DeviceConfig flag changes
        DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_PRIVACY,
                mContext.getMainExecutor(), mPropertyListener);

    }

    private List<String> getIgnoredIconSlots() {
@@ -244,8 +268,10 @@ public class QuickStatusBarHeader extends RelativeLayout implements
                com.android.internal.R.string.status_bar_camera));
        ignored.add(mContext.getResources().getString(
                com.android.internal.R.string.status_bar_microphone));
        if (mPermissionsHubEnabled) {
            ignored.add(mContext.getResources().getString(
                    com.android.internal.R.string.status_bar_location));
        }

        return ignored;
    }
@@ -262,7 +288,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
    }

    private void setChipVisibility(boolean chipVisible) {
        if (chipVisible) {
        if (chipVisible && mPermissionsHubEnabled) {
            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
+18 −1
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.privacy.PrivacyItem;
import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.privacy.PrivacyItemControllerKt;
import com.android.systemui.privacy.PrivacyType;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
@@ -82,7 +83,8 @@ public class PhoneStatusBarPolicy
                ZenModeController.Callback,
                DeviceProvisionedListener,
                KeyguardMonitor.Callback,
                PrivacyItemController.Callback {
                PrivacyItemController.Callback,
                LocationController.LocationChangeCallback {
    private static final String TAG = "PhoneStatusBarPolicy";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

@@ -257,6 +259,7 @@ public class PhoneStatusBarPolicy
        mKeyguardMonitor.addCallback(this);
        mPrivacyItemController.addCallback(this);
        mSensorPrivacyController.addCallback(mSensorPrivacyListener);
        mLocationController.addCallback(this);

        SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallback(this);
    }
@@ -635,6 +638,20 @@ public class PhoneStatusBarPolicy
        mIconController.setIconVisibility(mSlotLocation, showLocation);
    }

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

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

    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
+9 −0
Original line number Diff line number Diff line
@@ -249,6 +249,15 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
        requestLayout();
    }

    /**
     * Sets the list of ignored icon slots clearing the current list.
     * @param slots names of the icons to ignore
     */
    public void setIgnoredSlots(List<String> slots) {
        mIgnoredSlots.clear();
        addIgnoredSlots(slots);
    }

    /**
     * Layout is happening from end -> start
     */
Loading