summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PermissionController/Android.bp1
-rw-r--r--PermissionController/res/drawable/grant_dialog_permission_rationale_background.xml23
-rw-r--r--PermissionController/res/drawable/ic_more_info_arrow.xml23
-rw-r--r--PermissionController/res/drawable/ic_shield_exclamation_outline.xml23
-rw-r--r--PermissionController/res/layout/grant_permissions.xml23
-rw-r--r--PermissionController/res/layout/grant_permissions_material3.xml23
-rw-r--r--PermissionController/res/values/overlayable.xml6
-rw-r--r--PermissionController/res/values/strings.xml9
-rw-r--r--PermissionController/res/values/styles.xml38
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/SafetyLabelLiveData.kt116
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java14
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsViewHandler.java6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java5
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt38
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt76
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java5
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java5
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt28
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/SafetyLabelPermissionMapping.kt44
-rw-r--r--PermissionController/tests/mocking/Android.bp1
-rw-r--r--SafetyLabel/java/com/android/permission/safetylabel/DataLabel.java2
-rw-r--r--SafetyLabel/java/com/android/permission/safetylabel/DataType.java2
-rw-r--r--SafetyLabel/java/com/android/permission/safetylabel/SafetyLabel.java2
23 files changed, 491 insertions, 22 deletions
diff --git a/PermissionController/Android.bp b/PermissionController/Android.bp
index c254036f0..ca4d8ceeb 100644
--- a/PermissionController/Android.bp
+++ b/PermissionController/Android.bp
@@ -140,6 +140,7 @@ android_app {
"modules-utils-build_system",
"safety-center-resources-lib",
"lottie",
+ "safety-label"
],
proto: {
diff --git a/PermissionController/res/drawable/grant_dialog_permission_rationale_background.xml b/PermissionController/res/drawable/grant_dialog_permission_rationale_background.xml
new file mode 100644
index 000000000..b76b68c5e
--- /dev/null
+++ b/PermissionController/res/drawable/grant_dialog_permission_rationale_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <corners android:radius="16dp"/>
+ <stroke android:width="1dp" android:color="?androidprv:attr/textColorSecondaryInverse" />
+</shape> \ No newline at end of file
diff --git a/PermissionController/res/drawable/ic_more_info_arrow.xml b/PermissionController/res/drawable/ic_more_info_arrow.xml
new file mode 100644
index 000000000..73eb5ccfc
--- /dev/null
+++ b/PermissionController/res/drawable/ic_more_info_arrow.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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="48dp"
+ android:height="48dp" android:viewportWidth="48" android:viewportHeight="48"
+ android:autoMirrored="true" android:tint="?attr/colorControlNormal">
+ <path android:fillColor="@android:color/white"
+ android:pathData="M18.75,36 L16.6,33.85 26.5,23.95 16.6,14.05 18.75,11.9 30.8,23.95Z"/>
+</vector> \ No newline at end of file
diff --git a/PermissionController/res/drawable/ic_shield_exclamation_outline.xml b/PermissionController/res/drawable/ic_shield_exclamation_outline.xml
new file mode 100644
index 000000000..5785babf9
--- /dev/null
+++ b/PermissionController/res/drawable/ic_shield_exclamation_outline.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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="48dp"
+ android:height="48dp" android:viewportWidth="48" android:viewportHeight="48"
+ android:tint="?attr/colorControlNormal">
+ <path android:fillColor="@android:color/white"
+ android:pathData="M24,31.3Q24.7,31.3 25.2,30.8Q25.7,30.3 25.7,29.6Q25.7,28.9 25.2,28.4Q24.7,27.9 24,27.9Q23.3,27.9 22.8,28.4Q22.3,28.9 22.3,29.6Q22.3,30.3 22.8,30.8Q23.3,31.3 24,31.3ZM22.5,24.6H25.5V14.25H22.5ZM24,43.95Q17,42.2 12.5,35.825Q8,29.45 8,21.85V9.95L24,3.95L40,9.95V21.85Q40,29.45 35.5,35.825Q31,42.2 24,43.95ZM24,40.85Q29.75,38.95 33.375,33.675Q37,28.4 37,21.85V12.05L24,7.15L11,12.05V21.85Q11,28.4 14.625,33.675Q18.25,38.95 24,40.85ZM24,24Q24,24 24,24Q24,24 24,24Q24,24 24,24Q24,24 24,24Z"/>
+</vector>
diff --git a/PermissionController/res/layout/grant_permissions.xml b/PermissionController/res/layout/grant_permissions.xml
index ed1873519..ca0706171 100644
--- a/PermissionController/res/layout/grant_permissions.xml
+++ b/PermissionController/res/layout/grant_permissions.xml
@@ -62,6 +62,29 @@
</LinearLayout>
+ <!-- permission rationale -->
+ <LinearLayout
+ android:id="@+id/permission_rationale_container"
+ style="@style/PermissionGrantPermissionRationaleContent">
+
+ <ImageView
+ android:id="@+id/permission_rationale_icon"
+ android:importantForAccessibility="no"
+ android:src="@drawable/ic_shield_exclamation_outline"
+ style="@style/PermissionGrantPermissionRationaleIcon" />
+
+ <TextView
+ android:id="@+id/permission_rationale_message"
+ style="@style/PermissionGrantPermissionRationaleMessage" />
+
+ <ImageView
+ android:id="@+id/permission_rationale_more_info_icon"
+ android:importantForAccessibility="no"
+ android:src="@drawable/ic_more_info_arrow"
+ style="@style/PermissionGrantPermissionRationaleMoreInfoIcon" />
+
+ </LinearLayout>
+
<!-- location (precise/approximate) animations -->
<LinearLayout
android:layout_width="match_parent"
diff --git a/PermissionController/res/layout/grant_permissions_material3.xml b/PermissionController/res/layout/grant_permissions_material3.xml
index 843ea9bb7..9c3ab6eef 100644
--- a/PermissionController/res/layout/grant_permissions_material3.xml
+++ b/PermissionController/res/layout/grant_permissions_material3.xml
@@ -63,6 +63,29 @@
</LinearLayout>
+ <!-- permission rationale -->
+ <LinearLayout
+ android:id="@+id/permission_rationale_container"
+ style="@style/PermissionGrantPermissionRationaleContent">
+
+ <ImageView
+ android:id="@+id/permission_rationale_icon"
+ android:importantForAccessibility="no"
+ android:src="@drawable/ic_shield_exclamation_outline"
+ style="@style/PermissionGrantPermissionRationaleIcon" />
+
+ <TextView
+ android:id="@+id/permission_rationale_message"
+ style="@style/PermissionGrantPermissionRationaleMessage" />
+
+ <ImageView
+ android:id="@+id/permission_rationale_more_info_icon"
+ android:importantForAccessibility="no"
+ android:src="@drawable/ic_more_info_arrow"
+ style="@style/PermissionGrantPermissionRationaleMoreInfoIcon" />
+
+ </LinearLayout>
+
<!-- location (precise/approximate) animations -->
<LinearLayout
android:layout_width="match_parent"
diff --git a/PermissionController/res/values/overlayable.xml b/PermissionController/res/values/overlayable.xml
index 6e7973fb1..ed8dc8969 100644
--- a/PermissionController/res/values/overlayable.xml
+++ b/PermissionController/res/values/overlayable.xml
@@ -61,6 +61,12 @@
<item type="style" name="PermissionGrantButtonAllowOneTime" />
<item type="style" name="PermissionGrantButtonDeny" />
<item type="style" name="PermissionGrantButtonNoUpgrade" />
+
+ <item type="style" name="PermissionGrantPermissionRationaleContent" />
+ <item type="style" name="PermissionGrantPermissionRationaleIcon" />
+ <item type="style" name="PermissionGrantPermissionRationaleMessage" />
+ <item type="style" name="PermissionGrantPermissionRationaleMoreInfoIcon" />
+
<!-- END PERMISSION GRANT DIALOG -->
diff --git a/PermissionController/res/values/strings.xml b/PermissionController/res/values/strings.xml
index d16c095e7..b92cc4286 100644
--- a/PermissionController/res/values/strings.xml
+++ b/PermissionController/res/values/strings.xml
@@ -1713,4 +1713,13 @@ Allow <xliff:g id="app_name" example="Gmail">%4$s</xliff:g> to upload a bug repo
<!-- Summary for toggle controlling whether to show the first letter while typing passwords. [CHAR LIMIT=NONE] -->
<string name="show_password_summary">Display characters briefly as you type</string>
+ <!-- TODO(b/259279178): update with finalized permission rationale strings -->
+ <!-- Template for the permission rationale message when an app requests a permission. Third
+ parties are other organizations outside of the app developer. These could be companies or even
+ governmental organizations. But because we aren't able to be inclusive of all possibilities,
+ phrasing should be as generic as possible while still helping users understand they aren't just
+ sharing data with the developer company. [CHAR LIMIT=100] -->
+ <string name="permission_rationale_message_template">This app stated it may share
+ <xliff:g id="permission_name" example="location">%s</xliff:g> data with third parties</string>
+
</resources>
diff --git a/PermissionController/res/values/styles.xml b/PermissionController/res/values/styles.xml
index 42f4cc76f..60f39874b 100644
--- a/PermissionController/res/values/styles.xml
+++ b/PermissionController/res/values/styles.xml
@@ -183,6 +183,44 @@
<item name="android:background">?android:attr/selectableItemBackground</item>
</style>
+ <style name="PermissionGrantPermissionRationaleContent">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_marginStart">24dp</item>
+ <item name="android:layout_marginEnd">24dp</item>
+ <item name="android:layout_marginBottom">24dp</item>
+ <item name="android:padding">12dp</item>
+ <item name="android:orientation">horizontal</item>
+ <item name="android:background">@drawable/grant_dialog_permission_rationale_background</item>
+ </style>
+
+ <style name="PermissionGrantPermissionRationaleIcon">
+ <item name="android:layout_width">20dp</item>
+ <item name="android:layout_height">20dp</item>
+ <item name="android:layout_gravity">start|center_vertical</item>
+ <item name="android:scaleType">fitCenter</item>
+ <item name="android:tint">?android:attr/textColorSecondary</item>
+ </style>
+
+ <style name="PermissionGrantPermissionRationaleMessage"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:layout_width">0dp</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_weight">1</item>
+ <item name="android:layout_marginStart">12dp</item>
+ <item name="android:layout_marginEnd">12dp</item>
+ <item name="android:textSize">14sp</item>
+ </style>
+
+ <style name="PermissionGrantPermissionRationaleMoreInfoIcon">
+ <item name="android:layout_width">20dp</item>
+ <item name="android:layout_height">20dp</item>
+ <item name="android:layout_gravity">end|center_vertical</item>
+ <item name="android:scaleType">fitCenter</item>
+ <item name="android:tint">?android:attr/textColorSecondary</item>
+ </style>
+
<!-- for use in overlays -->
<style name="PermissionGrantButtonAllow"
parent="@style/PermissionGrantButton"></style>
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/SafetyLabelLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/SafetyLabelLiveData.kt
new file mode 100644
index 000000000..48909384f
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/SafetyLabelLiveData.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2022 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.data
+
+import android.app.Application
+import android.content.pm.PackageManager
+import android.os.PersistableBundle
+import android.util.Log
+import com.android.permission.safetylabel.DataCategoryConstants
+import com.android.permission.safetylabel.DataLabelConstants
+import com.android.permission.safetylabel.DataTypeConstants
+import com.android.permission.safetylabel.SafetyLabel
+import com.android.permissioncontroller.PermissionControllerApplication
+import com.android.permissioncontroller.permission.utils.KotlinUtils.isPermissionRationaleEnabled
+import com.android.permissioncontroller.permission.utils.KotlinUtils.isPlaceholderSafetyLabelDataEnabled
+import kotlinx.coroutines.Job
+
+/**
+ * SafetyLabel LiveData for the specified package
+ *
+ * @param app current Application
+ * @param packageName name of the package to get SafetyLabel information for
+ */
+class SafetyLabelLiveData
+private constructor(private val app: Application, private val packageName: String) :
+ SmartAsyncMediatorLiveData<SafetyLabel>() {
+
+ override suspend fun loadDataAndPostValue(job: Job) {
+ if (job.isCancelled) {
+ return
+ }
+
+ if (!isPermissionRationaleEnabled()) {
+ postValue(null)
+ return
+ }
+
+ if (packageName.isEmpty()) {
+ postValue(null)
+ return
+ }
+
+ val safetyLabel: SafetyLabel? =
+ try {
+ val metadataBundle: PersistableBundle? = getInstallMetadataBundle()
+ SafetyLabel.getSafetyLabelFromMetadata(metadataBundle)
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.w(LOG_TAG, "SafetyLabel for $packageName not found")
+ invalidateSingle(packageName)
+ null
+ }
+ postValue(safetyLabel)
+ }
+
+ // TODO(b/257293222): Update when hooking up PackageManager APIs
+ private fun getInstallMetadataBundle(): PersistableBundle? {
+ return if (isPlaceholderSafetyLabelDataEnabled()) {
+ placeholderMetadataBundle()
+ } else {
+ null
+ }
+ }
+
+ // TODO(b/257293222): Remove when hooking up PackageManager APIs
+ private fun placeholderMetadataBundle(): PersistableBundle {
+ val approximateLocationBundle = PersistableBundle().apply {
+ putIntArray(
+ "purposes",
+ (1..7).toList().toIntArray())
+ }
+
+ val locationBundle = PersistableBundle().apply {
+ putPersistableBundle(
+ DataTypeConstants.LOCATION_APPROX_LOCATION,
+ approximateLocationBundle)
+ }
+
+ val dataSharedBundle = PersistableBundle().apply {
+ putPersistableBundle(DataCategoryConstants.CATEGORY_LOCATION, locationBundle)
+ }
+
+ val dataLabelBundle = PersistableBundle().apply {
+ putPersistableBundle(DataLabelConstants.DATA_USAGE_SHARED, dataSharedBundle)
+ }
+
+ val safetyLabelBundle = PersistableBundle().apply {
+ putPersistableBundle("data_labels", dataLabelBundle)
+ }
+
+ return PersistableBundle().apply {
+ putPersistableBundle("safety_labels", safetyLabelBundle)
+ }
+ }
+
+ companion object : DataRepositoryForPackage<String, SafetyLabelLiveData>() {
+ private val LOG_TAG = SafetyLabelLiveData::class.java.simpleName
+
+ override fun newValue(key: String): SafetyLabelLiveData {
+ return SafetyLabelLiveData(PermissionControllerApplication.get(), key)
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
index 35501aaed..37acc9ef0 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
@@ -41,6 +41,7 @@ import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.drawable.Icon;
+import android.icu.lang.UCharacter;
import android.os.Bundle;
import android.os.Process;
import android.text.Annotation;
@@ -487,6 +488,16 @@ public class GrantPermissionsActivity extends SettingsActivity
setTitle(message);
}
+ CharSequence permissionRationaleMessage = null;
+ if (info.getShowPermissionRationale()) {
+ String permissionGroupLabel =
+ KotlinUtils.INSTANCE.getPermGroupLabel(this, info.getGroupName())
+ .toString();
+
+ permissionRationaleMessage = getString(R.string.permission_rationale_message_template,
+ UCharacter.toLowerCase(permissionGroupLabel));
+ }
+
ArrayList<Integer> idxs = new ArrayList<>();
mButtonVisibilities = new boolean[info.getButtonVisibilities().size()];
for (int i = 0; i < info.getButtonVisibilities().size(); i++) {
@@ -502,7 +513,8 @@ public class GrantPermissionsActivity extends SettingsActivity
}
mViewHandler.updateUi(info.getGroupName(), mTotalRequests, mCurrentRequestIdx, icon,
- message, detailMessage, mButtonVisibilities, mLocationVisibilities);
+ message, detailMessage, permissionRationaleMessage, mButtonVisibilities,
+ mLocationVisibilities);
if (showingNewGroup) {
mCurrentRequestIdx++;
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsViewHandler.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsViewHandler.java
index 78caebc21..03f6d604c 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsViewHandler.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsViewHandler.java
@@ -85,11 +85,15 @@ public interface GrantPermissionsViewHandler {
* @param message the message to display the user
* @param detailMessage another message to display to the user. This clarifies "message" in more
* detail
+ * @param permissionRationaleMessage another message to display to the user. This message lets
+ * users know developer stated data usages for the requested
+ * permission
* @param buttonVisibilities visibilities for each button
* @param locationVisibilities visibilities for location options
*/
void updateUi(String groupName, int groupCount, int groupIndex, Icon icon,
- CharSequence message, CharSequence detailMessage, boolean[] buttonVisibilities,
+ CharSequence message, CharSequence detailMessage,
+ CharSequence permissionRationaleMessage, boolean[] buttonVisibilities,
boolean[] locationVisibilities);
/**
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
index 194faff6f..6b09921cb 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
@@ -91,8 +91,11 @@ public class GrantPermissionsAutoViewHandler implements GrantPermissionsViewHand
@Override
public void updateUi(String groupName, int groupCount, int groupIndex, Icon icon,
- CharSequence message, CharSequence detailMessage, boolean[] buttonVisibilities,
+ CharSequence message, CharSequence detailMessage,
+ CharSequence permissionRationaleMessage, boolean[] buttonVisibilities,
boolean[] locationVisibilities) {
+ // permissionRationaleMessage ignored by auto
+
mGroupName = groupName;
mGroupCount = groupCount;
mGroupIndex = groupIndex;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt
index af6d922ba..e39a8fbad 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt
@@ -95,6 +95,7 @@ class GrantPermissionsViewHandlerImpl(
private var groupIcon: Icon? = null
private var groupMessage: CharSequence? = null
private var detailMessage: CharSequence? = null
+ private var permissionRationaleMessage: CharSequence? = null
private val buttonVisibilities = BooleanArray(NEXT_BUTTON) { false }
private val locationVisibilities = BooleanArray(NEXT_LOCATION_DIALOG) { false }
private var selectedPrecision: Int = 0
@@ -110,6 +111,8 @@ class GrantPermissionsViewHandlerImpl(
private var iconView: ImageView? = null
private var messageView: TextView? = null
private var detailMessageView: TextView? = null
+ private var permissionRationaleView: View? = null
+ private var permissionRationaleMessageView: TextView? = null
private var buttons: Array<Button?> = arrayOfNulls(NEXT_BUTTON)
private var locationViews: Array<View?> = arrayOfNulls(NEXT_LOCATION_DIALOG)
private var rootView: ViewGroup? = null
@@ -121,6 +124,8 @@ class GrantPermissionsViewHandlerImpl(
arguments.putParcelable(ARG_GROUP_ICON, groupIcon)
arguments.putCharSequence(ARG_GROUP_MESSAGE, groupMessage)
arguments.putCharSequence(ARG_GROUP_DETAIL_MESSAGE, detailMessage)
+ arguments.putCharSequence(ARG_GROUP_PERMISSION_RATIONALE_MESSAGE,
+ permissionRationaleMessage)
arguments.putBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES, buttonVisibilities)
arguments.putBooleanArray(ARG_DIALOG_LOCATION_VISIBILITIES, locationVisibilities)
arguments.putInt(ARG_DIALOG_SELECTED_PRECISION, selectedPrecision)
@@ -133,6 +138,8 @@ class GrantPermissionsViewHandlerImpl(
groupCount = savedInstanceState.getInt(ARG_GROUP_COUNT)
groupIndex = savedInstanceState.getInt(ARG_GROUP_INDEX)
detailMessage = savedInstanceState.getCharSequence(ARG_GROUP_DETAIL_MESSAGE)
+ permissionRationaleMessage =
+ savedInstanceState.getCharSequence(ARG_GROUP_PERMISSION_RATIONALE_MESSAGE)
setButtonVisibilities(savedInstanceState.getBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES))
setLocationVisibilities(savedInstanceState.getBooleanArray(
ARG_DIALOG_LOCATION_VISIBILITIES))
@@ -142,14 +149,15 @@ class GrantPermissionsViewHandlerImpl(
}
override fun updateUi(
- groupName: String,
+ groupName: String?,
groupCount: Int,
groupIndex: Int,
icon: Icon?,
message: CharSequence?,
detailMessage: CharSequence?,
- buttonVisibilities: BooleanArray,
- locationVisibilities: BooleanArray
+ permissionRationaleMessage: CharSequence?,
+ buttonVisibilities: BooleanArray?,
+ locationVisibilities: BooleanArray?
) {
this.groupName = groupName
@@ -158,6 +166,7 @@ class GrantPermissionsViewHandlerImpl(
groupIcon = icon
groupMessage = message
this.detailMessage = detailMessage
+ this.permissionRationaleMessage = permissionRationaleMessage
setButtonVisibilities(buttonVisibilities)
setLocationVisibilities(locationVisibilities)
@@ -170,6 +179,7 @@ class GrantPermissionsViewHandlerImpl(
private fun updateAll() {
updateDescription()
updateDetailDescription()
+ updatePermissionRationale()
updateButtons()
updateLocationVisibilities()
@@ -212,6 +222,10 @@ class GrantPermissionsViewHandlerImpl(
detailMessageView!!.movementMethod = LinkMovementMethod.getInstance()
iconView = rootView.findViewById(R.id.permission_icon)
+ permissionRationaleView = rootView.findViewById(R.id.permission_rationale_container)
+ permissionRationaleMessageView = rootView.findViewById(R.id.permission_rationale_message)
+ permissionRationaleView!!.setOnClickListener(this)
+
val buttons = arrayOfNulls<Button>(NEXT_BUTTON)
val numButtons = BUTTON_RES_ID_TO_NUM.size()
for (i in 0 until numButtons) {
@@ -305,6 +319,16 @@ class GrantPermissionsViewHandlerImpl(
}
}
+ private fun updatePermissionRationale() {
+ val message = permissionRationaleMessage
+ if (message == null || message.isEmpty()) {
+ permissionRationaleView!!.visibility = View.GONE
+ } else {
+ permissionRationaleMessageView!!.text = message
+ permissionRationaleView!!.visibility = View.VISIBLE
+ }
+ }
+
private fun updateButtons() {
for (i in 0 until BUTTON_RES_ID_TO_NUM.size()) {
val pos = BUTTON_RES_ID_TO_NUM.valueAt(i)
@@ -412,6 +436,11 @@ class GrantPermissionsViewHandlerImpl(
override fun onClick(view: View) {
val id = view.id
+ if (id == R.id.permission_rationale_container) {
+ // TODO(b/256913489): Implement Permission rationale details activity
+ return
+ }
+
if (id == R.id.permission_location_accuracy_radio_fine) {
(locationViews[FINE_RADIO_BUTTON] as RadioButton).isChecked = true
selectedPrecision = FINE_RADIO_BUTTON
@@ -515,6 +544,7 @@ class GrantPermissionsViewHandlerImpl(
}
companion object {
+ private val TAG = GrantPermissionsViewHandlerImpl::class.java.simpleName
const val ARG_GROUP_NAME = "ARG_GROUP_NAME"
const val ARG_GROUP_COUNT = "ARG_GROUP_COUNT"
@@ -522,6 +552,8 @@ class GrantPermissionsViewHandlerImpl(
const val ARG_GROUP_ICON = "ARG_GROUP_ICON"
const val ARG_GROUP_MESSAGE = "ARG_GROUP_MESSAGE"
private const val ARG_GROUP_DETAIL_MESSAGE = "ARG_GROUP_DETAIL_MESSAGE"
+ private const val ARG_GROUP_PERMISSION_RATIONALE_MESSAGE =
+ "ARG_GROUP_PERMISSION_RATIONALE_MESSAGE"
private const val ARG_DIALOG_BUTTON_VISIBILITIES = "ARG_DIALOG_BUTTON_VISIBILITIES"
private const val ARG_DIALOG_LOCATION_VISIBILITIES = "ARG_DIALOG_LOCATION_VISIBILITIES"
private const val ARG_DIALOG_SELECTED_PRECISION = "ARG_DIALOG_SELECTED_PRECISION"
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
index ecfea63d3..c6c5cef8e 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
@@ -46,6 +46,10 @@ import androidx.core.util.Consumer
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.android.modules.utils.build.SdkLevel
+import com.android.permission.safetylabel.DataCategory
+import com.android.permission.safetylabel.DataType
+import com.android.permission.safetylabel.DataTypeConstants
+import com.android.permission.safetylabel.SafetyLabel
import com.android.permissioncontroller.Constants
import com.android.permissioncontroller.DeviceUtils
import com.android.permissioncontroller.PermissionControllerApplication
@@ -70,6 +74,7 @@ import com.android.permissioncontroller.auto.DrivingDecisionReminderService
import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData
import com.android.permissioncontroller.permission.data.LightPackageInfoLiveData
import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData
+import com.android.permissioncontroller.permission.data.SafetyLabelLiveData
import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
import com.android.permissioncontroller.permission.data.get
import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
@@ -116,8 +121,12 @@ import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.
import com.android.permissioncontroller.permission.utils.AdminRestrictedPermissionsUtils
import com.android.permissioncontroller.permission.utils.KotlinUtils
import com.android.permissioncontroller.permission.utils.KotlinUtils.getDefaultPrecision
+import com.android.permissioncontroller.permission.utils.KotlinUtils.grantBackgroundRuntimePermissions
+import com.android.permissioncontroller.permission.utils.KotlinUtils.grantForegroundRuntimePermissions
import com.android.permissioncontroller.permission.utils.KotlinUtils.isLocationAccuracyEnabled
+import com.android.permissioncontroller.permission.utils.KotlinUtils.isPermissionRationaleEnabled
import com.android.permissioncontroller.permission.utils.PermissionMapping
+import com.android.permissioncontroller.permission.utils.SafetyLabelPermissionMapping
import com.android.permissioncontroller.permission.utils.SafetyNetLogger
import com.android.permissioncontroller.permission.utils.Utils
@@ -142,6 +151,7 @@ class GrantPermissionsViewModel(
private val LOG_TAG = GrantPermissionsViewModel::class.java.simpleName
private val user = Process.myUserHandle()
private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user]
+ private val safetyLabelLiveData = SafetyLabelLiveData[packageName]
private val dpm = app.getSystemService(DevicePolicyManager::class.java)!!
private val permissionPolicy = dpm.getPermissionPolicy(null)
private val permGroupsToSkip = mutableListOf<String>()
@@ -154,6 +164,7 @@ class GrantPermissionsViewModel(
}
private lateinit var packageInfo: LightPackageInfo
+ private var safetyLabel: SafetyLabel? = null
// All permissions that could possibly be affected by the provided requested permissions, before
// filtering system fixed, auto grant, etc.
@@ -174,7 +185,8 @@ class GrantPermissionsViewModel(
val message: RequestMessage = RequestMessage.FG_MESSAGE,
val detailMessage: RequestMessage = RequestMessage.NO_MESSAGE,
val sendToSettingsImmediately: Boolean = false,
- val openPhotoPicker: Boolean = false
+ val openPhotoPicker: Boolean = false,
+ val showPermissionRationale: Boolean = false
) {
val groupName = groupInfo.name
}
@@ -192,12 +204,16 @@ class GrantPermissionsViewModel(
init {
addSource(packagePermissionsLiveData) { onPackageLoaded() }
addSource(packageInfoLiveData) { onPackageLoaded() }
+ addSource(safetyLabelLiveData) { onPackageLoaded() }
+
// Load package state, if available
onPackageLoaded()
}
private fun onPackageLoaded() {
- if (packageInfoLiveData.isStale || packagePermissionsLiveData.isStale) {
+ if (packageInfoLiveData.isStale ||
+ packagePermissionsLiveData.isStale ||
+ safetyLabelLiveData.isStale) {
return
}
@@ -218,6 +234,8 @@ class GrantPermissionsViewModel(
return
}
+ safetyLabel = safetyLabelLiveData.value
+
val allAffectedPermissions = requestedPermissions.toMutableSet()
for (requestedPerm in requestedPermissions) {
allAffectedPermissions.addAll(computeAffectedPermissions(requestedPerm, groups))
@@ -553,7 +571,9 @@ class GrantPermissionsViewModel(
buttonVisibilities,
locationVisibilities,
message,
- detailMessage))
+ detailMessage,
+ showPermissionRationale = shouldShowPermissionRationale(
+ safetyLabel, groupState)))
}
sortPermissionGroups(requestInfos)
@@ -585,6 +605,41 @@ class GrantPermissionsViewModel(
}
}
+ private fun shouldShowPermissionRationale(
+ safetyLabel: SafetyLabel?,
+ groupState: GroupState
+ ): Boolean {
+ if (!isPermissionRationaleEnabled() ||
+ safetyLabel == null ||
+ safetyLabel.dataLabel.dataShared.isEmpty()) {
+ return false
+ }
+
+ val groupName = groupState.group.permGroupName
+ val categoriesForPermission: List<String> =
+ SafetyLabelPermissionMapping.getCategoriesForPermissionGroup(groupName)
+ categoriesForPermission.forEach categoryLoop@ { category ->
+ val dataCategory: DataCategory? = safetyLabel.dataLabel.dataShared[category]
+ if (dataCategory == null) {
+ // Continue to next
+ return@categoryLoop
+ }
+ val typesForCategory = DataTypeConstants.getValidDataTypesForCategory(category)
+ typesForCategory.forEach typeLoop@ { type ->
+ val dataType: DataType? = dataCategory.dataTypes[type]
+ if (dataType == null) {
+ // Continue to next
+ return@typeLoop
+ }
+ if (dataType.purposeSet.isNotEmpty()) {
+ return true
+ }
+ }
+ }
+
+ return false
+ }
+
/**
* Converts a list of LightAppPermGroups into a list of GroupStates
*/
@@ -769,10 +824,9 @@ class GrantPermissionsViewModel(
!isSplitGroupLowerGrant(group)) {
if (group.permissions[perm]?.isGrantedIncludingAppOp == false) {
if (isBackground) {
- KotlinUtils.grantBackgroundRuntimePermissions(app, group, listOf(perm))
+ grantBackgroundRuntimePermissions(app, group, listOf(perm))
} else {
- KotlinUtils.grantForegroundRuntimePermissions(app, group, listOf(perm),
- group.isOneTime)
+ grantForegroundRuntimePermissions(app, group, listOf(perm), group.isOneTime)
}
KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_USER_SET to false,
FLAG_PERMISSION_USER_FIXED to false, filterPermissions = listOf(perm))
@@ -830,9 +884,9 @@ class GrantPermissionsViewModel(
if (AdminRestrictedPermissionsUtils.mayAdminGrantPermission(
app, perm, user.identifier)) {
if (isBackground) {
- KotlinUtils.grantBackgroundRuntimePermissions(app, group, listOf(perm))
+ grantBackgroundRuntimePermissions(app, group, listOf(perm))
} else {
- KotlinUtils.grantForegroundRuntimePermissions(app, group, listOf(perm))
+ grantForegroundRuntimePermissions(app, group, listOf(perm))
}
KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_POLICY_FIXED to true,
FLAG_PERMISSION_USER_SET to false, FLAG_PERMISSION_USER_FIXED to false,
@@ -1033,11 +1087,11 @@ class GrantPermissionsViewModel(
PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED
}
if (groupState.isBackground) {
- KotlinUtils.grantBackgroundRuntimePermissions(app, groupState.group,
+ grantBackgroundRuntimePermissions(app, groupState.group,
groupState.affectedPermissions)
} else {
if (affectedForegroundPermissions == null) {
- KotlinUtils.grantForegroundRuntimePermissions(app, groupState.group,
+ grantForegroundRuntimePermissions(app, groupState.group,
groupState.affectedPermissions, isOneTime)
// This prevents weird flag state when app targetSDK switches from S+ to R-
if (groupState.affectedPermissions.contains(ACCESS_FINE_LOCATION)) {
@@ -1045,7 +1099,7 @@ class GrantPermissionsViewModel(
app, groupState.group, true)
}
} else {
- val newGroup = KotlinUtils.grantForegroundRuntimePermissions(app,
+ val newGroup = grantForegroundRuntimePermissions(app,
groupState.group, affectedForegroundPermissions, isOneTime)
if (!isOneTime || newGroup.isOneTime) {
KotlinUtils.setFlagsWhenLocationAccuracyChanged(app, newGroup,
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java
index 3c7a90b5b..45dd4c1f2 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java
@@ -146,8 +146,11 @@ public final class GrantPermissionsViewHandlerImpl implements GrantPermissionsVi
@Override
public void updateUi(String groupName, int groupCount, int groupIndex, Icon icon,
- CharSequence message, CharSequence detailMessage, boolean[] buttonVisibilities,
+ CharSequence message, CharSequence detailMessage,
+ CharSequence permissionRationaleMessage, boolean[] buttonVisibilities,
boolean[] locationVisibilities) {
+ // permissionRationaleMessage ignored by television
+
// TODO: Handle detailMessage
mGroupName = groupName;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java
index 1c0af63cc..ccd1ed42e 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java
@@ -96,8 +96,11 @@ public final class GrantPermissionsWearViewHandler implements GrantPermissionsVi
@Override
public void updateUi(String groupName, int groupCount, int groupIndex, Icon icon,
- CharSequence message, CharSequence detailMessage, boolean[] buttonVisibilities,
+ CharSequence message, CharSequence detailMessage,
+ CharSequence permissionRationaleMessage, boolean[] buttonVisibilities,
boolean[] locationVisibilities) {
+ // permissionRationaleMessage ignored by wear
+
// TODO: Handle detailMessage
boolean showDoNotAsk = buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON];
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt
index 6bf2ac7b6..f6693a7eb 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt
@@ -138,6 +138,13 @@ object KotlinUtils {
/** Whether to show 7-day toggle in privacy hub. */
private const val PRIVACY_DASHBOARD_7_DAY_TOGGLE = "privacy_dashboard_7_day_toggle"
+ /** Whether to show permission rationale in permission settings and grant dialog. */
+ private const val PRIVACY_PERMISSION_RATIONALE_ENABLED = "privacy_permission_rationale_enabled"
+
+ /** Whether to placeholder safety label data in permission settings and grant dialog. */
+ private const val PRIVACY_PLACEHOLDER_SAFETY_LABEL_DATA_ENABLED =
+ "privacy_placeholder_safety_label_data_enabled"
+
/** Default location precision */
private const val PROPERTY_LOCATION_PRECISION = "location_precision"
@@ -247,6 +254,27 @@ object KotlinUtils {
PROPERTY_PHOTO_PICKER_PROMPT_ENABLED, false)
}
+ /*
+ * Whether we should enable the permission rationale in permission settings and grant dialog
+ *
+ * @return whether the flag is enabled
+ */
+ @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake")
+ fun isPermissionRationaleEnabled(): Boolean {
+ return SdkLevel.isAtLeastU() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ PRIVACY_PERMISSION_RATIONALE_ENABLED, false)
+ }
+
+ /**
+ * Whether we should use placeholder safety label data
+ *
+ * @return whether the flag is enabled
+ */
+ fun isPlaceholderSafetyLabelDataEnabled(): Boolean {
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ PRIVACY_PLACEHOLDER_SAFETY_LABEL_DATA_ENABLED, false)
+ }
+
/**
* Given a Map, and a List, determines which elements are in the list, but not the map, and
* vice versa. Used primarily for determining which liveDatas are already being watched, and
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/SafetyLabelPermissionMapping.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/SafetyLabelPermissionMapping.kt
new file mode 100644
index 000000000..5fd852bb6
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/SafetyLabelPermissionMapping.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 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.utils
+
+import android.Manifest
+import com.android.permission.safetylabel.DataCategoryConstants
+
+/**
+ * This file contains the canonical mapping of permission and permission group to Safety Label
+ * categories and types used in the Permission settings screens and grant dialog. It also includes
+ * methods related to that mapping.
+ */
+object SafetyLabelPermissionMapping {
+
+ /**
+ * Get the Safety Label categories pertaining to a specified permission group.
+ *
+ * @param groupName the permission group name
+ *
+ * @return The categories or an empty list if the group does not have supported and mapped group
+ * to safety label category
+ */
+ fun getCategoriesForPermissionGroup(groupName: String): List<String> {
+ return if (groupName == Manifest.permission_group.LOCATION) {
+ listOf(DataCategoryConstants.CATEGORY_LOCATION)
+ } else {
+ emptyList()
+ }
+ }
+}
diff --git a/PermissionController/tests/mocking/Android.bp b/PermissionController/tests/mocking/Android.bp
index 2d4e6803e..e6074e335 100644
--- a/PermissionController/tests/mocking/Android.bp
+++ b/PermissionController/tests/mocking/Android.bp
@@ -108,6 +108,7 @@ android_test {
"modules-utils-build_system",
"safety-center-resources-lib",
"safety-center-internal-data",
+ "safety-label",
"lottie",
"androidx.test.rules",
diff --git a/SafetyLabel/java/com/android/permission/safetylabel/DataLabel.java b/SafetyLabel/java/com/android/permission/safetylabel/DataLabel.java
index 68af27470..564d5479f 100644
--- a/SafetyLabel/java/com/android/permission/safetylabel/DataLabel.java
+++ b/SafetyLabel/java/com/android/permission/safetylabel/DataLabel.java
@@ -32,7 +32,7 @@ import java.util.Map;
* {@link DataCategory}
*/
public class DataLabel {
- @VisibleForTesting public static final String KEY_DATA_LABEL = "data_labels";
+ @VisibleForTesting static final String KEY_DATA_LABEL = "data_labels";
private final Map<String, DataCategory> mDataCollected;
private final Map<String, DataCategory> mDataShared;
diff --git a/SafetyLabel/java/com/android/permission/safetylabel/DataType.java b/SafetyLabel/java/com/android/permission/safetylabel/DataType.java
index abd69ca6c..cc63b07ac 100644
--- a/SafetyLabel/java/com/android/permission/safetylabel/DataType.java
+++ b/SafetyLabel/java/com/android/permission/safetylabel/DataType.java
@@ -35,9 +35,9 @@ import java.util.Set;
* metadata related to the data usage purpose.
*/
public class DataType {
+ @VisibleForTesting static final String KEY_PURPOSES = "purposes";
@VisibleForTesting static final String KEY_USER_CONTROL = "user_control";
@VisibleForTesting static final String KEY_EPHEMERAL = "ephemeral";
- @VisibleForTesting static final String KEY_PURPOSES = "purposes";
@Purpose private final Set<Integer> mPurposeSet;
private final Boolean mUserControl;
diff --git a/SafetyLabel/java/com/android/permission/safetylabel/SafetyLabel.java b/SafetyLabel/java/com/android/permission/safetylabel/SafetyLabel.java
index a91ff901f..214b2db54 100644
--- a/SafetyLabel/java/com/android/permission/safetylabel/SafetyLabel.java
+++ b/SafetyLabel/java/com/android/permission/safetylabel/SafetyLabel.java
@@ -24,7 +24,7 @@ import androidx.annotation.VisibleForTesting;
/** Safety Label representation containing zero or more {@link DataCategory} for data shared */
public class SafetyLabel {
- @VisibleForTesting public static final String KEY_SAFETY_LABEL = "safety_label";
+ @VisibleForTesting static final String KEY_SAFETY_LABEL = "safety_labels";
private final DataLabel mDataLabel;
private SafetyLabel(@NonNull DataLabel dataLabel) {