diff options
33 files changed, 616 insertions, 471 deletions
diff --git a/PermissionController/AndroidManifest.xml b/PermissionController/AndroidManifest.xml index b85e5ebdc..325597e67 100644 --- a/PermissionController/AndroidManifest.xml +++ b/PermissionController/AndroidManifest.xml @@ -356,11 +356,6 @@ </intent-filter> </activity> - <activity android:name="com.android.permissioncontroller.permission.ui.OverlayWarningDialog" - android:excludeFromRecents="true" - android:exported="false" - android:theme="@style/Theme.DeviceDefault.Dialog.NoActionBar.DayNight" /> - <activity android:name="com.android.permissioncontroller.permission.ui.LocationProviderInterceptDialog" android:excludeFromRecents="true" android:exported="false" @@ -599,6 +594,8 @@ </intent-filter> </activity> + <!-- Unexported empty activity for in-process tests --> + <activity android:name="android.app.Activity" /> </application> </manifest> diff --git a/PermissionController/res/values/strings.xml b/PermissionController/res/values/strings.xml index 8c9f86d70..8399147c3 100644 --- a/PermissionController/res/values/strings.xml +++ b/PermissionController/res/values/strings.xml @@ -330,19 +330,6 @@ <!-- Title of the permission dialog for accessibility purposes- spoken to the user. [CHAR LIMIT=none] --> <string name="permission_request_title">Permission request</string> - <!-- Title for the dialog that warns the user they need to turn off screen overlays - before permissions can be changed. [CHAR LIMIT=NONE] --> - <string name="screen_overlay_title">Screen overlay detected</string> - - <!-- Message for the dialog that warns the user they need to turn off screen overlays - before permissions can be changed. The "Settings > Apps" conveys to the user to - go to Settings and click on apps, this may need updates in RTL languages. [CHAR LIMIT=NONE] --> - <string name="screen_overlay_message">To change this permission setting, you first have to turn off the screen overlay from Settings \u003e Apps</string> - - <!-- Button for the dialog that warns the user they need to turn off screen overlays - before permissions can be changed. [CHAR LIMIT=NONE] --> - <string name="screen_overlay_button">Open settings</string> - <!-- Title of dialog telling users that Install/Uninstall action is not supported on Android Wear. [CHAR LIMIT=30] --> <string name="wear_not_allowed_dlg_title">Android Wear</string> <!-- Title of dialog telling users that Install/Uninstall action is not supported on Android Wear. [CHAR LIMIT=none] --> diff --git a/PermissionController/src/com/android/permissioncontroller/permission/compat/LinkMovementMethodCompat.java b/PermissionController/src/com/android/permissioncontroller/permission/compat/LinkMovementMethodCompat.java new file mode 100644 index 000000000..637eb5fc4 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/compat/LinkMovementMethodCompat.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2023 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.compat; + +import android.text.Layout; +import android.text.Selection; +import android.text.Spannable; +import android.text.method.LinkMovementMethod; +import android.text.method.Touch; +import android.view.MotionEvent; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +/** + * Fixes the issue that links can be triggered for touches outside of line bounds for + * {@link LinkMovementMethod}. + * <p> + * This is based on the fix in ag/22301465. + */ +public class LinkMovementMethodCompat extends LinkMovementMethod { + @Override + public boolean onTouchEvent(@NonNull TextView widget, @NonNull Spannable buffer, + @NonNull MotionEvent event) { + int action = event.getAction(); + + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) { + int x = (int) event.getX(); + int y = (int) event.getY(); + + x -= widget.getTotalPaddingLeft(); + y -= widget.getTotalPaddingTop(); + + x += widget.getScrollX(); + y += widget.getScrollY(); + + Layout layout = widget.getLayout(); + boolean isOutOfLineBounds; + if (y < 0 || y > layout.getHeight()) { + isOutOfLineBounds = true; + } else { + int line = layout.getLineForVertical(y); + isOutOfLineBounds = x < layout.getLineLeft(line) || x > layout.getLineRight(line); + } + + if (isOutOfLineBounds) { + Selection.removeSelection(buffer); + + // return LinkMovementMethod.super.onTouchEvent(widget, buffer, event); + return Touch.onTouchEvent(widget, buffer, event); + } + } + + return super.onTouchEvent(widget, buffer, event); + } + + /** + * @return a {@link LinkMovementMethodCompat} instance + */ + @NonNull + public static LinkMovementMethodCompat getInstance() { + if (sInstance == null) { + sInstance = new LinkMovementMethodCompat(); + } + + return sInstance; + } + + private static LinkMovementMethodCompat sInstance; +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/legacy/PermissionApps.java b/PermissionController/src/com/android/permissioncontroller/permission/model/legacy/PermissionApps.java index 53ba48ace..196bfc6af 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/legacy/PermissionApps.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/legacy/PermissionApps.java @@ -38,8 +38,8 @@ import androidx.annotation.Nullable; import com.android.modules.utils.build.SdkLevel; import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.model.AppPermissionGroup; -import com.android.permissioncontroller.permission.utils.SubattributionUtils; import com.android.permissioncontroller.permission.utils.Utils; +import com.android.permissioncontroller.permission.utils.v31.SubattributionUtils; import java.util.ArrayList; import java.util.Collections; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java index 3c1a9df39..82620058d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java @@ -59,12 +59,12 @@ import com.android.permissioncontroller.permission.model.Permission; import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo; import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState; import com.android.permissioncontroller.permission.ui.AutoGrantPermissionsNotifier; -import com.android.permissioncontroller.permission.utils.AdminRestrictedPermissionsUtils; import com.android.permissioncontroller.permission.utils.ArrayUtils; import com.android.permissioncontroller.permission.utils.KotlinUtils; import com.android.permissioncontroller.permission.utils.PermissionMapping; import com.android.permissioncontroller.permission.utils.UserSensitiveFlagsUtils; import com.android.permissioncontroller.permission.utils.Utils; +import com.android.permissioncontroller.permission.utils.v31.AdminRestrictedPermissionsUtils; import com.android.role.controller.model.Role; import com.android.role.controller.model.Roles; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/OverlayWarningDialog.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/OverlayWarningDialog.java deleted file mode 100644 index de1f68c98..000000000 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/OverlayWarningDialog.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.permissioncontroller.permission.ui; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.ActivityNotFoundException; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.content.DialogInterface.OnDismissListener; -import android.content.Intent; -import android.os.Bundle; -import android.provider.Settings; -import android.util.Log; - -import com.android.permissioncontroller.R; - -public class OverlayWarningDialog extends Activity implements OnClickListener, OnDismissListener { - - private static final String TAG = "OverlayWarningDialog"; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - new AlertDialog.Builder(this) - .setTitle(R.string.screen_overlay_title) - .setMessage(R.string.screen_overlay_message) - .setPositiveButton(R.string.screen_overlay_button, this) - .setOnDismissListener(this) - .show(); - } - - @Override - public void onDismiss(DialogInterface dialog) { - finish(); - } - - @Override - public void onClick(DialogInterface dialog, int which) { - finish(); - try { - startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)); - } catch (ActivityNotFoundException e) { - Log.w(TAG, "No manage overlay settings", e); - } - } - -} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java index ad6b993fa..2de936469 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java @@ -55,11 +55,11 @@ import androidx.preference.TwoStatePreference; import com.android.car.ui.AlertDialogBuilder; import com.android.permissioncontroller.R; import com.android.permissioncontroller.auto.AutoSettingsFrameFragment; -import com.android.permissioncontroller.permission.ui.AdvancedConfirmDialogArgs; import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ChangeRequest; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModelFactory; +import com.android.permissioncontroller.permission.ui.v33.AdvancedConfirmDialogArgs; import com.android.permissioncontroller.permission.utils.KotlinUtils; import com.android.permissioncontroller.permission.utils.PackageRemovalMonitor; import com.android.settingslib.RestrictedLockUtils; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionHistoryPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionHistoryPreference.kt index a7cb6c340..7ea400127 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionHistoryPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionHistoryPreference.kt @@ -24,7 +24,7 @@ import androidx.preference.Preference.OnPreferenceClickListener import com.android.car.ui.preference.CarUiPreference import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModelLegacy +import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageDetailsViewModelLegacy /** Preference that displays a permission usage for an app. */ @RequiresApi(Build.VERSION_CODES.S) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt index eee9e8b6e..a16bc1ce4 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageDetailsFragment.kt @@ -42,8 +42,8 @@ import com.android.permissioncontroller.permission.model.v31.PermissionUsages import com.android.permissioncontroller.permission.model.v31.PermissionUsages.PermissionsUsagesChangeCallback import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity import com.android.permissioncontroller.permission.ui.auto.AutoDividerPreference -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModelFactoryLegacy -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModelLegacy +import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageDetailsViewModelFactoryLegacy +import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageDetailsViewModelLegacy import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupLabel import com.android.permissioncontroller.permission.utils.Utils import java.time.Clock diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageFragment.kt index a21e257c3..d4a2a073e 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/dashboard/AutoPermissionUsageFragment.kt @@ -40,9 +40,9 @@ import com.android.permissioncontroller.permission.model.v31.PermissionUsages import com.android.permissioncontroller.permission.model.v31.PermissionUsages.PermissionsUsagesChangeCallback import com.android.permissioncontroller.permission.ui.model.ManagePermissionsViewModel import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageControlPreferenceUtils -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageViewModelFactoryLegacy -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageViewModelLegacy -import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageViewModelLegacy.PermissionGroupWithUsageCount +import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageViewModelFactoryLegacy +import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageViewModelLegacy +import com.android.permissioncontroller.permission.ui.legacy.PermissionUsageViewModelLegacy.PermissionGroupWithUsageCount import com.android.permissioncontroller.permission.utils.Utils @RequiresApi(Build.VERSION_CODES.S) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java index bacf50144..946484626 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java @@ -74,13 +74,13 @@ import androidx.lifecycle.ViewModelProvider; import com.android.modules.utils.build.SdkLevel; import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData.FullStoragePackageState; -import com.android.permissioncontroller.permission.ui.AdvancedConfirmDialogArgs; import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonState; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ChangeRequest; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModelFactory; +import com.android.permissioncontroller.permission.ui.v33.AdvancedConfirmDialogArgs; import com.android.permissioncontroller.permission.utils.KotlinUtils; import com.android.permissioncontroller.permission.utils.Utils; import com.android.settingslib.RestrictedLockUtils; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java index 90d7204cf..220507426 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java @@ -53,6 +53,7 @@ import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage; import com.android.permissioncontroller.permission.model.v31.PermissionUsages; import com.android.permissioncontroller.permission.ui.Category; import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity; +import com.android.permissioncontroller.permission.ui.handheld.v31.CardViewPreference; import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel; import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModelFactory; import com.android.permissioncontroller.permission.utils.KotlinUtils; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/CardViewPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/CardViewPreference.java index 6c76d906b..008813a83 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/CardViewPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/CardViewPreference.java @@ -14,13 +14,15 @@ * limitations under the License. */ -package com.android.permissioncontroller.permission.ui.handheld; +package com.android.permissioncontroller.permission.ui.handheld.v31; import android.content.Context; import android.content.Intent; +import android.os.Build; import android.view.View; import android.widget.Button; +import androidx.annotation.RequiresApi; import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; @@ -30,6 +32,7 @@ import com.android.permissioncontroller.R; /** * A Preference representing a banner message represented as a CardView */ +@RequiresApi(Build.VERSION_CODES.S) public class CardViewPreference extends Preference { private String mAction; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFooterPreference.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFooterPreference.kt index f3abc7619..88b5ebe87 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFooterPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFooterPreference.kt @@ -18,7 +18,6 @@ package com.android.permissioncontroller.permission.ui.handheld.v34 import android.content.Context import android.text.SpannableString -import android.text.method.LinkMovementMethod import android.text.style.ClickableSpan import android.util.AttributeSet import android.view.View @@ -26,6 +25,7 @@ import android.widget.TextView import androidx.preference.Preference import androidx.preference.PreferenceViewHolder import com.android.permissioncontroller.R +import com.android.permissioncontroller.permission.compat.LinkMovementMethodCompat /** A preference for a footer with an icon and a link. */ class AppDataSharingUpdatesFooterPreference : Preference { @@ -80,7 +80,7 @@ class AppDataSharingUpdatesFooterPreference : Preference { footerLinkView?.let { it.visibility = if (onFooterLinkClick == null) View.GONE else View.VISIBLE it.text = footerLinkText - it.movementMethod = LinkMovementMethod.getInstance() + it.movementMethod = LinkMovementMethodCompat.getInstance() } super.onBindViewHolder(holder) } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/PermissionRationaleViewHandlerImpl.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/PermissionRationaleViewHandlerImpl.kt index 7504ae7d4..3998ca141 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/PermissionRationaleViewHandlerImpl.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/PermissionRationaleViewHandlerImpl.kt @@ -21,7 +21,6 @@ import android.content.Context import android.content.res.ColorStateList import android.os.Build import android.os.Bundle -import android.text.method.LinkMovementMethod import android.transition.ChangeBounds import android.transition.TransitionManager import android.view.Gravity @@ -29,13 +28,13 @@ import android.view.LayoutInflater import android.view.View import android.view.View.OnClickListener import android.view.ViewGroup -import android.view.WindowManager import android.view.animation.AnimationUtils import android.widget.Button import android.widget.LinearLayout import android.widget.TextView import androidx.annotation.RequiresApi import com.android.permissioncontroller.R +import com.android.permissioncontroller.permission.compat.LinkMovementMethodCompat import com.android.permissioncontroller.permission.ui.v34.PermissionRationaleViewHandler import com.android.permissioncontroller.permission.ui.v34.PermissionRationaleViewHandler.Result.Companion.CANCELLED @@ -146,16 +145,16 @@ class PermissionRationaleViewHandlerImpl( titleView = rootView.findViewById(R.id.permission_rationale_title) dataSharingSourceMessageView = rootView.findViewById(R.id.data_sharing_source_message) - dataSharingSourceMessageView!!.movementMethod = LinkMovementMethod.getInstance() + dataSharingSourceMessageView!!.movementMethod = LinkMovementMethodCompat.getInstance() purposeTitleView = rootView.findViewById(R.id.purpose_title) purposeMessageView = rootView.findViewById(R.id.purpose_message) learnMoreMessageView = rootView.findViewById(R.id.learn_more_message) - learnMoreMessageView!!.movementMethod = LinkMovementMethod.getInstance() + learnMoreMessageView!!.movementMethod = LinkMovementMethodCompat.getInstance() settingsMessageView = rootView.findViewById(R.id.settings_message) - settingsMessageView!!.movementMethod = LinkMovementMethod.getInstance() + settingsMessageView!!.movementMethod = LinkMovementMethodCompat.getInstance() if (!shouldShowSettingsSection) { val settingsSectionView: ViewGroup? = rootView.findViewById(R.id.settings_section) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModelLegacy.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt index 667667200..e219153f3 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModelLegacy.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt @@ -15,7 +15,7 @@ */ @file:Suppress("DEPRECATION") -package com.android.permissioncontroller.permission.ui.model.v31 +package com.android.permissioncontroller.permission.ui.legacy import android.Manifest import android.app.AppOpsManager @@ -44,7 +44,7 @@ import com.android.permissioncontroller.permission.utils.KotlinUtils import com.android.permissioncontroller.permission.utils.KotlinUtils.getPackageLabel import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.utils.StringUtils -import com.android.permissioncontroller.permission.utils.SubattributionUtils +import com.android.permissioncontroller.permission.utils.v31.SubattributionUtils import com.android.permissioncontroller.permission.utils.Utils import java.time.Instant import java.util.concurrent.TimeUnit diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModelLegacy.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageViewModelLegacy.kt index ff21a7216..d0e751f7d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModelLegacy.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageViewModelLegacy.kt @@ -15,7 +15,7 @@ */ @file:Suppress("DEPRECATION") -package com.android.permissioncontroller.permission.ui.model.v31 +package com.android.permissioncontroller.permission.ui.legacy import android.Manifest import android.app.LoaderManager diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt index ddae7c0ee..91b6de077 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt @@ -63,7 +63,7 @@ import com.android.permissioncontroller.permission.model.livedatatypes.LightAppP import com.android.permissioncontroller.permission.model.livedatatypes.LightPermission import com.android.permissioncontroller.permission.service.PermissionChangeStorageImpl import com.android.permissioncontroller.permission.service.v33.PermissionDecisionStorageImpl -import com.android.permissioncontroller.permission.ui.AdvancedConfirmDialogArgs +import com.android.permissioncontroller.permission.ui.v33.AdvancedConfirmDialogArgs import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.ALLOW import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.ALLOW_ALWAYS import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.ALLOW_FOREGROUND 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 7cd1ea325..3a689fd72 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt @@ -118,7 +118,7 @@ import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT import com.android.permissioncontroller.permission.ui.v34.PermissionRationaleActivity -import com.android.permissioncontroller.permission.utils.AdminRestrictedPermissionsUtils +import com.android.permissioncontroller.permission.utils.v31.AdminRestrictedPermissionsUtils import com.android.permissioncontroller.permission.utils.KotlinUtils import com.android.permissioncontroller.permission.utils.KotlinUtils.getDefaultPrecision import com.android.permissioncontroller.permission.utils.KotlinUtils.grantBackgroundRuntimePermissions @@ -302,8 +302,9 @@ class GrantPermissionsViewModel( appPermGroup.permissions[perm]?.isGrantedIncludingAppOp == true && appPermGroup.permissions[perm]?.isRevokeWhenRequested == false } - if (allAffectedGranted) { - groupStates[key]!!.state = STATE_ALLOWED + if (allAffectedGranted || isCompatStorageGrant(appPermGroup)) { + groupStates[key] = GroupState(appPermGroup, state.isBackground, + state.affectedPermissions, STATE_ALLOWED) } } } else { @@ -804,6 +805,11 @@ class GrantPermissionsViewModel( // then skip the request return STATE_SKIPPED } + // If the "false grant" for apps that don't support the permission has been applied, + // treat the permission as already granted + if (isCompatStorageGrant(group)) { + return STATE_ALLOWED + } } val isBackground = perm in group.backgroundPermNames @@ -876,7 +882,7 @@ class GrantPermissionsViewModel( * ACCESS_MEDIA_LOCATION granted */ private fun isPartialStorageGrant(group: LightAppPermGroup): Boolean { - if (!KotlinUtils.isPhotoPickerPromptEnabled() || group.permGroupName != READ_MEDIA_VISUAL) { + if (group.permGroupName != READ_MEDIA_VISUAL || !KotlinUtils.isPhotoPickerPromptEnabled()) { return false } @@ -886,6 +892,20 @@ class GrantPermissionsViewModel( } } + /** + * A compat storage grant is provided when the user selects "select photos" on an app that does + * not explicitly request the READ_MEDIA_VISUAL_USER_SELECTED permission. It grants RMVUS, and + * applies the "revoked compat" state to all other permissions in the group. + */ + private fun isCompatStorageGrant(group: LightAppPermGroup): Boolean { + if (group.permGroupName != READ_MEDIA_VISUAL || !KotlinUtils.isPhotoPickerPromptEnabled()) { + return false + } + return group.permissions[READ_MEDIA_VISUAL_USER_SELECTED] + ?.isGrantedIncludingAppOp == true && + group.permissions.values.any { it.isCompatRevoked } + } + private fun getStateFromPolicy(perm: String, group: LightAppPermGroup): Int { val isBackground = perm in group.backgroundPermNames var skipGroup = false diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt index 00df47d3e..7633859fb 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt @@ -55,7 +55,7 @@ import com.android.permissioncontroller.permission.ui.handheld.v31.shouldShowSub import com.android.permissioncontroller.permission.utils.KotlinUtils import com.android.permissioncontroller.permission.utils.KotlinUtils.getPackageLabel import com.android.permissioncontroller.permission.utils.PermissionMapping -import com.android.permissioncontroller.permission.utils.SubattributionUtils +import com.android.permissioncontroller.permission.utils.v31.SubattributionUtils import com.android.permissioncontroller.permission.utils.Utils import java.time.Instant import java.util.Objects diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v34/PermissionRationaleViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v34/PermissionRationaleViewModel.kt index caf073632..3d205a270 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v34/PermissionRationaleViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v34/PermissionRationaleViewModel.kt @@ -126,7 +126,7 @@ class PermissionRationaleViewModel( val purposes = PermissionMapping.getSafetyLabelSharingPurposesForGroup( safetyLabelInfo.safetyLabel, permissionGroupName) - if (isStale) { + if (value == null) { logPermissionRationaleDialogViewed(purposes) } value = diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java index a5961dcec..e2df47009 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java @@ -66,13 +66,13 @@ import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData.FullStoragePackageState; import com.android.permissioncontroller.permission.model.AppPermissionGroup; import com.android.permissioncontroller.permission.model.AppPermissions; -import com.android.permissioncontroller.permission.ui.AdvancedConfirmDialogArgs; import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonState; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ChangeRequest; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModelFactory; +import com.android.permissioncontroller.permission.ui.v33.AdvancedConfirmDialogArgs; import com.android.permissioncontroller.permission.utils.KotlinUtils; import com.android.permissioncontroller.permission.utils.Utils; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/AdvancedConfirmDialogArgs.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/v33/AdvancedConfirmDialogArgs.kt index 6b26c7e47..b841f3aeb 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/AdvancedConfirmDialogArgs.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/v33/AdvancedConfirmDialogArgs.kt @@ -1,28 +1,27 @@ /* * Copyright (C) 2022 The Android Open Source Project * - * Licensed under the Apache License; - * Version 2.0 (the "License"); + * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.permissioncontroller.permission.ui +package com.android.permissioncontroller.permission.ui.v33 +import android.os.Build import androidx.annotation.DrawableRes +import androidx.annotation.RequiresApi import androidx.annotation.StringRes import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel +@RequiresApi(Build.VERSION_CODES.TIRAMISU) data class AdvancedConfirmDialogArgs( @DrawableRes val iconId: Int = 0, @StringRes val titleId: Int = 0, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java b/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java index 7ccdd6465..b0aaac9ea 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java @@ -660,10 +660,6 @@ public final class Utils { return typedValue.resourceId; } - public static List<ApplicationInfo> getAllInstalledApplications(Context context) { - return context.getPackageManager().getInstalledApplications(0); - } - /** * Is the group or background group user sensitive? * diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/AdminRestrictedPermissionsUtils.java b/PermissionController/src/com/android/permissioncontroller/permission/utils/v31/AdminRestrictedPermissionsUtils.java index 4944093ac..e9d68c9c6 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/AdminRestrictedPermissionsUtils.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/v31/AdminRestrictedPermissionsUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.permission.utils; +package com.android.permissioncontroller.permission.utils.v31; import android.Manifest; import android.app.admin.DevicePolicyManager; @@ -24,6 +24,7 @@ import android.os.UserManager; import android.util.ArraySet; import com.android.modules.utils.build.SdkLevel; +import com.android.permissioncontroller.permission.utils.Utils; /** * A class for dealing with permissions that the admin may not grant in certain configurations. diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/SubattributionUtils.java b/PermissionController/src/com/android/permissioncontroller/permission/utils/v31/SubattributionUtils.java index 2785eca74..8919953b4 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/SubattributionUtils.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/v31/SubattributionUtils.java @@ -14,9 +14,8 @@ * limitations under the License. */ -package com.android.permissioncontroller.permission.utils; +package com.android.permissioncontroller.permission.utils.v31; -import android.annotation.SuppressLint; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.Attribution; @@ -25,6 +24,7 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.os.Build; +import androidx.annotation.ChecksSdkIntAtLeast; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; @@ -42,6 +42,7 @@ public class SubattributionUtils { /** * Returns true if the app supports subattribution. */ + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S) public static boolean isSubattributionSupported(Context context, ApplicationInfo appInfo) { if (!SdkLevel.isAtLeastS()) { return false; @@ -50,6 +51,7 @@ public class SubattributionUtils { } /** Returns whether the provided package supports subattribution. */ + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S) public static boolean isSubattributionSupported(LightPackageInfo lightPackageInfo) { return SdkLevel.isAtLeastS() && lightPackageInfo.getAreAttributionsUserVisible(); } @@ -59,7 +61,6 @@ public class SubattributionUtils { * {@code null} otherwise. */ @Nullable - @SuppressLint("NewApi") // isSubattributionSupported checks api level public static Map<Integer, String> getAttributionLabels(Context context, PackageInfo pkgInfo) { if (!isSubattributionSupported(context, pkgInfo.applicationInfo)) { return null; @@ -72,7 +73,6 @@ public class SubattributionUtils { * {@code null} otherwise. */ @Nullable - @SuppressLint("NewApi") // isSubattributionSupported checks api level public static Map<Integer, String> getAttributionLabels(Context context, ApplicationInfo appInfo) { if (!isSubattributionSupported(context, appInfo)) { @@ -114,7 +114,6 @@ public class SubattributionUtils { /** Returns the attribution label map for the package if the app supports subattribution. */ @Nullable - @SuppressLint("NewApi") // isSubattributionSupported checks api level public static Map<Integer, String> getAttributionLabels(Context context, LightPackageInfo lightPackageInfo) { if (!isSubattributionSupported(lightPackageInfo)) { diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/SafetyCenterReceiver.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/SafetyCenterReceiver.kt index 874fe942e..885b8ea86 100644 --- a/PermissionController/src/com/android/permissioncontroller/privacysources/SafetyCenterReceiver.kt +++ b/PermissionController/src/com/android/permissioncontroller/privacysources/SafetyCenterReceiver.kt @@ -39,8 +39,6 @@ import com.android.permissioncontroller.permission.utils.Utils import com.android.permissioncontroller.privacysources.WorkPolicyInfo.Companion.WORK_POLICY_INFO_SOURCE_ID import com.android.permissioncontroller.privacysources.v34.AppDataSharingUpdatesPrivacySource import com.android.permissioncontroller.privacysources.v34.AppDataSharingUpdatesPrivacySource.Companion.APP_DATA_SHARING_UPDATES_SOURCE_ID -import com.android.permissioncontroller.privacysources.v34.HealthConnectPrivacySource -import com.android.permissioncontroller.privacysources.v34.HealthConnectPrivacySource.Companion.HEALTH_CONNECT_SOURCE_ID import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.Default @@ -59,7 +57,6 @@ private fun createMapOfSourceIdsToSources(context: Context): Map<String, Privacy if (SdkLevel.isAtLeastU()) { sourceMap[APP_DATA_SHARING_UPDATES_SOURCE_ID] = AppDataSharingUpdatesPrivacySource() - sourceMap[HEALTH_CONNECT_SOURCE_ID] = HealthConnectPrivacySource() } return sourceMap diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/v34/HealthConnectPrivacySource.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/v34/HealthConnectPrivacySource.kt deleted file mode 100644 index f05b061c7..000000000 --- a/PermissionController/src/com/android/permissioncontroller/privacysources/v34/HealthConnectPrivacySource.kt +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2023 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.privacysources.v34 - -import android.app.PendingIntent -import android.app.PendingIntent.FLAG_IMMUTABLE -import android.app.PendingIntent.FLAG_UPDATE_CURRENT -import android.content.Context -import android.content.Intent -import android.os.Build -import android.safetycenter.SafetyCenterManager -import android.safetycenter.SafetyEvent -import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_DEVICE_REBOOTED -import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED -import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED -import android.safetycenter.SafetySourceData -import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED -import android.safetycenter.SafetySourceStatus -import androidx.annotation.RequiresApi -import com.android.permissioncontroller.R -import com.android.permissioncontroller.permission.utils.Utils -import com.android.permissioncontroller.privacysources.PrivacySource -import com.android.permissioncontroller.privacysources.SafetyCenterReceiver.RefreshEvent -import com.android.permissioncontroller.privacysources.SafetyCenterReceiver.RefreshEvent.EVENT_DEVICE_REBOOTED -import com.android.permissioncontroller.privacysources.SafetyCenterReceiver.RefreshEvent.EVENT_REFRESH_REQUESTED -import com.android.permissioncontroller.privacysources.SafetyCenterReceiver.RefreshEvent.UNKNOWN - -/** - * Privacy source providing the Health Connect entry to Safety Center. - * - * The content of the Health Connect is static, however the entry should only be displayed if the - * Health Connect Permission UI Enabled feature is enabled. - */ -@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) -class HealthConnectPrivacySource : PrivacySource { - - override val shouldProcessProfileRequest: Boolean = false - - override fun safetyCenterEnabledChanged(context: Context, enabled: Boolean) { - // Do nothing. - } - - override fun rescanAndPushSafetyCenterData( - context: Context, - intent: Intent, - refreshEvent: RefreshEvent - ) { - val safetyCenterManager: SafetyCenterManager = - Utils.getSystemServiceSafe(context, SafetyCenterManager::class.java) - - val safetySourceData = - if (Utils.isHealthPermissionUiEnabled()) { - SafetySourceData.Builder() - .setStatus( - SafetySourceStatus.Builder( - context.getString(R.string.health_connect_title), - context.getString(R.string.health_connect_summary), - SEVERITY_LEVEL_UNSPECIFIED - ) - .setPendingIntent( - PendingIntent.getActivity( - context, - /* requestCode= */ 0, - Intent(HEALTH_CONNECT_INTENT_ACTION), - FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE - ) - ) - .build(), - ) - .build() - } else { - null - } - - safetyCenterManager.setSafetySourceData( - HEALTH_CONNECT_SOURCE_ID, - safetySourceData, - createSafetyEventForHealthConnect(refreshEvent, intent) - ) - } - - private fun createSafetyEventForHealthConnect( - refreshEvent: RefreshEvent, - intent: Intent - ): SafetyEvent { - return when (refreshEvent) { - EVENT_REFRESH_REQUESTED -> { - val refreshBroadcastId = - intent.getStringExtra( - SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID - ) - SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED) - .setRefreshBroadcastId(refreshBroadcastId) - .build() - } - EVENT_DEVICE_REBOOTED -> { - SafetyEvent.Builder(SAFETY_EVENT_TYPE_DEVICE_REBOOTED).build() - } - UNKNOWN -> { - SafetyEvent.Builder(SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build() - } - } - } - - /** Companion object for [HealthConnectPrivacySource]. */ - companion object { - /** Source id for safety center source for health connect. */ - const val HEALTH_CONNECT_SOURCE_ID = "AndroidHealthConnect" - const val HEALTH_CONNECT_INTENT_ACTION = - "android.health.connect.action.HEALTH_HOME_SETTINGS" - } -} diff --git a/PermissionController/tests/inprocess/Android.bp b/PermissionController/tests/inprocess/Android.bp index 0cc355daa..78c767f1d 100644 --- a/PermissionController/tests/inprocess/Android.bp +++ b/PermissionController/tests/inprocess/Android.bp @@ -33,7 +33,10 @@ android_test { target_sdk_version: "30", min_sdk_version: "30", - srcs: ["src/**/*.kt"], + srcs: [ + "src/**/*.kt", + "src/com/android/permissioncontroller/permission/compat/LinkMovementMethodCompatTest.java", + ], libs: [ "android.test.base", @@ -46,6 +49,7 @@ android_test { "androidx.test.ext.junit", "androidx.test.uiautomator_uiautomator", "compatibility-device-util-axt", + "kotlin-test", "permission-test-util-lib", ], diff --git a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/compat/LinkMovementMethodCompatTest.java b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/compat/LinkMovementMethodCompatTest.java new file mode 100644 index 000000000..b4b18dbbe --- /dev/null +++ b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/compat/LinkMovementMethodCompatTest.java @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2023 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.compat; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import android.app.Activity; +import android.content.Context; +import android.os.SystemClock; +import android.text.Selection; +import android.text.Spannable; +import android.text.Spanned; +import android.text.method.MovementMethod; +import android.text.style.ClickableSpan; +import android.util.TypedValue; +import android.view.MotionEvent; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.test.annotation.UiThreadTest; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.rule.ActivityTestRule; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +/** + * Test for {@link LinkMovementMethodCompat} without using Mockito, which is unavailable for + * in-process tests. + * + * @see android.text.method.cts.LinkMovementMethodTest + */ +public class LinkMovementMethodCompatTest { + private static final String CONTENT = "clickable\nunclickable\nclickable"; + + private Activity mActivity; + private LinkMovementMethodCompat mMethod; + private TextView mView; + private Spannable mSpannable; + private MockClickableSpan mClickable0; + private MockClickableSpan mClickable1; + + @Rule + public ActivityTestRule<Activity> mActivityRule = new ActivityTestRule<>(Activity.class); + + @Before + public void setup() throws Throwable { + mActivity = mActivityRule.getActivity(); + mMethod = new LinkMovementMethodCompat(); + + // Set the content view with a text view which contains 3 lines, + mActivityRule.runOnUiThread(() -> mView = new TextViewNoIme(mActivity)); + mView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10); + mView.setText(CONTENT, TextView.BufferType.SPANNABLE); + + mActivityRule.runOnUiThread(() -> mActivity.setContentView(mView)); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + + mSpannable = (Spannable) mView.getText(); + // make first line clickable + mClickable0 = markClickable(0, CONTENT.indexOf('\n')); + // make last line clickable + mClickable1 = markClickable(CONTENT.lastIndexOf('\n'), CONTENT.length()); + } + + @Test + public void testConstructor() { + new LinkMovementMethodCompat(); + } + + @Test + public void testGetInstance() { + MovementMethod method0 = LinkMovementMethodCompat.getInstance(); + assertTrue(method0 instanceof LinkMovementMethodCompat); + + MovementMethod method1 = LinkMovementMethodCompat.getInstance(); + assertNotNull(method1); + assertSame(method0, method1); + } + + @UiThreadTest + @Test + public void testOnTouchEvent() { + assertSelection(mSpannable, -1); + + // press on first line (Clickable) + assertTrue(pressOnLine(0)); + assertSelectClickableLeftToRight(mSpannable, mClickable0); + + // release on first line + mClickable0.clearClickCount(); + assertTrue(releaseOnLine(0)); + mClickable0.assertClickCount(1); + + // press on second line (unclickable) + assertSelectClickableLeftToRight(mSpannable, mClickable0); + // just clear selection + pressOnLine(1); + assertSelection(mSpannable, -1); + + // press on last line (Clickable) + assertTrue(pressOnLine(2)); + assertSelectClickableLeftToRight(mSpannable, mClickable1); + + // release on last line + mClickable1.clearClickCount(); + assertTrue(releaseOnLine(2)); + mClickable1.assertClickCount(1); + + // release on second line (unclickable) + assertSelectClickableLeftToRight(mSpannable, mClickable1); + // just clear selection + releaseOnLine(1); + assertSelection(mSpannable, -1); + } + + @UiThreadTest + @Test + public void testOnTouchEvent_outsideLineBounds() { + assertSelection(mSpannable, -1); + + // press on first line (clickable) + assertTrue(pressOnLine(0)); + assertSelectClickableLeftToRight(mSpannable, mClickable0); + + // release above first line + mClickable0.clearClickCount(); + float x = (mView.getLayout().getLineLeft(0) + mView.getLayout().getLineRight(0)) / 2f; + float y = -1f; + assertFalse(performMotionAtPoint(x, y, MotionEvent.ACTION_UP)); + mClickable0.assertClickCount(0); + + // press on first line (clickable) + assertTrue(pressOnLine(0)); + assertSelectClickableLeftToRight(mSpannable, mClickable0); + + // release to left of first line + mClickable0.clearClickCount(); + x = mView.getLayout().getLineLeft(0) - 1f; + y = (mView.getLayout().getLineTop(0) + mView.getLayout().getLineBottom(0)) / 2f; + assertFalse(performMotionAtPoint(x, y, MotionEvent.ACTION_UP)); + mClickable0.assertClickCount(0); + + // press on first line (clickable) + assertTrue(pressOnLine(0)); + assertSelectClickableLeftToRight(mSpannable, mClickable0); + + // release to right of first line + mClickable0.clearClickCount(); + x = mView.getLayout().getLineRight(0) + 1f; + y = (mView.getLayout().getLineTop(0) + mView.getLayout().getLineBottom(0)) / 2f; + assertFalse(performMotionAtPoint(x, y, MotionEvent.ACTION_UP)); + mClickable0.assertClickCount(0); + + // press on last line (clickable) + assertTrue(pressOnLine(2)); + assertSelectClickableLeftToRight(mSpannable, mClickable1); + + // release below last line + mClickable1.clearClickCount(); + x = (mView.getLayout().getLineLeft(0) + mView.getLayout().getLineRight(0)) / 2f; + y = mView.getLayout().getHeight() + 1f; + assertFalse(performMotionAtPoint(x, y, MotionEvent.ACTION_UP)); + mClickable1.assertClickCount(0); + } + + private MockClickableSpan markClickable(final int start, final int end) throws Throwable { + final MockClickableSpan clickableSpan = new MockClickableSpan(); + mActivityRule.runOnUiThread(() -> mSpannable.setSpan(clickableSpan, start, end, + Spanned.SPAN_MARK_MARK)); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + return clickableSpan; + } + private boolean performMotionAtPoint(float x, float y, int action) { + long now = SystemClock.uptimeMillis(); + return mMethod.onTouchEvent(mView, mSpannable, + MotionEvent.obtain(now, now, action, x, y, 0)); + } + + private boolean performMotionOnLine(int line, int action) { + float x = (mView.getLayout().getLineLeft(line) + mView.getLayout().getLineRight(line)) / 2f; + float y = (mView.getLayout().getLineTop(line) + mView.getLayout().getLineBottom(line)) / 2f; + return performMotionAtPoint(x, y, action); + } + + private boolean pressOnLine(int line) { + return performMotionOnLine(line, MotionEvent.ACTION_DOWN); + } + + private boolean releaseOnLine(int line) { + return performMotionOnLine(line, MotionEvent.ACTION_UP); + } + + private void assertSelection(Spannable spannable, int start, int end) { + assertEquals(start, Selection.getSelectionStart(spannable)); + assertEquals(end, Selection.getSelectionEnd(spannable)); + } + + private void assertSelection(Spannable spannable, int position) { + assertSelection(spannable, position, position); + } + + private void assertSelectClickableLeftToRight(Spannable spannable, + ClickableSpan clickableSpan) { + assertSelection(spannable, spannable.getSpanStart(clickableSpan), + spannable.getSpanEnd(clickableSpan)); + } + + public static class TextViewNoIme extends TextView { + public TextViewNoIme(@NonNull Context context) { + super(context); + } + + @Override + public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + return null; + } + } + + public static class MockClickableSpan extends ClickableSpan { + private int mClickCount = 0; + + @Override + public void onClick(@NonNull View widget) { + ++mClickCount; + } + + public void assertClickCount(int expectedClickCount) { + assertEquals(expectedClickCount, mClickCount); + } + + public void clearClickCount() { + mClickCount = 0; + } + } +} diff --git a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/UtilsTest.kt b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/UtilsTest.kt index c53f94195..9cd6da588 100644 --- a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/UtilsTest.kt +++ b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/UtilsTest.kt @@ -17,23 +17,199 @@ package com.android.permissioncontroller.permission.util import android.Manifest.permission.READ_CONTACTS +import android.Manifest.permission_group.ACTIVITY_RECOGNITION +import android.Manifest.permission_group.CALENDAR +import android.Manifest.permission_group.CALL_LOG +import android.Manifest.permission_group.CAMERA import android.Manifest.permission_group.CONTACTS +import android.Manifest.permission_group.LOCATION +import android.Manifest.permission_group.MICROPHONE +import android.Manifest.permission_group.NEARBY_DEVICES +import android.Manifest.permission_group.PHONE +import android.Manifest.permission_group.READ_MEDIA_AURAL +import android.Manifest.permission_group.READ_MEDIA_VISUAL +import android.Manifest.permission_group.SENSORS +import android.Manifest.permission_group.SMS +import android.Manifest.permission_group.STORAGE +import android.Manifest.permission_group.UNDEFINED +import android.content.ComponentName import android.content.Context import android.content.Intent +import android.content.SharedPreferences +import android.content.pm.PackageManager.NameNotFoundException import android.content.res.Resources import androidx.test.platform.app.InstrumentationRegistry import com.android.permissioncontroller.Constants.EXTRA_SESSION_ID import com.android.permissioncontroller.Constants.INVALID_SESSION_ID +import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.utils.Utils +import com.android.permissioncontroller.privacysources.WorkPolicyInfo import com.google.common.truth.Truth.assertThat import org.junit.Test - +import kotlin.test.assertFailsWith class UtilsTest { - private val context = InstrumentationRegistry.getInstrumentation().context as Context + private val context = InstrumentationRegistry.getInstrumentation().targetContext as Context + + @Test + fun getAbsoluteTimeString_zero_returnsNull() { + assertThat(Utils.getAbsoluteTimeString(context, 0)).isNull() + } + + @Test + fun getAbsoluteTimeString_currentTime_returnsTimeFormatString() { + val time = Utils.getAbsoluteTimeString(context, System.currentTimeMillis()) + assertThat(time).isNotNull() + if (time != null) { + if (time.contains(":")) { + val times = time.split(":") + assertThat(times.size).isEqualTo(2) + val isTime = times[1].contains("am", true) || times[1].contains("pm", true) + assertThat(isTime).isTrue() + } else { + assertThat(time.contains(".")).isTrue() + val times = time.split(".") + assertThat(times.size).isEqualTo(3) + } + } + } + + @Test + fun getAbsoluteTimeString_previousDateTime_returnsDateFormatString() { + val lastAccessTime = 1680739840723L + val time = Utils.getAbsoluteTimeString(context, lastAccessTime) + assertThat(time).isEqualTo("Apr 5, 2023") + } + + @Test + fun getBlockedIcon_invalidGroupName_returnsMinusOne() { + assertThat(Utils.getBlockedIcon(INVALID_GROUP_NAME)).isEqualTo(-1) + } + + @Test + fun getBlockedIcon_validGroupName() { + assertThat(Utils.getBlockedIcon(CAMERA)).isEqualTo(R.drawable.ic_camera_blocked) + } + + @Test + fun getBlockedTitle_invalidGroupName_returnsMinusOne() { + assertThat(Utils.getBlockedTitle(INVALID_GROUP_NAME)).isEqualTo(-1) + } + @Test + fun getBlockedTitle_validGroupName() { + assertThat(Utils.getBlockedTitle(CAMERA)).isEqualTo(R.string.blocked_camera_title) + } + + @Test + fun getDeviceProtectedSharedPreferences() { + assertThat(Utils.getDeviceProtectedSharedPreferences(context)) + .isInstanceOf(SharedPreferences::class.java) + } + + @Test + fun getEnterpriseString() { + assertThat(Utils.getEnterpriseString(context, WorkPolicyInfo.WORK_POLICY_TITLE, + R.string.work_policy_title)).isInstanceOf(String::class.java) + } + + @Test + fun getOneTimePermissionsTimeout_returnsNonNegativeTimeout() { + assertThat(Utils.getOneTimePermissionsTimeout()).isGreaterThan(0L) + } + + @Test + fun getOneTimePermissionsKilledDelaySelfRevoked() { + assertThat(Utils.getOneTimePermissionsKilledDelay(true)).isEqualTo(0) + } + + @Test + fun getOneTimePermissionsKilledDelayNonSelfRevoked() { + assertThat(Utils.getOneTimePermissionsKilledDelay(false)).isAtLeast(0L) + } @Test - fun assertGetOrGenerateSessionIdExpectedSessionId() { + fun getPackageInfoForComponentName_NonExistComponent_throwsNameNotFoundException() { + val testComponent = ComponentName("com.test.package", "TestClass") + assertFailsWith<NameNotFoundException> { + Utils.getPackageInfoForComponentName(context, testComponent) + } + } + + @Test + fun getPermissionGroupDescriptionString_undefinedPermissionGroup() { + val description = "test permission group description" + val resultString = + context.getString(R.string.permission_description_summary_generic, description) + assertThat(Utils.getPermissionGroupDescriptionString(context, UNDEFINED, description)) + .isEqualTo(resultString) + } + + @Test + fun getPermissionGroupDescriptionString_validPermissionGroup() { + val permissionGroupNames = listOf(ACTIVITY_RECOGNITION, CALENDAR, CALL_LOG, + CAMERA, CONTACTS, LOCATION, MICROPHONE, NEARBY_DEVICES, PHONE, READ_MEDIA_AURAL, + READ_MEDIA_VISUAL, SENSORS, SMS, STORAGE) + for (permissionGroupName in permissionGroupNames) { + assertThat(Utils.getPermissionGroupDescriptionString(context, permissionGroupName, "")) + .isNotNull() + } + } + + @Test + fun getPermissionLastAccessSummaryTimestamp_lastAccessSummaryTimestampIsNull() { + val result = Utils.getPermissionLastAccessSummaryTimestamp(null, context, LOCATION) + assertThat(result.first).isEqualTo("") + assertThat(result.second).isEqualTo(Utils.NOT_IN_LAST_7D) + assertThat(result.third).isEqualTo("") + } + + @Test + fun getPermissionLastAccessSummaryTimestamp_sensorDataPermission_lastAccessSummaryTimestampIsToday() { + val result = Utils.getPermissionLastAccessSummaryTimestamp(System.currentTimeMillis(), + context, LOCATION) + assertThat(result.first).isNotEmpty() + assertThat(result.second).isEqualTo(Utils.LAST_24H_SENSOR_TODAY) + assertThat(result.third).isNotEmpty() + } + + @Test + fun getPermissionLastAccessSummaryTimestamp_sensorDataPermission_lastAccessSummaryTimestampIsYesterday() { + val result = Utils.getPermissionLastAccessSummaryTimestamp( + System.currentTimeMillis() - 24 * 60 * 60 * 1000, context, LOCATION) + assertThat(result.first).isNotEmpty() + assertThat(result.second).isEqualTo(Utils.LAST_24H_SENSOR_YESTERDAY) + assertThat(result.third).isNotEmpty() + } + + @Test + fun getPermissionLastAccessSummaryTimestamp_sensorDataPermission_lastAccessSummaryTimestampIsLast7Days() { + val result = Utils.getPermissionLastAccessSummaryTimestamp( + System.currentTimeMillis() - 6 * 24 * 60 * 60 * 1000, context, LOCATION) + assertThat(result.first).isNotEmpty() + assertThat(result.second).isEqualTo(Utils.LAST_7D_SENSOR) + assertThat(result.third).isNotEmpty() + } + + @Test + fun getPermissionLastAccessSummaryTimestamp_nonSensorDataPermission_lastAccessSummaryTimestampIsLast24Hrs() { + val result = Utils.getPermissionLastAccessSummaryTimestamp( + System.currentTimeMillis(), context, STORAGE) + assertThat(result.first).isNotEmpty() + assertThat(result.second).isEqualTo(Utils.LAST_24H_CONTENT_PROVIDER) + assertThat(result.third).isNotEmpty() + } + + @Test + fun getPermissionLastAccessSummaryTimestamp_nonSensorDataPermission_lastAccessSummaryTimestampIs7Days() { + val result = Utils.getPermissionLastAccessSummaryTimestamp( + System.currentTimeMillis() - 5 * 60 * 60 * 24 * 1000, context, STORAGE) + assertThat(result.first).isNotEmpty() + assertThat(result.second).isEqualTo(Utils.LAST_7D_CONTENT_PROVIDER) + assertThat(result.third).isNotEmpty() + } + + @Test + fun getOrGenerateSessionId_validSessionId() { val intent = Intent() intent.putExtra(EXTRA_SESSION_ID, VALID_SESSION_ID) val sessionId = Utils.getOrGenerateSessionId(intent) @@ -41,15 +217,15 @@ class UtilsTest { } @Test - fun assertGetOrGenerateSessionIdRandomSessionId() { + fun getOrGenerateSessionId_invalidSessionId() { val intent = Intent() val sessionId = Utils.getOrGenerateSessionId(intent) assertThat(sessionId).isNotEqualTo(INVALID_SESSION_ID) } @Test - fun assertGetGroupPermissionInfosValidGroup() { - val permissionInfos = Utils.getGroupPermissionInfos(GROUP_NAME, context) + fun getGroupPermissionInfos_validGroupName_returnsGroupPermissions() { + val permissionInfos = Utils.getGroupPermissionInfos(CONTACTS, context) assertThat(permissionInfos).isNotNull() val permissions = mutableListOf<String>() for (permissionInfo in permissionInfos!!) { @@ -59,25 +235,38 @@ class UtilsTest { } @Test - fun assertGetGroupPermissionInfosInValidGroup() { + fun getGroupPermissionInfos_inValidGroup_returnsNull() { assertThat(Utils.getGroupPermissionInfos(INVALID_GROUP_NAME, context)).isNull() } @Test - fun assertGetColorResIdValidId() { + fun getGroupPermissionInfos_undefinedGroup_returnsAllSystemPermissions() { + val permissionInfos = Utils.getGroupPermissionInfos(UNDEFINED, context) + assertThat(permissionInfos).isNotNull() + } + + @Test + fun getGroupPermissionInfo_permissionName_returnsSamePermission() { + val permissionInfos = Utils.getGroupPermissionInfos(READ_CONTACTS, context) + assertThat(permissionInfos).isNotNull() + assertThat(permissionInfos!!.size).isEqualTo(1) + assertThat(permissionInfos[0].name).isEqualTo(READ_CONTACTS) + } + + @Test + fun getColorResId_validId_returnsNonZero() { assertThat(Utils.getColorResId(context, android.R.attr.colorPrimary)) .isNotEqualTo(Resources.ID_NULL) } @Test - fun assertGetColorResIdInValidId() { + fun getColorResId_inValidId_returnsZero() { assertThat(Utils.getColorResId(context, INVALID_ATTR_ID)).isEqualTo(Resources.ID_NULL) } companion object { private const val INVALID_ATTR_ID = 1000 private const val VALID_SESSION_ID = 10000L - private const val GROUP_NAME = CONTACTS private const val INVALID_GROUP_NAME = "invalid group name" } } diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/HealthConnectPrivacySourceTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/HealthConnectPrivacySourceTest.kt deleted file mode 100644 index c68f1cc67..000000000 --- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/HealthConnectPrivacySourceTest.kt +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) 2023 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.tests.mocking.privacysources - -import android.app.PendingIntent -import android.app.PendingIntent.FLAG_IMMUTABLE -import android.app.PendingIntent.FLAG_UPDATE_CURRENT -import android.content.Context -import android.content.ContextWrapper -import android.content.Intent -import android.os.Build -import android.provider.DeviceConfig -import android.provider.DeviceConfig.NAMESPACE_PRIVACY -import android.safetycenter.SafetyCenterManager -import android.safetycenter.SafetyCenterManager.ACTION_REFRESH_SAFETY_SOURCES -import android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID -import android.safetycenter.SafetyEvent -import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED -import android.safetycenter.SafetySourceData -import android.safetycenter.SafetySourceStatus -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SdkSuppress -import com.android.dx.mockito.inline.extended.ExtendedMockito -import com.android.permissioncontroller.PermissionControllerApplication -import com.android.permissioncontroller.permission.utils.Utils -import com.android.permissioncontroller.privacysources.SafetyCenterReceiver.RefreshEvent.EVENT_REFRESH_REQUESTED -import com.android.permissioncontroller.privacysources.v34.HealthConnectPrivacySource -import com.android.permissioncontroller.privacysources.v34.HealthConnectPrivacySource.Companion.HEALTH_CONNECT_SOURCE_ID -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.anyBoolean -import org.mockito.ArgumentMatchers.eq -import org.mockito.Mock -import org.mockito.Mockito.verify -import org.mockito.Mockito.verifyZeroInteractions -import org.mockito.Mockito.`when` as whenever -import org.mockito.MockitoAnnotations -import org.mockito.MockitoSession -import org.mockito.quality.Strictness - -/** Tests for [HealthConnectPrivacySource]. */ -@RunWith(AndroidJUnit4::class) -@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") -class HealthConnectPrivacySourceTest { - - private lateinit var mockitoSession: MockitoSession - private lateinit var healthConnectPrivacySource: HealthConnectPrivacySource - private lateinit var context: Context - @Mock lateinit var mockSafetyCenterManager: SafetyCenterManager - - @Before - fun setup() { - context = ApplicationProvider.getApplicationContext() - MockitoAnnotations.initMocks(this) - mockitoSession = - ExtendedMockito.mockitoSession() - .mockStatic(DeviceConfig::class.java) - .mockStatic(PermissionControllerApplication::class.java) - .mockStatic(Utils::class.java) - .strictness(Strictness.LENIENT) - .startMocking() - whenever( - Utils.getSystemServiceSafe( - any(ContextWrapper::class.java), - eq(SafetyCenterManager::class.java) - ) - ) - .thenReturn(mockSafetyCenterManager) - - healthConnectPrivacySource = HealthConnectPrivacySource() - } - - @After - fun cleanup() { - mockitoSession.finishMocking() - } - - @Test - fun safetyCenterEnabledChanged_enabled_doesNothing() { - healthConnectPrivacySource.safetyCenterEnabledChanged(context, true) - - verifyZeroInteractions(mockSafetyCenterManager) - } - - @Test - fun safetyCenterEnabledChanged_disabled_doesNothing() { - healthConnectPrivacySource.safetyCenterEnabledChanged(context, false) - - verifyZeroInteractions(mockSafetyCenterManager) - } - - @Test - fun rescanAndPushSafetyCenterData_healthPermissionUIEnabled_setsDataWithStatus() { - val refreshIntent = - Intent(ACTION_REFRESH_SAFETY_SOURCES) - .putExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID, REFRESH_ID) - - healthConnectPrivacySource.rescanAndPushSafetyCenterData( - context, - refreshIntent, - EVENT_REFRESH_REQUESTED - ) - - val expectedSafetySourceData = - if (Utils.isHealthPermissionUiEnabled()) { - SafetySourceData.Builder() - .setStatus( - SafetySourceStatus.Builder( - HEALTH_CONNECT_TITLE, - HEALTH_CONNECT_SUMMARY, - SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED - ) - .setPendingIntent( - PendingIntent.getActivity( - context, - /* requestCode= */ 0, - Intent(HEALTH_CONNECT_INTENT_ACTION), - FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE - ) - ) - .build() - ) - .build() - } else { - null - } - - val expectedSafetyEvent = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED) - .setRefreshBroadcastId(REFRESH_ID) - .build() - verify(mockSafetyCenterManager) - .setSafetySourceData( - HEALTH_CONNECT_SOURCE_ID, - expectedSafetySourceData, - expectedSafetyEvent - ) - } - - @Test - fun rescanAndPushSafetyCenterData_healthPermissionUIDisabled_setsNullData() { - setHealthPermissionUIEnabled(false) - val refreshIntent = - Intent(ACTION_REFRESH_SAFETY_SOURCES) - .putExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID, REFRESH_ID) - - healthConnectPrivacySource.rescanAndPushSafetyCenterData( - context, - refreshIntent, - EVENT_REFRESH_REQUESTED - ) - - val expectedSafetyEvent = - SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED) - .setRefreshBroadcastId(REFRESH_ID) - .build() - verify(mockSafetyCenterManager) - .setSafetySourceData(HEALTH_CONNECT_SOURCE_ID, null, expectedSafetyEvent) - } - - /** Companion object for [HealthConnectPrivacySourceTest]. */ - companion object { - // Real context, used in order to avoid mocking resources. - const val HEALTH_CONNECT_TITLE: String = "Health Connect" - const val HEALTH_CONNECT_SUMMARY: String = "Manage app access to health data" - const val REFRESH_ID: String = "refresh_id" - const val HEALTH_CONNECT_INTENT_ACTION = - "android.health.connect.action.HEALTH_HOME_SETTINGS" - - /** Sets the value for the Health Permission feature [DeviceConfig] property. */ - private fun setHealthPermissionUIEnabled(enabled: Boolean) { - whenever( - DeviceConfig.getBoolean( - eq(NAMESPACE_PRIVACY), - eq("health_permission_ui_enabled"), - anyBoolean() - ) - ) - .thenReturn(enabled) - } - } -} diff --git a/SafetyCenter/Resources/res/raw-v34/safety_center_config.xml b/SafetyCenter/Resources/res/raw-v34/safety_center_config.xml index 5fbf6e9f2..1c3330c63 100644 --- a/SafetyCenter/Resources/res/raw-v34/safety_center_config.xml +++ b/SafetyCenter/Resources/res/raw-v34/safety_center_config.xml @@ -67,9 +67,9 @@ <dynamic-safety-source id="AndroidHealthConnect" profile="primary_profile_only" - packageName="com.android.permissioncontroller" + packageName="com.android.healthconnect.controller" initialDisplayState="hidden" - refreshOnPageOpenAllowed="true" + refreshOnPageOpenAllowed="false" title="@com.android.safetycenter.resources:string/health_connect_title" searchTerms="@com.android.safetycenter.resources:string/health_connect_search_terms"/> <dynamic-safety-source |