diff options
author | Ganesh Olekar <olekarg@google.com> | 2021-09-17 16:02:23 +0000 |
---|---|---|
committer | Ganesh Olekar <olekarg@google.com> | 2022-02-03 17:44:13 +0000 |
commit | d209f63b29715e3e1d47f10dbeb2d98e67344817 (patch) | |
tree | e2c5b84b91ab71c8ba2945a271c61484dd556bf8 | |
parent | 6d24f7f84f7296e9cd2391de01adf2870b2e59bc (diff) | |
download | Permission-d209f63b29715e3e1d47f10dbeb2d98e67344817.tar.gz |
Add warning banners to the permission pages if the sensor is blocked
Test: Added tests to check warning banners are displayed when sensors are blocked
Test: atest SensorBlockedBannerTest -c
Bug: 197757242
Change-Id: I96b72c72c09dba1428ac0c5b6f83b741713577e4
Merged-In: Iaca65d1e98965723d4461b2972917fc508f2e085
Merged-In: Ic770e4a56df7a6afce7b4ebe809979d6d177807d
Merged-In: If695ac98448999838bc34f0bb9c703e15ee15475
Merged-In: Ic81b7a14a3bcfa0379891cd46c4bf05c0ad986b2
Merged-In: I5664b7895cd307088c66c078c9ef57bbb2f9a5ca
Merged-In: Ifdd99cbc9385f43d162fc2a1102c32fb0ccd1644
Merged-in: Iba3f9d776a166e4ab119c765596dbd51af26a869
15 files changed, 532 insertions, 4 deletions
diff --git a/PermissionController/AndroidManifest.xml b/PermissionController/AndroidManifest.xml index a6fee5833..6c9cf207c 100644 --- a/PermissionController/AndroidManifest.xml +++ b/PermissionController/AndroidManifest.xml @@ -46,6 +46,7 @@ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" /> + <uses-permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY" /> <!-- TODO(b/170896938): make this privileged(signature may only work on pixel) --> <uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" /> <uses-permission android:name="android.permission.START_VIEW_PERMISSION_USAGE" /> diff --git a/PermissionController/res/drawable/ic_camera_blocked.xml b/PermissionController/res/drawable/ic_camera_blocked.xml new file mode 100644 index 000000000..775974e48 --- /dev/null +++ b/PermissionController/res/drawable/ic_camera_blocked.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?attr/colorControlNormal"> + <path android:fillColor="@android:color/white" + android:pathData="M18,10.48V6c0,-1.1 -0.9,-2 -2,-2H6.83l2,2H16v7.17l2,2v-1.65l4,3.98v-11l-4,3.98zM16,16L6,6 4,4 2.81,2.81 1.39,4.22l0.85,0.85C2.09,5.35 2,5.66 2,6v12c0,1.1 0.9,2 2,2h12c0.34,0 0.65,-0.09 0.93,-0.24l2.85,2.85 1.41,-1.41L18,18l-2,-2zM4,18V6.83L15.17,18H4z"/> +</vector> diff --git a/PermissionController/res/drawable/ic_location_blocked.xml b/PermissionController/res/drawable/ic_location_blocked.xml new file mode 100644 index 000000000..cdfc85336 --- /dev/null +++ b/PermissionController/res/drawable/ic_location_blocked.xml @@ -0,0 +1,26 @@ +<!-- + ~ Copyright (C) 2021 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. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M21.19,21.19l-4.92,-4.92 -1.45,-1.45 -7.51,-7.51 -1.53,-1.53 -2.97,-2.97 -1.42,1.41 3.7,3.7C5.04,8.27 5,8.63 5,9c0,5.25 7,13 7,13s1.52,-1.69 3.15,-4.02l4.63,4.63 1.41,-1.42zM12,18.88c-1.87,-2.39 -4.41,-6.15 -4.91,-8.95l6.62,6.62c-0.58,0.84 -1.17,1.64 -1.71,2.33zM12,4c2.76,0 5,2.24 5,5 0,1.16 -0.48,2.56 -1.18,3.99l1.48,1.48C18.28,12.62 19,10.68 19,9c0,-3.87 -3.13,-7 -7,-7 -1.98,0 -3.76,0.82 -5.03,2.14l1.42,1.42C9.3,4.6 10.58,4 12,4zM10.15,7.33l3.52,3.52c0.5,-0.46 0.83,-1.11 0.83,-1.85 0,-1.38 -1.12,-2.5 -2.5,-2.5 -0.73,0 -1.39,0.32 -1.85,0.83z"/> +</vector>
\ No newline at end of file diff --git a/PermissionController/res/drawable/ic_mic_blocked.xml b/PermissionController/res/drawable/ic_mic_blocked.xml new file mode 100644 index 000000000..21fc7aa8b --- /dev/null +++ b/PermissionController/res/drawable/ic_mic_blocked.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?attr/colorControlNormal"> + <path android:fillColor="@android:color/white" + android:pathData="M11,5c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v5.17l1.82,1.82c0.11,-0.31 0.18,-0.64 0.18,-0.99V5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v1.17l2,2V5zM2.81,2.81L1.39,4.22l11.65,11.65c-0.33,0.08 -0.68,0.13 -1.04,0.13 -2.76,0 -5,-2.24 -5,-5H5c0,3.53 2.61,6.43 6,6.92V21h2v-3.08c0.57,-0.08 1.12,-0.24 1.64,-0.46l5.14,5.14 1.41,-1.41L2.81,2.81zM19,11h-2c0,0.91 -0.26,1.75 -0.69,2.48l1.46,1.46C18.54,13.82 19,12.47 19,11z"/> +</vector> diff --git a/PermissionController/res/layout/app_permission.xml b/PermissionController/res/layout/app_permission.xml index 222dcf8b7..48de3864d 100644 --- a/PermissionController/res/layout/app_permission.xml +++ b/PermissionController/res/layout/app_permission.xml @@ -174,7 +174,6 @@ android:src="@drawable/ic_info_outline" android:layout_marginBottom="16dp" android:layout_marginStart="24dp" - android:contentDescription="Info" style="@style/ImageViewIcon" /> <TextView diff --git a/PermissionController/res/layout/warning_banner_preference_card.xml b/PermissionController/res/layout/warning_banner_preference_card.xml new file mode 100644 index 000000000..611d24b82 --- /dev/null +++ b/PermissionController/res/layout/warning_banner_preference_card.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2021 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + style="@style/WarningBannerMainContainer" > + + <androidx.cardview.widget.CardView + style="@style/WarningBannerCardView" > + + <RelativeLayout + style="@style/WarningBannerDimensions" > + + <androidx.preference.internal.PreferenceImageView + android:id="@android:id/icon" + style="@style/WarningBannerIcon" /> + + <TextView android:id="@android:id/title" + android:layout_below="@android:id/icon" + style="@style/WarningBannerTitle" /> + + <TextView android:id="@android:id/summary" + android:layout_below="@android:id/title" + android:layout_alignStart="@android:id/title" + style="@style/WarningBannerSummary" /> + + <Button + android:id="@+id/button_id" + android:layout_below="@android:id/summary" + android:focusable="true" + style="@style/WarningBannerButton" /> + + </RelativeLayout> + + </androidx.cardview.widget.CardView> + + <!-- Preference should place its actual preference widget here. --> + <LinearLayout android:id="@android:id/widget_frame" + style="@style/WarningBannerWidgetFrame" /> + +</LinearLayout> diff --git a/PermissionController/res/values-night-v31/colors.xml b/PermissionController/res/values-night-v31/colors.xml new file mode 100644 index 000000000..e5e451870 --- /dev/null +++ b/PermissionController/res/values-night-v31/colors.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 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. + --> +<resources> + <color name="warning_surface">#333124</color> + <color name="warning_onsurface">#FDD663</color> +</resources>
\ No newline at end of file diff --git a/PermissionController/res/values-v31/colors.xml b/PermissionController/res/values-v31/colors.xml new file mode 100644 index 000000000..1e4d8c1c8 --- /dev/null +++ b/PermissionController/res/values-v31/colors.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 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. + --> +<resources> + <color name="warning_surface">#F0E3A8</color> + <color name="warning_onsurface">#895900</color> +</resources>
\ No newline at end of file diff --git a/PermissionController/res/values/overlayable.xml b/PermissionController/res/values/overlayable.xml index 47e9cd615..10561a933 100644 --- a/PermissionController/res/values/overlayable.xml +++ b/PermissionController/res/values/overlayable.xml @@ -279,6 +279,18 @@ <item type="bool" name="config_useMaterial3PermissionGrantDialog" /> <!-- END GENERAL CONFIGS --> + <!-- START WARNING BANNER PREFERENCE STYLE --> + <item type="style" name="WarningBannerMainContainer" /> + <item type="style" name="WarningBannerCardView" /> + <item type="style" name="WarningBannerIcon" /> + <item type="style" name="WarningBannerDimensions" /> + <item type="style" name="WarningBannerText" /> + <item type="style" name="WarningBannerTitle" /> + <item type="style" name="WarningBannerSummary" /> + <item type="style" name="WarningBannerButton" /> + <item type="style" name="WarningBannerWidgetFrame" /> + <!-- END WARNING BANNER PREFERENCE STYLE --> + </policy> </overlayable> diff --git a/PermissionController/res/values/strings.xml b/PermissionController/res/values/strings.xml index a3650709a..cdaa61c07 100644 --- a/PermissionController/res/values/strings.xml +++ b/PermissionController/res/values/strings.xml @@ -1342,4 +1342,17 @@ Allow <xliff:g id="app_name" example="Gmail">%4$s</xliff:g> to upload a bug repo <!-- Info label for permissions for apps holding special exempted roles. [CHAR LIMIT=none] --> <string name="exempt_info_label"><xliff:g id="app_name" example="Gmail">%1$s</xliff:g> is protected by Android. Because your data is processed on this device, this app’s permission usage isn’t shown on your privacy dashboard. </string> + <!-- Info label for the warning banner title if camera is blocked [CHAR LIMIT=none] --> + <string name="blocked_camera_title">Device camera is blocked</string> + <!-- Info label for the warning banner title if microphone is blocked [CHAR LIMIT=none] --> + <string name="blocked_microphone_title">Device microphone is blocked</string> + <!-- Info label for the warning banner title if location is off [CHAR LIMIT=none] --> + <string name="blocked_location_title">Device location is off</string> + <!-- Info label to display that the sensor is blocked for apps and services [CHAR LIMIT=none] --> + <string name="blocked_sensor_summary">For apps and services</string> + <!-- Info label to display that the mic is blocked for apps and services [CHAR LIMIT=none] --> + <string name="blocked_mic_summary">Microphone data may still be shared when you call an emergency number.</string> + <!-- Label for the button to change the sensor status [CHAR LIMIT=none] --> + <string name="blocked_sensor_button_label">Change</string> + </resources> diff --git a/PermissionController/res/values/styles.xml b/PermissionController/res/values/styles.xml index 4478b01a1..a7424bfbc 100644 --- a/PermissionController/res/values/styles.xml +++ b/PermissionController/res/values/styles.xml @@ -1094,4 +1094,87 @@ <!-- END PRIVACY DASHBOARD --> + <!-- START WARNING BANNER --> + + <style name="WarningBannerMainContainer"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:background">?android:attr/selectableItemBackground</item> + <item name="android:clipToPadding">false</item> + <item name="android:focusable">true</item> + <item name="android:gravity">center_vertical</item> + <item name="android:minHeight">?android:attr/listPreferredItemHeightSmall</item> + <item name="android:paddingEnd">?android:attr/listPreferredItemPaddingEnd</item> + <item name="android:paddingStart">?android:attr/listPreferredItemPaddingStart</item> + </style> + + <style name="WarningBannerCardView" + xmlns:card_view="http://schemas.android.com/apk/res-auto" + xmlns:app="http://schemas.android.com/apk/res-auto" > + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="app:cardCornerRadius">20dp</item> + <item name="app:cardBackgroundColor">@color/warning_surface</item> + <item name="app:cardElevation">0dp</item> + <item name="card_view:contentPaddingBottom">8dp</item> + <item name="card_view:contentPaddingTop">20dp</item> + <item name="card_view:contentPaddingLeft">20dp</item> + <item name="card_view:contentPaddingRight">20dp</item> + </style> + + <style name="WarningBannerIcon"> + <item name="android:layout_width">24dp</item> + <item name="android:layout_height">24dp</item> + <item name="android:scaleType">fitCenter</item> + <item name="android:layout_marginBottom">8dp</item> + <item name="android:tint">@color/warning_onsurface</item> + </style> + + <style name="WarningBannerDimensions"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + </style> + + <style name="WarningBannerText"> + <item name="android:layout_marginBottom">8dp</item> + <item name="android:textColor">?android:attr/textColorPrimary</item> + </style> + + <style name="WarningBannerTitle" parent="@style/WarningBannerText"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:textSize">20sp</item> + <item name="android:lineHeight">24sp</item> + </style> + + <style name="WarningBannerSummary" parent="@style/WarningBannerText"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:textSize">14sp</item> + <item name="android:lineHeight">20sp</item> + </style> + + <style name="WarningBannerButton"> + <item name="android:layout_height">48dp</item> + <item name="android:minWidth">48dp</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_alignParentBottom">true</item> + <item name="android:layout_alignParentEnd">true</item> + <item name="android:textSize">14sp</item> + <item name="android:lineHeight">20sp</item> + <item name="android:layout_marginBottom">8dp</item> + <item name="android:textColor">@color/warning_onsurface</item> + <item name="android:background">?android:attr/selectableItemBackground</item> + </style> + + <style name="WarningBannerWidgetFrame"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">match_parent</item> + <item name="android:gravity">end|center_vertical</item> + <item name="android:paddingStart">16dp</item> + <item name="android:orientation">vertical</item> + </style> + + <!-- END WARNING BANNER --> + </resources> diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/CardViewPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/CardViewPreference.java new file mode 100644 index 000000000..6c76d906b --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/CardViewPreference.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 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.permissioncontroller.permission.ui.handheld; + +import android.content.Context; +import android.content.Intent; +import android.view.View; +import android.widget.Button; + +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + +import com.android.permissioncontroller.R; + + +/** + * A Preference representing a banner message represented as a CardView + */ +public class CardViewPreference extends Preference { + + private String mAction; + + public CardViewPreference(Context context, String action) { + super(context); + this.setLayoutResource(R.layout.warning_banner_preference_card); + mAction = action; + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + Button button = (Button) holder.findViewById(R.id.button_id); + button.setText(R.string.blocked_sensor_button_label); + button.setContentDescription(getContext().getString(R.string.blocked_sensor_button_label)); + button.setVisibility(View.VISIBLE); + button.setOnClickListener(v -> { + getContext().startActivity(new Intent(mAction)); + }); + } + +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java index 4491557d3..50c64df02 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java @@ -45,6 +45,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; +import android.provider.Settings; import android.util.ArrayMap; import android.util.Log; import android.view.Menu; @@ -97,6 +98,7 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem private static final String LOG_TAG = "PermissionAppsFragment"; private static final String STORAGE_ALLOWED_FULL = "allowed_storage_full"; private static final String STORAGE_ALLOWED_SCOPED = "allowed_storage_scoped"; + private static final String BLOCKED_SENSOR_PREF_KEY = "sensor_card"; private static final int SHOW_LOAD_DELAY_MS = 200; private static final int AGGREGATE_DATA_FILTER_BEGIN_DAYS = 1; @@ -123,6 +125,7 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem private PermissionAppsViewModel mViewModel; private PermissionUsages mPermissionUsages; private List<AppPermissionUsage> mAppPermissionUsages = new ArrayList<>(); + private Boolean mSensorStatus; @Override public void onCreate(Bundle savedInstanceState) { @@ -173,6 +176,10 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem mPermissionUsages.load(null, null, filterTimeBeginMillis, Long.MAX_VALUE, PermissionUsages.USAGE_FLAG_LAST, getActivity().getLoaderManager(), false, false, this, false); + + if (Utils.shouldDisplayCardIfBlocked(mPermGroupName)) { + mViewModel.getSensorStatusLiveData().observe(this, this::setSensorStatus); + } } } @@ -243,6 +250,60 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem } } + @RequiresApi(Build.VERSION_CODES.S) + private void setSensorStatus(Boolean sensorStatus) { + mSensorStatus = sensorStatus; + displaySensorCard(); + } + + @RequiresApi(Build.VERSION_CODES.S) + private void displaySensorCard() { + if (Utils.shouldDisplayCardIfBlocked(mPermGroupName)) { + if (mSensorStatus) { + setSensorCard(); + } else { + removeSensorCard(); + } + } + } + + @RequiresApi(Build.VERSION_CODES.S) + private void setSensorCard() { + CardViewPreference sensorCard = findPreference(BLOCKED_SENSOR_PREF_KEY); + if (sensorCard == null) { + sensorCard = createSensorCard(); + getPreferenceScreen().addPreference(sensorCard); + } + sensorCard.setVisible(true); + } + + @RequiresApi(Build.VERSION_CODES.S) + private CardViewPreference createSensorCard() { + boolean isLocation = Manifest.permission_group.LOCATION.equals(mPermGroupName); + Context context = getPreferenceManager().getContext(); + String action = isLocation ? Settings.ACTION_LOCATION_SOURCE_SETTINGS + : Settings.ACTION_PRIVACY_SETTINGS; + CardViewPreference sensorCard = new CardViewPreference(context, action); + sensorCard.setKey(BLOCKED_SENSOR_PREF_KEY); + sensorCard.setIcon(Utils.getBlockedIcon(mPermGroupName)); + sensorCard.setTitle(Utils.getBlockedTitle(mPermGroupName)); + boolean isMicrophone = Manifest.permission_group.MICROPHONE.equals(mPermGroupName); + int cardSummary = + isMicrophone ? R.string.blocked_mic_summary : R.string.blocked_sensor_summary; + sensorCard.setSummary(context.getString(cardSummary)); + sensorCard.setVisible(true); + sensorCard.setOrder(-1); + return sensorCard; + } + + @RequiresApi(Build.VERSION_CODES.S) + private void removeSensorCard() { + CardViewPreference sensorCard = findPreference(BLOCKED_SENSOR_PREF_KEY); + if (sensorCard != null) { + sensorCard.setVisible(false); + } + } + @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); @@ -285,8 +346,11 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem Map<String, Preference> existingPrefs = new ArrayMap<>(); for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) { - PreferenceCategory category = (PreferenceCategory) - getPreferenceScreen().getPreference(i); + Preference pref = getPreferenceScreen().getPreference(i); + if (BLOCKED_SENSOR_PREF_KEY.equals(pref.getKey())) { + continue; + } + PreferenceCategory category = (PreferenceCategory) pref; category.setOrderingAsAdded(true); int numPreferences = category.getPreferenceCount(); for (int j = 0; j < numPreferences; j++) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt index c7a13dbab..46b0d2cfb 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt @@ -17,10 +17,14 @@ package com.android.permissioncontroller.permission.ui.model import android.Manifest +import android.Manifest.permission_group.LOCATION import android.app.Application import android.content.Intent +import android.hardware.SensorPrivacyManager +import android.os.Build import android.os.Bundle import android.os.UserHandle +import androidx.annotation.RequiresApi import androidx.fragment.app.Fragment import androidx.lifecycle.AbstractSavedStateViewModelFactory import androidx.lifecycle.MediatorLiveData @@ -33,6 +37,7 @@ import com.android.permissioncontroller.permission.data.AllPackageInfosLiveData import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData.FullStoragePackageState import com.android.permissioncontroller.permission.data.SinglePermGroupPackagesUiInfoLiveData +import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState import com.android.permissioncontroller.permission.ui.Category import com.android.permissioncontroller.permission.ui.LocationProviderInterceptDialog @@ -42,6 +47,7 @@ import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewMo import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel.Companion.SHOW_ALWAYS_ALLOWED import com.android.permissioncontroller.permission.utils.LocationUtils import com.android.permissioncontroller.permission.utils.navigateSafe +import com.android.permissioncontroller.permission.utils.Utils /** * ViewModel for the PermissionAppsFragment. Has a liveData with all of the UI info for each @@ -70,6 +76,12 @@ class PermissionAppsViewModel( val showAllowAlwaysStringLiveData = state.getLiveData(SHOW_ALWAYS_ALLOWED, false) val categorizedAppsLiveData = CategorizedAppsLiveData(groupName) + @get:RequiresApi(Build.VERSION_CODES.S) + val sensorStatusLiveData: SensorStatusLiveData by lazy(LazyThreadSafetyMode.NONE) + @RequiresApi(Build.VERSION_CODES.S) { + SensorStatusLiveData() + } + fun updateShowSystem(showSystem: Boolean) { if (showSystem != state.get(SHOULD_SHOW_SYSTEM_KEY)) { state.set(SHOULD_SHOW_SYSTEM_KEY, showSystem) @@ -80,6 +92,65 @@ class PermissionAppsViewModel( get() = state.get(CREATION_LOGGED_KEY) ?: false set(value) = state.set(CREATION_LOGGED_KEY, value) + /** + * A LiveData that tracks the status (blocked or available) of a sensor + */ + @RequiresApi(Build.VERSION_CODES.S) + inner class SensorStatusLiveData() : SmartUpdateMediatorLiveData<Boolean>() { + val sensorPrivacyManager = app.getSystemService(SensorPrivacyManager::class.java)!! + val sensor = Utils.getSensorCode(groupName) + val isLocation = LOCATION.equals(groupName) + + init { + checkAndUpdateStatus() + } + + fun checkAndUpdateStatus() { + var blocked: Boolean + + if (isLocation) { + blocked = !LocationUtils.isLocationEnabled(app.getApplicationContext()) + } else { + blocked = sensorPrivacyManager.isSensorPrivacyEnabled(sensor) + } + + if (blocked) { + value = blocked + } + } + + override fun onActive() { + super.onActive() + checkAndUpdateStatus() + if (isLocation) { + LocationUtils.addLocationListener(locListener) + } else { + sensorPrivacyManager.addSensorPrivacyListener(sensor, listener) + } + } + + override fun onInactive() { + super.onInactive() + if (isLocation) { + LocationUtils.removeLocationListener(locListener) + } else { + sensorPrivacyManager.removeSensorPrivacyListener(sensor, listener) + } + } + + private val listener = { sensor: Int, status: Boolean -> + value = status + } + + private val locListener = { status: Boolean -> + value = !status + } + + override fun onUpdate() { + // Do nothing + } + } + inner class CategorizedAppsLiveData(groupName: String) : MediatorLiveData<@kotlin.jvm.JvmSuppressWildcards Map<Category, List<Pair<String, UserHandle>>>>() { @@ -265,4 +336,4 @@ class PermissionAppsViewModelFactory( @Suppress("UNCHECKED_CAST") return PermissionAppsViewModel(state, app, groupName) as T } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java b/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java index 155ab5ab9..74328747e 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java @@ -64,6 +64,7 @@ import android.content.res.Resources.Theme; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.hardware.SensorPrivacyManager; import android.os.Build; import android.os.Parcelable; import android.os.Process; @@ -84,6 +85,7 @@ import android.view.MenuItem; import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import androidx.annotation.StringRes; import androidx.core.text.BidiFormatter; import androidx.core.util.Preconditions; @@ -160,6 +162,9 @@ public final class Utils { private static final String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED = "location_access_check_enabled"; + /** Whether or not warning banner is displayed when device sensors are off **/ + public static final String PROPERTY_WARNING_BANNER_DISPLAY_ENABLED = "warning_banner_enabled"; + /** All permission whitelists. */ public static final int FLAGS_PERMISSION_WHITELIST_ALL = PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM @@ -193,6 +198,13 @@ public final class Utils { private static final ArrayMap<String, Integer> PERM_GROUP_UPGRADE_REQUEST_RES; private static final ArrayMap<String, Integer> PERM_GROUP_UPGRADE_REQUEST_DETAIL_RES; + /** Permission -> Sensor codes */ + private static final ArrayMap<String, Integer> PERM_SENSOR_CODES; + /** Permission -> Icon res id */ + private static final ArrayMap<String, Integer> PERM_BLOCKED_ICON; + /** Permission -> Title res id */ + private static final ArrayMap<String, Integer> PERM_BLOCKED_TITLE; + public static final int FLAGS_ALWAYS_USER_SENSITIVE = FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED | FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED; @@ -351,6 +363,23 @@ public final class Utils { .put(MICROPHONE, R.string.permgroupupgraderequestdetail_microphone); PERM_GROUP_UPGRADE_REQUEST_DETAIL_RES .put(CAMERA, R.string.permgroupupgraderequestdetail_camera); + + PERM_SENSOR_CODES = new ArrayMap<>(); + if (SdkLevel.isAtLeastS()) { + PERM_SENSOR_CODES.put(CAMERA, SensorPrivacyManager.Sensors.CAMERA); + PERM_SENSOR_CODES.put(MICROPHONE, SensorPrivacyManager.Sensors.MICROPHONE); + } + + PERM_BLOCKED_ICON = new ArrayMap<>(); + PERM_BLOCKED_ICON.put(CAMERA, R.drawable.ic_camera_blocked); + PERM_BLOCKED_ICON.put(MICROPHONE, R.drawable.ic_mic_blocked); + PERM_BLOCKED_ICON.put(LOCATION, R.drawable.ic_location_blocked); + + PERM_BLOCKED_TITLE = new ArrayMap<>(); + PERM_BLOCKED_TITLE.put(CAMERA, R.string.blocked_camera_title); + PERM_BLOCKED_TITLE.put(MICROPHONE, R.string.blocked_microphone_title); + PERM_BLOCKED_TITLE.put(LOCATION, R.string.blocked_location_title); + } private Utils() { @@ -1250,4 +1279,36 @@ public final class Utils { || Manifest.permission_group.CAMERA.equals(groupName) || Manifest.permission_group.MICROPHONE.equals(groupName); } + + /** + * Returns if a card should be shown if the sensor is blocked + **/ + public static boolean shouldDisplayCardIfBlocked(@NonNull String permissionGroupName) { + return DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_WARNING_BANNER_DISPLAY_ENABLED, true) && ( + CAMERA.equals(permissionGroupName) || MICROPHONE.equals(permissionGroupName) + || LOCATION.equals(permissionGroupName)); + } + + /** + * Returns the sensor code for a permission + **/ + @RequiresApi(Build.VERSION_CODES.S) + public static int getSensorCode(@NonNull String permissionGroupName) { + return PERM_SENSOR_CODES.getOrDefault(permissionGroupName, -1); + } + + /** + * Returns the blocked icon code for a permission + **/ + public static int getBlockedIcon(@NonNull String permissionGroupName) { + return PERM_BLOCKED_ICON.getOrDefault(permissionGroupName, -1); + } + + /** + * Returns the blocked title code for a permission + **/ + public static int getBlockedTitle(@NonNull String permissionGroupName) { + return PERM_BLOCKED_TITLE.getOrDefault(permissionGroupName, -1); + } } |