summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PermissionController/res/layout-v33/preference_issue_card.xml133
-rw-r--r--PermissionController/res/layout-v33/preference_more_issues_card.xml11
-rw-r--r--PermissionController/res/layout-v33/preference_safety_status.xml11
-rw-r--r--PermissionController/res/values-night-v33/themes.xml2
-rw-r--r--PermissionController/res/values-v33/attrs.xml5
-rw-r--r--PermissionController/res/values-v33/styles.xml4
-rw-r--r--PermissionController/res/values-v33/themes.xml12
-rw-r--r--PermissionController/res/xml/roles.xml4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ClickableDisabledSwitchPreference.java4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java32
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardPreference.kt14
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt5
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java29
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java7
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt20
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsActivity.java8
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt50
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyIllustrationPreference.kt3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java7
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt3
-rw-r--r--tests/cts/permissionpolicy/res/raw/android_manifest.xml9
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt10
-rw-r--r--tests/cts/rolemultiuser/Android.bp47
-rw-r--r--tests/cts/rolemultiuser/AndroidManifest.xml32
-rw-r--r--tests/cts/rolemultiuser/AndroidTest.xml48
-rw-r--r--tests/cts/rolemultiuser/OWNERS3
-rw-r--r--tests/cts/rolemultiuser/TEST_MAPPING12
-rw-r--r--tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt227
28 files changed, 609 insertions, 143 deletions
diff --git a/PermissionController/res/layout-v33/preference_issue_card.xml b/PermissionController/res/layout-v33/preference_issue_card.xml
index e6d749142..107c778a1 100644
--- a/PermissionController/res/layout-v33/preference_issue_card.xml
+++ b/PermissionController/res/layout-v33/preference_issue_card.xml
@@ -13,81 +13,84 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<androidx.constraintlayout.widget.ConstraintLayout
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/issue_card"
- android:clickable="false"
- android:screenReaderFocusable="true"
- style="@style/SafetyCenterCard.Issue">
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/issue_card"
+ android:clickable="false"
+ android:screenReaderFocusable="true"
+ style="@style/SafetyCenterCard.Issue">
- <ImageButton
- android:id="@+id/issue_card_dismiss_btn"
- android:src="@drawable/ic_safety_issue_dismiss"
- android:contentDescription="@string/safety_center_issue_card_dismiss_button"
- style="@style/SafetyCenterIssueDismiss" />
+ <ImageButton
+ android:id="@+id/issue_card_dismiss_btn"
+ android:src="@drawable/ic_safety_issue_dismiss"
+ android:contentDescription="@string/safety_center_issue_card_dismiss_button"
+ style="@style/SafetyCenterIssueDismiss" />
- <TextView
- android:id="@+id/issue_card_attribution_title"
- android:text="@string/summary_placeholder"
- android:screenReaderFocusable="false"
- style="@style/SafetyCenterIssueAttributionTitle" />
+ <TextView
+ android:id="@+id/issue_card_attribution_title"
+ android:text="@string/summary_placeholder"
+ android:screenReaderFocusable="false"
+ style="@style/SafetyCenterIssueAttributionTitle" />
- <TextView
- android:id="@+id/issue_card_title"
- android:text="@string/summary_placeholder"
- android:screenReaderFocusable="false"
- style="@style/SafetyCenterIssueTitle" />
+ <TextView
+ android:id="@+id/issue_card_title"
+ android:text="@string/summary_placeholder"
+ android:screenReaderFocusable="false"
+ style="@style/SafetyCenterIssueTitle" />
- <TextView
- android:id="@+id/issue_card_subtitle"
- android:text="@string/summary_placeholder"
- android:screenReaderFocusable="false"
- style="@style/SafetyCenterIssueSubtitle" />
+ <TextView
+ android:id="@+id/issue_card_subtitle"
+ android:text="@string/summary_placeholder"
+ android:screenReaderFocusable="false"
+ style="@style/SafetyCenterIssueSubtitle" />
- <TextView
- android:id="@+id/issue_card_summary"
- android:text="@string/summary_placeholder"
- android:screenReaderFocusable="false"
- style="@style/SafetyCenterIssueSummary" />
+ <TextView
+ android:id="@+id/issue_card_summary"
+ android:text="@string/summary_placeholder"
+ android:screenReaderFocusable="false"
+ style="@style/SafetyCenterIssueSummary" />
- <include
- android:id="@+id/issue_card_action_button_list"
- layout="?attr/scActionButtonListLayout"/>
+ <include
+ android:id="@+id/issue_card_action_button_list"
+ layout="?attr/scActionButtonListLayout"/>
- <com.android.permissioncontroller.permission.ui.v33.widget.SafetyProtectionSectionView
- android:id="@+id/issue_card_protected_by_android"
- android:importantForAccessibility="no"
- style="@style/SafetyCenterIssueSafetyProtectionSection" />
+ <com.android.permissioncontroller.permission.ui.v33.widget.SafetyProtectionSectionView
+ android:id="@+id/issue_card_protected_by_android"
+ android:importantForAccessibility="no"
+ style="@style/SafetyCenterIssueSafetyProtectionSection" />
- <ImageView
- android:id="@+id/resolved_issue_image"
- android:src="@drawable/safety_center_issue_resolved_avd"
- android:importantForAccessibility="no"
- style="@style/SafetyCenterIssueCardResolvedImage" />
+ <ImageView
+ android:id="@+id/resolved_issue_image"
+ android:src="@drawable/safety_center_issue_resolved_avd"
+ android:importantForAccessibility="no"
+ style="@style/SafetyCenterIssueCardResolvedImage" />
- <TextView
- android:id="@+id/resolved_issue_text"
- android:text="@string/safety_center_resolved_issue_fallback"
- style="@style/SafetyCenterIssueCardResolvedTitle" />
+ <TextView
+ android:id="@+id/resolved_issue_text"
+ android:text="@string/safety_center_resolved_issue_fallback"
+ style="@style/SafetyCenterIssueCardResolvedTitle" />
- <!-- This group doesn't contain issue_card_attribution_title, issue_card_dismiss_btn,
- issue_card_subtitle or issue_card_protected_by_android since the version of
- ConstraintLayout we're using doesn't allow us to override the group's visibility on
- individual group members. See b/242705351 for context. -->
- <androidx.constraintlayout.widget.Group
- android:id="@+id/default_issue_content"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="visible"
- app:constraint_referenced_ids="issue_card_title,issue_card_summary,issue_card_action_button_list" />
+ <!-- This group doesn't contain issue_card_attribution_title, issue_card_dismiss_btn,
+ issue_card_subtitle or issue_card_protected_by_android since the version of
+ ConstraintLayout we're using doesn't allow us to override the group's visibility on
+ individual group members. See b/242705351 for context. -->
+ <androidx.constraintlayout.widget.Group
+ android:id="@+id/default_issue_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="visible"
+ app:constraint_referenced_ids="issue_card_title,issue_card_summary,issue_card_action_button_list" />
- <androidx.constraintlayout.widget.Group
- android:id="@+id/resolved_issue_content"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- app:constraint_referenced_ids="resolved_issue_image,resolved_issue_text" />
+ <androidx.constraintlayout.widget.Group
+ android:id="@+id/resolved_issue_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ app:constraint_referenced_ids="resolved_issue_image,resolved_issue_text" />
-</androidx.constraintlayout.widget.ConstraintLayout>
+ </androidx.constraintlayout.widget.ConstraintLayout>
+</FrameLayout> \ No newline at end of file
diff --git a/PermissionController/res/layout-v33/preference_more_issues_card.xml b/PermissionController/res/layout-v33/preference_more_issues_card.xml
index c93125762..d0ac6b1f5 100644
--- a/PermissionController/res/layout-v33/preference_more_issues_card.xml
+++ b/PermissionController/res/layout-v33/preference_more_issues_card.xml
@@ -13,8 +13,11 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<com.android.permissioncontroller.safetycenter.ui.view.MoreIssuesHeaderView
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/issue_card"
- style="@style/SafetyCenterMoreIssuesCollapsed"/>
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <com.android.permissioncontroller.safetycenter.ui.view.MoreIssuesHeaderView
+ android:id="@+id/more_issues_card"
+ style="@style/SafetyCenterMoreIssuesCollapsed"/>
+</FrameLayout> \ No newline at end of file
diff --git a/PermissionController/res/layout-v33/preference_safety_status.xml b/PermissionController/res/layout-v33/preference_safety_status.xml
index 42bf1c22d..17adc035f 100644
--- a/PermissionController/res/layout-v33/preference_safety_status.xml
+++ b/PermissionController/res/layout-v33/preference_safety_status.xml
@@ -14,7 +14,12 @@
~ limitations under the License.
-->
-<com.android.permissioncontroller.safetycenter.ui.view.StatusCardView
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:clickable="false"
- style="@style/SafetyCenterCard.Status" /> \ No newline at end of file
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <com.android.permissioncontroller.safetycenter.ui.view.StatusCardView
+ android:id="@+id/status_card"
+ android:clickable="false"
+ style="@style/SafetyCenterCard.Status" />
+</FrameLayout> \ No newline at end of file
diff --git a/PermissionController/res/values-night-v33/themes.xml b/PermissionController/res/values-night-v33/themes.xml
index 9b6f638a6..31940ede5 100644
--- a/PermissionController/res/values-night-v33/themes.xml
+++ b/PermissionController/res/values-night-v33/themes.xml
@@ -52,6 +52,8 @@
@style/SecondarySafetyCenterActionButton.Responsive
</item>
+ <item name="scCardSideMargin">@dimen/sc_spacing_large</item>
+
<item name="textColorScActionButton">@color/sc_primary_action_button_text</item>
<item name="textColorScSecondaryActionButton">?android:attr/textColorPrimary</item>
diff --git a/PermissionController/res/values-v33/attrs.xml b/PermissionController/res/values-v33/attrs.xml
index 4a417076d..b136ab718 100644
--- a/PermissionController/res/values-v33/attrs.xml
+++ b/PermissionController/res/values-v33/attrs.xml
@@ -39,8 +39,9 @@
<attr name="scStatusButtonStyle" format="reference" />
<attr name="scActionButtonListLayout" format="reference" />
<attr name="scActionButtonTheme" format="reference" />
- <attr name="scActionButtonStyle" format="reference"/>
- <attr name="scSecondaryActionButtonStyle" format="reference"/>
+ <attr name="scActionButtonStyle" format="reference" />
+ <attr name="scSecondaryActionButtonStyle" format="reference" />
+ <attr name="scCardSideMargin" format="dimension" />
<attr name="colorScShieldAccent" format="color" />
</resources> \ No newline at end of file
diff --git a/PermissionController/res/values-v33/styles.xml b/PermissionController/res/values-v33/styles.xml
index 94344615b..7e959c2f4 100644
--- a/PermissionController/res/values-v33/styles.xml
+++ b/PermissionController/res/values-v33/styles.xml
@@ -317,8 +317,8 @@
<item name="android:paddingEnd">@dimen/sc_spacing_xxxlarge</item>
<item name="android:paddingTop">@dimen/sc_spacing_xxxlarge</item>
<item name="android:paddingBottom">@dimen/sc_card_margin_bottom</item>
- <item name="android:layout_marginStart">@dimen/sc_spacing_large</item>
- <item name="android:layout_marginEnd">@dimen/sc_spacing_large</item>
+ <item name="android:layout_marginStart">?attr/scCardSideMargin</item>
+ <item name="android:layout_marginEnd">?attr/scCardSideMargin</item>
<item name="android:background">@drawable/safety_center_card_background</item>
</style>
diff --git a/PermissionController/res/values-v33/themes.xml b/PermissionController/res/values-v33/themes.xml
index 82a8ef5b6..a3d1b16c3 100644
--- a/PermissionController/res/values-v33/themes.xml
+++ b/PermissionController/res/values-v33/themes.xml
@@ -57,6 +57,8 @@
@style/SecondarySafetyCenterActionButton.Fixed
</item>
+ <item name="scCardSideMargin">@dimen/sc_spacing_large</item>
+
<item name="textColorScActionButton">@color/sc_primary_action_button_text</item>
<item name="textColorScSecondaryActionButton">?android:attr/textColorPrimary</item>
@@ -69,6 +71,10 @@
<style name="Theme.SafetyCenterQs" parent="Theme.SafetyCenterQsBase" />
+ <style name="Theme.SafetyCenterQsExpressive" parent="Theme.SafetyCenterQs">
+ <item name="scCardSideMargin">0dp</item>
+ </style>
+
<style name="Theme.SafetyCenterBase" parent="Theme.PermissionController.Settings.FilterTouches">
<item name="colorSurface">@color/sc_surface_light</item>
<item name="colorSurfaceVariant">@color/sc_surface_variant_light</item>
@@ -104,6 +110,8 @@
@style/SecondarySafetyCenterActionButton.Responsive
</item>
+ <item name="scCardSideMargin">@dimen/sc_spacing_large</item>
+
<item name="textColorScActionButton">@color/sc_primary_action_button_text</item>
<item name="textColorScSecondaryActionButton">?android:attr/textColorPrimary</item>
@@ -115,4 +123,8 @@
</style>
<style name="Theme.SafetyCenter" parent="Theme.SafetyCenterBase" />
+
+ <style name="Theme.SafetyCenterExpressive" parent="Theme.SafetyCenter">
+ <item name="scCardSideMargin">0dp</item>
+ </style>
</resources> \ No newline at end of file
diff --git a/PermissionController/res/xml/roles.xml b/PermissionController/res/xml/roles.xml
index 2989d8f96..7b1f84a20 100644
--- a/PermissionController/res/xml/roles.xml
+++ b/PermissionController/res/xml/roles.xml
@@ -1238,7 +1238,9 @@
<permission-set name="virtual_device" />
<!-- For capturing audio from the app on the device. -->
<permission name="android.permission.RECORD_AUDIO" />
-
+ <permission
+ name="android.permission.ADD_MIRROR_DISPLAY"
+ featureFlag="android.companion.virtualdevice.flags.Flags.enableLimitedVdmRole" />
<!--TODO(b/201605314) For calling Telecom framework API for audio streaming-->
<!--<permission name="android.permission.PROVIDE_CALL_ENDPOINTS" />-->
</permissions>
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ClickableDisabledSwitchPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ClickableDisabledSwitchPreference.java
index 3ab4faa66..54cb86ff3 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ClickableDisabledSwitchPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ClickableDisabledSwitchPreference.java
@@ -26,7 +26,7 @@ import android.view.ViewGroup;
import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceViewHolder;
-import androidx.preference.SwitchPreference;
+import androidx.preference.SwitchPreferenceCompat;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.safetycenter.ui.model.PrivacyControlsViewModel;
@@ -38,7 +38,7 @@ import com.android.permissioncontroller.safetycenter.ui.model.PrivacyControlsVie
* method will not register any changes while it appears disabled.
*/
@RequiresApi(TIRAMISU)
-public class ClickableDisabledSwitchPreference extends SwitchPreference {
+public class ClickableDisabledSwitchPreference extends SwitchPreferenceCompat {
private boolean mAppearDisabled;
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java
index 7622270b9..88759797e 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java
@@ -52,6 +52,7 @@ import androidx.preference.PreferenceViewHolder;
import com.android.modules.utils.build.SdkLevel;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel;
+import com.android.settingslib.widget.GroupSectionDividerMixin;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.shape.AbsoluteCornerSize;
@@ -62,7 +63,8 @@ import java.util.Objects;
/** A preference that displays a card representing a {@link SafetyCenterIssue}. */
@RequiresApi(TIRAMISU)
-public class IssueCardPreference extends Preference implements ComparablePreference {
+public class IssueCardPreference extends Preference
+ implements ComparablePreference, GroupSectionDividerMixin {
public static final String TAG = IssueCardPreference.class.getSimpleName();
@@ -101,12 +103,13 @@ public class IssueCardPreference extends Preference implements ComparablePrefere
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- holder.itemView.setBackgroundResource(mPositionInCardList.getBackgroundDrawableResId());
+ View issueCardView = holder.itemView.requireViewById(R.id.issue_card);
+ issueCardView.setBackgroundResource(mPositionInCardList.getBackgroundDrawableResId());
int topMargin = getTopMargin(mPositionInCardList, getContext());
- MarginLayoutParams layoutParams = (MarginLayoutParams) holder.itemView.getLayoutParams();
+ MarginLayoutParams layoutParams = (MarginLayoutParams) issueCardView.getLayoutParams();
if (layoutParams.topMargin != topMargin) {
layoutParams.topMargin = topMargin;
- holder.itemView.setLayoutParams(layoutParams);
+ issueCardView.setLayoutParams(layoutParams);
}
// Set default group visibility in case view is being reused
@@ -202,19 +205,20 @@ public class IssueCardPreference extends Preference implements ComparablePrefere
private void configureSafetyProtectionView(PreferenceViewHolder holder) {
View safetyProtectionSectionView =
holder.findViewById(R.id.issue_card_protected_by_android);
+ View issueCard = holder.findViewById(R.id.issue_card);
if (safetyProtectionSectionView.getVisibility() == View.GONE) {
- holder.itemView.setPaddingRelative(
- holder.itemView.getPaddingStart(),
- holder.itemView.getPaddingTop(),
- holder.itemView.getPaddingEnd(),
+ issueCard.setPaddingRelative(
+ issueCard.getPaddingStart(),
+ issueCard.getPaddingTop(),
+ issueCard.getPaddingEnd(),
/* bottom= */ getContext()
.getResources()
.getDimensionPixelSize(R.dimen.sc_card_margin_bottom));
} else {
- holder.itemView.setPaddingRelative(
- holder.itemView.getPaddingStart(),
- holder.itemView.getPaddingTop(),
- holder.itemView.getPaddingEnd(),
+ issueCard.setPaddingRelative(
+ issueCard.getPaddingStart(),
+ issueCard.getPaddingTop(),
+ issueCard.getPaddingEnd(),
/* bottom= */ 0);
}
}
@@ -427,9 +431,7 @@ public class IssueCardPreference extends Preference implements ComparablePrefere
TypedValue buttonThemeValue = new TypedValue();
mContext.getTheme()
.resolveAttribute(
- R.attr.scActionButtonTheme,
- buttonThemeValue,
- /* resolveRefs= */ false);
+ R.attr.scActionButtonTheme, buttonThemeValue, /* resolveRefs= */ false);
mContextThemeWrapper = new ContextThemeWrapper(context, buttonThemeValue.data);
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardPreference.kt
index a63e19984..5c86b4515 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardPreference.kt
@@ -24,6 +24,7 @@ import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
import com.android.permissioncontroller.R
import com.android.permissioncontroller.safetycenter.ui.view.MoreIssuesHeaderView
+import com.android.settingslib.widget.GroupSectionDividerMixin
/** A preference that displays a card linking to a list of more {@link SafetyCenterIssue}. */
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@@ -34,8 +35,8 @@ internal class MoreIssuesCardPreference(
private var newMoreIssuesCardData: MoreIssuesCardData,
private val dismissedOnly: Boolean,
val isStaticHeader: Boolean,
- private val onClickListener: () -> Unit
-) : Preference(context), ComparablePreference {
+ private val onClickListener: () -> Unit,
+) : Preference(context), ComparablePreference, GroupSectionDividerMixin {
init {
layoutResource = R.layout.preference_more_issues_card
@@ -44,11 +45,12 @@ internal class MoreIssuesCardPreference(
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
- val issueHeaderView = holder.itemView as MoreIssuesHeaderView
+ val issueHeaderView =
+ holder.itemView.requireViewById<MoreIssuesHeaderView>(R.id.more_issues_card)
if (isStaticHeader) {
issueHeaderView.showStaticHeader(
context.getString(R.string.safety_center_dismissed_issues_card_title),
- newMoreIssuesCardData.severityLevel
+ newMoreIssuesCardData.severityLevel,
)
} else {
issueHeaderView.showExpandableHeader(
@@ -62,7 +64,7 @@ internal class MoreIssuesCardPreference(
}
),
overrideChevronIconResId,
- onClickListener
+ onClickListener,
)
}
}
@@ -94,5 +96,5 @@ internal class MoreIssuesCardPreference(
internal data class MoreIssuesCardData(
val severityLevel: Int,
val hiddenIssueCount: Int,
- val isExpanded: Boolean
+ val isExpanded: Boolean,
)
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt
index 57e4175ca..c5287af53 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt
@@ -28,11 +28,12 @@ import androidx.preference.PreferenceViewHolder
import com.android.permissioncontroller.Constants.EXTRA_SESSION_ID
import com.android.permissioncontroller.R
import com.android.permissioncontroller.safetycenter.SafetyCenterConstants
+import com.android.settingslib.widget.GroupSectionDividerMixin
/** A preference that displays the Security and Privacy brand name on a Safety Center subpage. */
@RequiresApi(UPSIDE_DOWN_CAKE)
internal class SafetyBrandChipPreference(context: Context, attrs: AttributeSet) :
- Preference(context, attrs) {
+ Preference(context, attrs), GroupSectionDividerMixin {
init {
setLayoutResource(R.layout.preference_brand_chip)
@@ -67,7 +68,7 @@ internal class SafetyBrandChipPreference(context: Context, attrs: AttributeSet)
fun closeSubpage(
fragmentActivity: FragmentActivity,
fragmentContext: Context,
- sessionId: Long
+ sessionId: Long,
) {
val openedFromHomepage =
fragmentActivity
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java
index c6f2d146f..04206479f 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java
@@ -29,6 +29,8 @@ import static com.android.permissioncontroller.safetycenter.SafetyCenterConstant
import static com.android.permissioncontroller.safetycenter.SafetyCenterConstants.PRIVATE_PROFILE_SUFFIX;
import static com.android.permissioncontroller.safetycenter.SafetyCenterConstants.WORK_PROFILE_SUFFIX;
+import static java.util.Objects.requireNonNull;
+
import android.app.ActionBar;
import android.content.Intent;
import android.content.res.Configuration;
@@ -60,6 +62,7 @@ import com.android.permissioncontroller.permission.utils.Utils;
import com.android.permissioncontroller.safetycenter.ui.model.PrivacyControlsViewModel.Pref;
import com.android.settingslib.activityembedding.ActivityEmbeddingUtils;
import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
+import com.android.settingslib.widget.SettingsThemeHelper;
import java.util.List;
import java.util.Objects;
@@ -78,10 +81,10 @@ public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity {
private static final String EXTRA_PREVENT_TRAMPOLINE_TO_SETTINGS =
"com.android.permissioncontroller.safetycenter.extra.PREVENT_TRAMPOLINE_TO_SETTINGS";
- private SafetyCenterManager mSafetyCenterManager;
+ @Nullable private SafetyCenterManager mSafetyCenterManager;
@Override
- public void onCreate(Bundle savedInstanceState) {
+ public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSafetyCenterManager = getSystemService(SafetyCenterManager.class);
@@ -93,18 +96,25 @@ public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity {
return;
}
+ if (SettingsThemeHelper.isExpressiveTheme(this)) {
+ // Setting a theme programmatically causes standard preferences to display weirdly.
+ // See b/377519324.
+ setTheme(R.style.Theme_SafetyCenterExpressive);
+ }
+
Fragment frag;
+ Intent intent = getIntent();
final boolean maybeOpenSubpage =
SafetyCenterUiFlags.getShowSubpages()
- && getIntent().getAction().equals(ACTION_SAFETY_CENTER);
- if (maybeOpenSubpage && getIntent().hasExtra(EXTRA_SAFETY_SOURCES_GROUP_ID)) {
- String groupId = getIntent().getStringExtra(EXTRA_SAFETY_SOURCES_GROUP_ID);
+ && Objects.equals(intent.getAction(), ACTION_SAFETY_CENTER);
+ if (maybeOpenSubpage && intent.hasExtra(EXTRA_SAFETY_SOURCES_GROUP_ID)) {
+ String groupId = intent.getStringExtra(EXTRA_SAFETY_SOURCES_GROUP_ID);
frag = openRelevantSubpage(groupId);
- } else if (maybeOpenSubpage && getIntent().hasExtra(EXTRA_SETTINGS_FRAGMENT_ARGS_KEY)) {
- String preferenceKey = getIntent().getStringExtra(EXTRA_SETTINGS_FRAGMENT_ARGS_KEY);
+ } else if (maybeOpenSubpage && intent.hasExtra(EXTRA_SETTINGS_FRAGMENT_ARGS_KEY)) {
+ String preferenceKey = intent.getStringExtra(EXTRA_SETTINGS_FRAGMENT_ARGS_KEY);
String groupId = getParentGroupId(preferenceKey);
frag = openRelevantSubpage(groupId);
- } else if (getIntent().getAction().equals(PRIVACY_CONTROLS_ACTION)) {
+ } else if (Objects.equals(intent.getAction(), PRIVACY_CONTROLS_ACTION)) {
setTitle(R.string.privacy_controls_title);
frag = PrivacyControlsFragment.newInstance();
} else {
@@ -306,7 +316,8 @@ public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity {
return PRIVACY_SOURCES_GROUP_ID;
}
- SafetyCenterConfig safetyCenterConfig = mSafetyCenterManager.getSafetyCenterConfig();
+ SafetyCenterConfig safetyCenterConfig =
+ requireNonNull(mSafetyCenterManager).getSafetyCenterConfig();
String[] splitKey;
if (preferenceKey.endsWith(PERSONAL_PROFILE_SUFFIX)) {
splitKey = preferenceKey.split("_" + PERSONAL_PROFILE_SUFFIX);
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
index efbd57080..ed6bc382c 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
@@ -56,6 +56,7 @@ import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData
import com.android.permissioncontroller.safetycenter.ui.model.StatusUiData;
import com.android.safetycenter.internaldata.SafetyCenterBundles;
import com.android.safetycenter.resources.SafetyCenterResourcesApk;
+import com.android.settingslib.widget.SettingsThemeHelper;
import kotlin.Unit;
@@ -121,12 +122,16 @@ public final class SafetyCenterDashboardFragment extends SafetyCenterFragment {
mEntriesGroup = getPreferenceScreen().findPreference(ENTRIES_GROUP_KEY);
mStaticEntriesGroup = getPreferenceScreen().findPreference(STATIC_ENTRIES_GROUP_KEY);
+ Preference spacerPreference = getPreferenceScreen().findPreference(SPACER_KEY);
+ if (SettingsThemeHelper.isExpressiveTheme(requireContext())) {
+ getPreferenceScreen().removePreference(spacerPreference);
+ }
+
if (mIsQuickSettingsFragment) {
getPreferenceScreen().removePreference(mEntriesGroup);
mEntriesGroup = null;
getPreferenceScreen().removePreference(mStaticEntriesGroup);
mStaticEntriesGroup = null;
- Preference spacerPreference = getPreferenceScreen().findPreference(SPACER_KEY);
getPreferenceScreen().removePreference(spacerPreference);
}
getSafetyCenterViewModel().getStatusUiLiveData().observe(this, this::updateStatus);
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt
index 9feecf5d4..04503de5e 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt
@@ -22,7 +22,6 @@ import android.safetycenter.SafetyCenterErrorDetails
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.lifecycle.ViewModelProvider
-import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen
import androidx.recyclerview.widget.RecyclerView
import com.android.permissioncontroller.Constants.EXTRA_SESSION_ID
@@ -33,10 +32,12 @@ import com.android.permissioncontroller.safetycenter.ui.model.LiveSafetyCenterVi
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel
import com.android.safetycenter.resources.SafetyCenterResourcesApk
+import com.android.settingslib.widget.SettingsBasePreferenceFragment
+import com.android.settingslib.widget.SettingsThemeHelper
/** A base fragment that represents a page in Safety Center. */
@RequiresApi(TIRAMISU)
-abstract class SafetyCenterFragment : PreferenceFragmentCompat() {
+abstract class SafetyCenterFragment : SettingsBasePreferenceFragment() {
lateinit var safetyCenterViewModel: SafetyCenterViewModel
lateinit var sameTaskSourceIds: List<String>
@@ -51,12 +52,17 @@ abstract class SafetyCenterFragment : PreferenceFragmentCompat() {
override fun onCreateAdapter(
preferenceScreen: PreferenceScreen
- ): RecyclerView.Adapter<RecyclerView.ViewHolder> {
+ ): RecyclerView.Adapter<out RecyclerView.ViewHolder> {
/* The scroll-to-result functionality for settings search is currently implemented only for
* subpages i.e. non expand-and-collapse type entries. Hence, we check that the flag is
* enabled before using an adapter that does the highlighting and scrolling. */
- val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> =
- if (SafetyCenterUiFlags.getShowSubpages()) {
+ val adapter: RecyclerView.Adapter<out RecyclerView.ViewHolder> =
+ if (
+ SafetyCenterUiFlags.getShowSubpages() &&
+ !SettingsThemeHelper.isExpressiveTheme(requireContext())
+ ) {
+ // TODO: b/378433878 - Create highlight adapter for settings expressive theme, which
+ // has a different base class.
highlightManager.createAdapter(preferenceScreen)
} else {
super.onCreateAdapter(preferenceScreen)
@@ -80,7 +86,7 @@ abstract class SafetyCenterFragment : PreferenceFragmentCompat() {
safetyCenterViewModel =
ViewModelProvider(
requireActivity(),
- LiveSafetyCenterViewModelFactory(requireActivity().getApplication())
+ LiveSafetyCenterViewModelFactory(requireActivity().getApplication()),
)
.get(SafetyCenterViewModel::class.java)
safetyCenterViewModel.safetyCenterUiLiveData.observe(this) { uiData: SafetyCenterUiData? ->
@@ -177,7 +183,7 @@ abstract class SafetyCenterFragment : PreferenceFragmentCompat() {
safetyCenterViewModel.interactionLogger.recordForIssue(
Action.SAFETY_CENTER_VIEWED,
maybeIssue,
- isDismissed = false
+ isDismissed = false,
)
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsActivity.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsActivity.java
index 2ad282449..d9f45cc08 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsActivity.java
@@ -24,7 +24,9 @@ import android.permission.PermissionManager;
import androidx.fragment.app.FragmentActivity;
import com.android.modules.utils.build.SdkLevel;
+import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.utils.Utils;
+import com.android.settingslib.widget.SettingsThemeHelper;
/** Activity for the Safety Center Quick Settings Activity */
public class SafetyCenterQsActivity extends FragmentActivity {
@@ -39,6 +41,12 @@ public class SafetyCenterQsActivity extends FragmentActivity {
return;
}
+ if (SettingsThemeHelper.isExpressiveTheme(this)) {
+ // Safe to set expressive theme here since QS doesn't display vanilla preferences.
+ // See b/377519324.
+ setTheme(R.style.Theme_SafetyCenterQsExpressive);
+ }
+
configureFragment();
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt
index fdade2189..2e5ba49ae 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt
@@ -28,6 +28,7 @@ import com.android.permissioncontroller.safetycenter.ui.SafetyBrandChipPreferenc
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData
import com.android.safetycenter.resources.SafetyCenterResourcesApk
import com.android.settingslib.widget.FooterPreference
+import com.android.settingslib.widget.SettingsThemeHelper
/** A fragment that represents a generic subpage in Safety Center. */
@RequiresApi(UPSIDE_DOWN_CAKE)
@@ -45,15 +46,16 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() {
setPreferencesFromResource(R.xml.safety_center_subpage, rootKey)
sourceGroupId = requireArguments().getString(SOURCE_GROUP_ID_KEY)!!
- subpageBrandChip = getPreferenceScreen().findPreference(BRAND_CHIP_KEY)!!
- subpageIllustration = getPreferenceScreen().findPreference(ILLUSTRATION_KEY)!!
- subpageIssueGroup = getPreferenceScreen().findPreference(ISSUE_GROUP_KEY)!!
- subpageEntryGroup = getPreferenceScreen().findPreference(ENTRY_GROUP_KEY)!!
- subpageFooter = getPreferenceScreen().findPreference(FOOTER_KEY)!!
+ subpageBrandChip = preferenceScreen.findPreference(BRAND_CHIP_KEY)!!
+ subpageIllustration = preferenceScreen.findPreference(ILLUSTRATION_KEY)!!
+ subpageIssueGroup = preferenceScreen.findPreference(ISSUE_GROUP_KEY)!!
+ subpageEntryGroup = preferenceScreen.findPreference(ENTRY_GROUP_KEY)!!
+ subpageFooter = preferenceScreen.findPreference(FOOTER_KEY)!!
subpageBrandChip.setupListener(requireActivity(), safetyCenterSessionId)
setupIllustration()
setupFooter()
+ maybeRemoveSpacer()
prerenderCurrentSafetyCenterData()
}
@@ -80,7 +82,7 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() {
return
}
- requireActivity().setTitle(entryGroup.title)
+ requireActivity().title = entryGroup.title
updateSafetyCenterIssues(uiData)
updateSafetyCenterEntries(entryGroup)
}
@@ -91,7 +93,7 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() {
val drawable = SafetyCenterResourcesApk(context).getDrawableByName(resName, context.theme)
if (drawable == null) {
Log.w(TAG, "$sourceGroupId doesn't have any matching illustration")
- subpageIllustration.setVisible(false)
+ subpageIllustration.isVisible = false
}
subpageIllustration.illustrationDrawable = drawable
@@ -102,12 +104,19 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() {
val footerText = SafetyCenterResourcesApk(requireContext()).getStringByName(resName)
if (footerText.isEmpty()) {
Log.w(TAG, "$sourceGroupId doesn't have any matching footer")
- subpageFooter.setVisible(false)
+ subpageFooter.isVisible = false
}
// footer is ordered last by default
// in order to keep a spacer after the footer, footer needs to be the second from last
- subpageFooter.setOrder(Int.MAX_VALUE - 2)
- subpageFooter.setSummary(footerText)
+ subpageFooter.order = Int.MAX_VALUE - 2
+ subpageFooter.summary = footerText
+ }
+
+ private fun maybeRemoveSpacer() {
+ if (SettingsThemeHelper.isExpressiveTheme(requireContext())) {
+ val spacerPreference = preferenceScreen.findPreference<SpacerPreference>(SPACER_KEY)!!
+ preferenceScreen.removePreference(spacerPreference)
+ }
}
private fun updateSafetyCenterIssues(uiData: SafetyCenterUiData?) {
@@ -131,7 +140,7 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() {
subpageIssues,
subpageDismissedIssues,
uiData.resolvedIssues,
- requireActivity().getTaskId()
+ requireActivity().taskId,
)
}
@@ -145,23 +154,24 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() {
PendingIntentSender.getTaskIdForEntry(
entry.id,
sameTaskSourceIds,
- requireActivity()
+ requireActivity(),
),
entry,
- safetyCenterViewModel
+ safetyCenterViewModel,
)
)
}
}
companion object {
- private val TAG: String = SafetyCenterSubpageFragment::class.java.simpleName
- private const val BRAND_CHIP_KEY: String = "subpage_brand_chip"
- private const val ILLUSTRATION_KEY: String = "subpage_illustration"
- private const val ISSUE_GROUP_KEY: String = "subpage_issue_group"
- private const val ENTRY_GROUP_KEY: String = "subpage_entry_group"
- private const val FOOTER_KEY: String = "subpage_footer"
- private const val SOURCE_GROUP_ID_KEY: String = "source_group_id"
+ private val TAG = SafetyCenterSubpageFragment::class.java.simpleName
+ private const val BRAND_CHIP_KEY = "subpage_brand_chip"
+ private const val ILLUSTRATION_KEY = "subpage_illustration"
+ private const val ISSUE_GROUP_KEY = "subpage_issue_group"
+ private const val ENTRY_GROUP_KEY = "subpage_entry_group"
+ private const val FOOTER_KEY = "subpage_footer"
+ private const val SPACER_KEY = "subpage_spacer"
+ private const val SOURCE_GROUP_ID_KEY = "source_group_id"
/** Creates an instance of SafetyCenterSubpageFragment with the arguments set */
@JvmStatic
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyIllustrationPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyIllustrationPreference.kt
index 5acb27131..3976fa7e3 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyIllustrationPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyIllustrationPreference.kt
@@ -25,11 +25,12 @@ import androidx.annotation.RequiresApi
import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
import com.android.permissioncontroller.R
+import com.android.settingslib.widget.GroupSectionDividerMixin
/** A preference that displays the illustration on a Safety Center subpage. */
@RequiresApi(UPSIDE_DOWN_CAKE)
internal class SafetyIllustrationPreference(context: Context, attrs: AttributeSet) :
- Preference(context, attrs) {
+ Preference(context, attrs), GroupSectionDividerMixin {
init {
layoutResource = R.layout.preference_illustration
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java
index 811841845..abf159955 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java
@@ -40,6 +40,7 @@ import com.android.permissioncontroller.R;
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel;
import com.android.permissioncontroller.safetycenter.ui.model.StatusUiData;
import com.android.permissioncontroller.safetycenter.ui.view.StatusCardView;
+import com.android.settingslib.widget.GroupSectionDividerMixin;
import kotlin.Pair;
@@ -48,7 +49,8 @@ import java.util.Objects;
/** Preference which displays a visual representation of {@link SafetyCenterStatus}. */
@RequiresApi(TIRAMISU)
-public class SafetyStatusPreference extends Preference implements ComparablePreference {
+public class SafetyStatusPreference extends Preference
+ implements ComparablePreference, GroupSectionDividerMixin {
private static final String TAG = "SafetyStatusPreference";
@@ -82,7 +84,8 @@ public class SafetyStatusPreference extends Preference implements ComparablePref
}
Context context = getContext();
- StatusCardView statusCardView = (StatusCardView) holder.itemView;
+ StatusCardView statusCardView = holder.itemView.requireViewById(R.id.status_card);
+
configureButtons(context, statusCardView);
statusCardView
.getTitleAndSummaryContainerView()
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt
index 030b67be9..7f619d1ca 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt
@@ -55,6 +55,7 @@ internal class SpacerPreference(context: Context, attrs: AttributeSet) :
}
private var maxKnownToolbarHeight = 0
+
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
val spacer = holder.itemView
@@ -74,7 +75,7 @@ internal class SpacerPreference(context: Context, attrs: AttributeSet) :
oldLeft: Int,
oldTop: Int,
oldRight: Int,
- oldBottom: Int
+ oldBottom: Int,
) {
adjustHeight(spacer)
}
diff --git a/tests/cts/permissionpolicy/res/raw/android_manifest.xml b/tests/cts/permissionpolicy/res/raw/android_manifest.xml
index e1608f878..0cd91b50a 100644
--- a/tests/cts/permissionpolicy/res/raw/android_manifest.xml
+++ b/tests/cts/permissionpolicy/res/raw/android_manifest.xml
@@ -7873,6 +7873,15 @@
<permission android:name="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY"
android:protectionLevel="signature|role"/>
+ <!-- Allows an application to create displays that mirror other displays' content.
+ <p>Not for use by third-party applications.
+ <p>Protection level: internal|role
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ENABLE_LIMITED_VDM_ROLE)
+ @hide @SystemApi -->
+ <permission android:name="android.permission.ADD_MIRROR_DISPLAY"
+ android:protectionLevel="internal|role"
+ android:featureFlag="android.companion.virtualdevice.flags.enable_limited_vdm_role" />
+
<!-- @hide @SystemApi Allows an application to access locusId events in the usage stats. -->
<permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
android:protectionLevel="signature|role" />
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt
index d509add3a..da70fc186 100644
--- a/tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt
@@ -17,15 +17,22 @@
package android.permissionui.cts
import android.os.Build
+import android.permission.flags.Flags
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import androidx.test.filters.FlakyTest
import androidx.test.filters.SdkSuppress
import org.junit.Assume.assumeFalse
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
/** Runtime permission behavior tests for permission splits. */
@FlakyTest
class PermissionSplitTest : BaseUsePermissionTest() {
+
+ @Rule @JvmField val mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
@Before
fun assumeNotTv() {
assumeFalse(isTv)
@@ -56,6 +63,7 @@ class PermissionSplitTest : BaseUsePermissionTest() {
}
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ @RequiresFlagsDisabled(Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED)
@Test
fun testBodySensorSplit() {
installPackage(APP_APK_PATH_31)
@@ -63,6 +71,7 @@ class PermissionSplitTest : BaseUsePermissionTest() {
}
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ @RequiresFlagsDisabled(Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED)
@Test
fun testBodySensorSplit32() {
installPackage(APP_APK_PATH_32)
@@ -70,6 +79,7 @@ class PermissionSplitTest : BaseUsePermissionTest() {
}
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ @RequiresFlagsDisabled(Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED)
@Test
fun testBodySensorNonSplit() {
installPackage(APP_APK_PATH_LATEST)
diff --git a/tests/cts/rolemultiuser/Android.bp b/tests/cts/rolemultiuser/Android.bp
new file mode 100644
index 000000000..13fbf28ff
--- /dev/null
+++ b/tests/cts/rolemultiuser/Android.bp
@@ -0,0 +1,47 @@
+// Copyright (C) 2024 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "CtsRoleMultiUserTestCases",
+ defaults: ["mts-target-sdk-version-current"],
+ sdk_version: "test_current",
+ min_sdk_version: "30",
+
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+
+ static_libs: [
+ "bedstead-flags",
+ "bedstead-multiuser",
+ "com.android.permission.flags-aconfig-java-export",
+ "ctstestrunner-axt",
+ "Harrier",
+ "flag-junit",
+ "Nene",
+ "truth",
+ ],
+
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "mcts-permission",
+ ],
+}
diff --git a/tests/cts/rolemultiuser/AndroidManifest.xml b/tests/cts/rolemultiuser/AndroidManifest.xml
new file mode 100644
index 000000000..1524c5703
--- /dev/null
+++ b/tests/cts/rolemultiuser/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * Copyright (C) 2024 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.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.app.rolemultiuser.cts">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.app.rolemultiuser.cts"
+ android:label="CTS multi-user tests of android.app.role">
+ </instrumentation>
+</manifest>
diff --git a/tests/cts/rolemultiuser/AndroidTest.xml b/tests/cts/rolemultiuser/AndroidTest.xml
new file mode 100644
index 000000000..00f4c0993
--- /dev/null
+++ b/tests/cts/rolemultiuser/AndroidTest.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+
+<configuration description="Config for CTS role multi-user test cases">
+
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="permissions" />
+ <option name="config-descriptor:metadata" key="parameter" value="all_foldable_states" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="multiuser" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="run_on_sdk_sandbox" />
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.permission.apex" />
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsRoleMultiUserTestCases.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="mkdir -p /data/local/tmp/cts-role" />
+ <option name="teardown-command" value="rm -rf /data/local/tmp/cts-role"/>
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.app.rolemultiuser.cts" />
+ <option name="exclude-annotation" value="com.android.bedstead.enterprise.annotations.RequireRunOnWorkProfile" />
+ <option name="exclude-annotation" value="com.android.bedstead.multiuser.annotations.RequireRunOnSecondaryUser" />
+ <option name="runtime-hint" value="5m" />
+ </test>
+</configuration>
diff --git a/tests/cts/rolemultiuser/OWNERS b/tests/cts/rolemultiuser/OWNERS
new file mode 100644
index 000000000..fb6099cf7
--- /dev/null
+++ b/tests/cts/rolemultiuser/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 137825
+
+include platform/frameworks/base:/core/java/android/permission/OWNERS
diff --git a/tests/cts/rolemultiuser/TEST_MAPPING b/tests/cts/rolemultiuser/TEST_MAPPING
new file mode 100644
index 000000000..323e3094c
--- /dev/null
+++ b/tests/cts/rolemultiuser/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "postsubmit": [
+ {
+ "name": "CtsRoleMultiUserTestCases"
+ }
+ ],
+ "mainline-postsubmit": [
+ {
+ "name": "CtsRoleMultiUserTestCases[com.google.android.permission.apex]"
+ }
+ ]
+}
diff --git a/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt b/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt
new file mode 100644
index 000000000..d1bf5284c
--- /dev/null
+++ b/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2024 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 android.app.rolemultiuser.cts
+
+import android.app.role.RoleManager
+import android.content.Context
+import android.os.Build
+import android.os.Process
+import androidx.test.filters.SdkSuppress
+import com.android.bedstead.enterprise.annotations.EnsureHasWorkProfile
+import com.android.bedstead.enterprise.workProfile
+import com.android.bedstead.flags.annotations.RequireFlagsEnabled
+import com.android.bedstead.harrier.BedsteadJUnit4
+import com.android.bedstead.harrier.DeviceState
+import com.android.bedstead.multiuser.annotations.EnsureHasAdditionalUser
+import com.android.bedstead.multiuser.annotations.EnsureHasPrivateProfile
+import com.android.bedstead.multiuser.annotations.EnsureHasSecondaryUser
+import com.android.bedstead.multiuser.annotations.RequireRunNotOnSecondaryUser
+import com.android.bedstead.multiuser.privateProfile
+import com.android.bedstead.multiuser.secondaryUser
+import com.android.bedstead.nene.TestApis.context
+import com.android.bedstead.nene.TestApis.users
+import com.android.bedstead.nene.types.OptionalBoolean
+import com.android.bedstead.permissions.CommonPermissions.INTERACT_ACROSS_USERS_FULL
+import com.android.bedstead.permissions.CommonPermissions.MANAGE_DEFAULT_APPLICATIONS
+import com.android.bedstead.permissions.CommonPermissions.MANAGE_ROLE_HOLDERS
+import com.android.bedstead.permissions.annotations.EnsureDoesNotHavePermission
+import com.android.bedstead.permissions.annotations.EnsureHasPermission
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
+import org.junit.Assume.assumeFalse
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(BedsteadJUnit4::class)
+class RoleManagerMultiUserTest {
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @Test
+ @Throws(Exception::class)
+ fun cannotGetActiveUserForNonCrossUserRole() {
+ assertThrows(IllegalArgumentException::class.java) {
+ roleManager.getActiveUserForRole(RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER)
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(MANAGE_ROLE_HOLDERS)
+ @EnsureDoesNotHavePermission(INTERACT_ACROSS_USERS_FULL)
+ @Test
+ @Throws(Exception::class)
+ fun cannotGetActiveUserForRoleWithoutInteractAcrossUserPermission() {
+ assertThrows(SecurityException::class.java) {
+ roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL)
+ @EnsureDoesNotHavePermission(MANAGE_ROLE_HOLDERS, MANAGE_DEFAULT_APPLICATIONS)
+ @Test
+ @Throws(Exception::class)
+ fun cannotGetActiveUserForRoleWithoutManageRoleAndManageDefaultApplicationsPermission() {
+ assertThrows(SecurityException::class.java) {
+ roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @Test
+ @Throws(Exception::class)
+ fun cannotSetActiveUserForNonCrossUserRole() {
+ assertThrows(IllegalArgumentException::class.java) {
+ roleManager.setActiveUserForRole(
+ RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER,
+ Process.myUserHandle(),
+ 0,
+ )
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(MANAGE_ROLE_HOLDERS)
+ @EnsureDoesNotHavePermission(INTERACT_ACROSS_USERS_FULL)
+ @Test
+ @Throws(Exception::class)
+ fun cannotSetActiveUserForRoleWithoutInteractAcrossUserPermission() {
+ assertThrows(SecurityException::class.java) {
+ roleManager.setActiveUserForRole(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ Process.myUserHandle(),
+ 0,
+ )
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL)
+ @EnsureDoesNotHavePermission(MANAGE_ROLE_HOLDERS, MANAGE_DEFAULT_APPLICATIONS)
+ @Test
+ @Throws(Exception::class)
+ fun cannotSetActiveUserForRoleWithoutManageRoleAndManageDefaultApplicationsPermission() {
+ assertThrows(SecurityException::class.java) {
+ roleManager.setActiveUserForRole(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ Process.myUserHandle(),
+ 0,
+ )
+ }
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @Test
+ @Throws(Exception::class)
+ fun cannotSetActiveUserForRoleToNonExistentUser() {
+ val targetActiveUser = users().nonExisting().userHandle()
+ roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser, 0)
+
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isNotEqualTo(targetActiveUser)
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasPrivateProfile(installInstrumentedApp = OptionalBoolean.TRUE)
+ @Test
+ @Throws(Exception::class)
+ fun cannotSetActiveUserForRoleToPrivateProfileUser() {
+ val targetActiveUser = deviceState.privateProfile().userHandle()
+ roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser, 0)
+
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isNotEqualTo(targetActiveUser)
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasAdditionalUser(installInstrumentedApp = OptionalBoolean.TRUE)
+ @EnsureHasSecondaryUser
+ @RequireRunNotOnSecondaryUser
+ @Test
+ @Throws(Exception::class)
+ fun cannotSetActiveUserForRoleToUserNotInProfileGroup() {
+ val targetActiveUser = deviceState.secondaryUser().userHandle()
+ roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser, 0)
+
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isNotEqualTo(targetActiveUser)
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureDoesNotHavePermission(MANAGE_DEFAULT_APPLICATIONS)
+ @EnsureHasWorkProfile(installInstrumentedApp = OptionalBoolean.TRUE)
+ @Test
+ @Throws(Exception::class)
+ fun setAndGetActiveUserForRoleSetCurrentUserWithManageRoleHoldersPermission() {
+ assumeFalse(
+ "setActiveUser not supported for private profile",
+ users().current().type().name() == PRIVATE_PROFILE_TYPE_NAME,
+ )
+
+ val targetActiveUser = users().current().userHandle()
+ roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser, 0)
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_DEFAULT_APPLICATIONS)
+ @EnsureDoesNotHavePermission(MANAGE_ROLE_HOLDERS)
+ @EnsureHasWorkProfile(installInstrumentedApp = OptionalBoolean.TRUE)
+ @Test
+ @Throws(Exception::class)
+ fun setAndGetActiveUserForRoleSetCurrentUserWithManageDefaultApplicationPermission() {
+ assumeFalse(
+ "setActiveUser not supported for private profile",
+ users().current().type().name() == PRIVATE_PROFILE_TYPE_NAME,
+ )
+
+ val targetActiveUser = users().current().userHandle()
+ roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser, 0)
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ }
+
+ @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED)
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasWorkProfile(installInstrumentedApp = OptionalBoolean.TRUE)
+ @Test
+ @Throws(Exception::class)
+ fun setAndGetActiveUserForRoleSetWorkProfile() {
+ val targetActiveUser = deviceState.workProfile().userHandle()
+ roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser, 0)
+
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ }
+
+ companion object {
+ private const val PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME =
+ RoleManager.ROLE_RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY
+ private const val PRIVATE_PROFILE_TYPE_NAME = "android.os.usertype.profile.PRIVATE"
+ private val context: Context = context().instrumentedContext()
+ private val roleManager: RoleManager = context.getSystemService(RoleManager::class.java)
+
+ @JvmField @ClassRule @Rule val deviceState = DeviceState()
+ }
+}