diff options
Diffstat (limited to 'PermissionController/src')
22 files changed, 337 insertions, 153 deletions
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/UserPackageInfosLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/UserPackageInfosLiveData.kt index 36586e7cc..02285809c 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/UserPackageInfosLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/UserPackageInfosLiveData.kt @@ -18,9 +18,13 @@ package com.android.permissioncontroller.permission.data import android.app.Application +import android.content.pm.PackageManager +import android.content.pm.PackageManager.GET_ATTRIBUTIONS +import android.content.pm.PackageManager.GET_ATTRIBUTIONS_LONG import android.content.pm.PackageManager.GET_PERMISSIONS import android.content.pm.PackageManager.MATCH_ALL import android.os.UserHandle +import com.android.modules.utils.build.SdkLevel import com.android.permissioncontroller.PermissionControllerApplication import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo import kotlinx.coroutines.Job @@ -31,16 +35,13 @@ import kotlinx.coroutines.Job * @param app The current application * @param user The user whose packages are desired */ -class UserPackageInfosLiveData private constructor( - private val app: Application, - private val user: UserHandle -) : SmartAsyncMediatorLiveData<@kotlin.jvm.JvmSuppressWildcards List<LightPackageInfo>>(), +class UserPackageInfosLiveData +private constructor(private val app: Application, private val user: UserHandle) : + SmartAsyncMediatorLiveData<@JvmSuppressWildcards List<LightPackageInfo>>(), PackageBroadcastReceiver.PackageBroadcastListener, PermissionListenerMultiplexer.PermissionChangeCallback { - /** - * Whether or not the permissions in this liveData are out of date - */ + /** Whether or not the permissions in this liveData are out of date */ var permChangeStale = false override fun onPackageUpdate(packageName: String) { @@ -68,15 +69,29 @@ class UserPackageInfosLiveData private constructor( permChangeStale = false } - /** - * Get all of the packages in the system, organized by user. - */ + /** Get all of the packages in the system, organized by user. */ override suspend fun loadDataAndPostValue(job: Job) { if (job.isCancelled) { return } - val packageInfos = app.applicationContext.packageManager - .getInstalledPackagesAsUser(GET_PERMISSIONS or MATCH_ALL, user.identifier) + + val packageInfos = + if (SdkLevel.isAtLeastU()) { + app.applicationContext.packageManager.getInstalledPackagesAsUser( + PackageManager.PackageInfoFlags.of( + GET_PERMISSIONS.toLong() or GET_ATTRIBUTIONS_LONG or MATCH_ALL.toLong() + ), + user.identifier + ) + } else if (SdkLevel.isAtLeastS()) { + app.applicationContext.packageManager.getInstalledPackagesAsUser( + GET_PERMISSIONS or GET_ATTRIBUTIONS or MATCH_ALL, user.identifier + ) + } else { + app.applicationContext.packageManager.getInstalledPackagesAsUser( + GET_PERMISSIONS or MATCH_ALL, user.identifier + ) + } postValue(packageInfos.map { packageInfo -> LightPackageInfo(packageInfo) }) } @@ -103,6 +118,7 @@ class UserPackageInfosLiveData private constructor( /** * Repository for UserPackageInfosLiveDatas. + * * <p> Key value is a UserHandle, value is its corresponding LiveData. */ companion object : DataRepository<UserHandle, UserPackageInfosLiveData>() { @@ -110,4 +126,4 @@ class UserPackageInfosLiveData private constructor( return UserPackageInfosLiveData(PermissionControllerApplication.get(), key) } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/v34/LightInstallSourceInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/v34/LightInstallSourceInfoLiveData.kt index 8957ea0a9..bbc62dfc9 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/v34/LightInstallSourceInfoLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/v34/LightInstallSourceInfoLiveData.kt @@ -27,7 +27,7 @@ import com.android.permissioncontroller.permission.data.DataRepositoryForPackage import com.android.permissioncontroller.permission.data.PackageBroadcastReceiver import com.android.permissioncontroller.permission.data.SmartAsyncMediatorLiveData import com.android.permissioncontroller.permission.model.livedatatypes.v34.LightInstallSourceInfo -import com.android.permissioncontroller.permission.model.livedatatypes.v34.LightInstallSourceInfo.Companion.UNKNOWN_INSTALL_SOURCE +import com.android.permissioncontroller.permission.model.livedatatypes.v34.LightInstallSourceInfo.Companion.INSTALL_SOURCE_UNAVAILABLE import kotlinx.coroutines.Job /** @@ -73,12 +73,12 @@ private constructor( val lightInstallSourceInfo: LightInstallSourceInfo = try { val installSourceInfo = getInstallSourceInfo(packageName) - LightInstallSourceInfo(installSourceInfo.initiatingPackageName, - installSourceInfo.packageSource) + LightInstallSourceInfo( + installSourceInfo.packageSource, installSourceInfo.initiatingPackageName) } catch (e: PackageManager.NameNotFoundException) { Log.w(LOG_TAG, "InstallSourceInfo for $packageName not found") invalidateSingle(packageName to user) - UNKNOWN_INSTALL_SOURCE + INSTALL_SOURCE_UNAVAILABLE } postValue(lightInstallSourceInfo) } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/v34/SafetyLabelInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/v34/SafetyLabelInfoLiveData.kt index 9fd6e3bdd..6229218d4 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/v34/SafetyLabelInfoLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/v34/SafetyLabelInfoLiveData.kt @@ -82,10 +82,8 @@ private constructor( return } - // TODO(b/261607291): Add support for preinstall apps that provide SafetyLabel. Initiating - // package is null until updated from an app store val lightInstallSourceInfo = lightInstallSourceInfoLiveData.value - if (lightInstallSourceInfo?.isStoreInstalled() != true) { + if (lightInstallSourceInfo?.supportsSafetyLabel != true) { postValue(SafetyLabelInfo.UNAVAILABLE) return } @@ -94,7 +92,7 @@ private constructor( try { val safetyLabel: SafetyLabel? = getSafetyLabel(packageName, user) if (safetyLabel != null) { - SafetyLabelInfo(safetyLabel, lightInstallSourceInfo.initiatingPackageName) + SafetyLabelInfo(safetyLabel, lightInstallSourceInfo) } else { SafetyLabelInfo.UNAVAILABLE } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/LightInstallSourceInfo.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/LightInstallSourceInfo.kt index 8be6054e5..e75c1eadf 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/LightInstallSourceInfo.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/LightInstallSourceInfo.kt @@ -16,37 +16,46 @@ package com.android.permissioncontroller.permission.model.livedatatypes.v34 -import android.content.pm.PackageInstaller import android.content.pm.PackageInstaller.PACKAGE_SOURCE_STORE import android.content.pm.PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED /** * A lighter version of the system's InstallSourceInfo class, containing select information about * the install source. - * - * @param initiatingPackageName The package name of the install source (usually the app store) - * @param packageSource Indicates the package source of the app [PackageInstaller.PackageSourceType] */ -data class LightInstallSourceInfo( - val initiatingPackageName: String?, - private val packageSource: Int -) { - /** Return {@code true} if package considered to be installed by a store */ - fun isStoreInstalled(): Boolean { +class LightInstallSourceInfo { + private constructor() { + initiatingPackageName = null + isPreloadedApp = false + supportsSafetyLabel = false + } + + constructor(packageSource: Int, initiatingPackage: String?) { + initiatingPackageName = initiatingPackage // Stores should be setting PACKAGE_SOURCE_STORE, but it's not enforced. So include the // default source of unspecified. All other sources should be explicitly set to another // PACKAGE_SOURCE_ value - return initiatingPackageName != null && - (packageSource == PACKAGE_SOURCE_STORE || - packageSource == PACKAGE_SOURCE_UNSPECIFIED) - } + val isStoreInstalled = + initiatingPackageName != null && + (packageSource == PACKAGE_SOURCE_STORE || + packageSource == PACKAGE_SOURCE_UNSPECIFIED) - /** Return {@code true} if package considered to be provided as a preloaded app */ - fun isPreloadedApp(): Boolean { - return initiatingPackageName == null && packageSource == PACKAGE_SOURCE_UNSPECIFIED + isPreloadedApp = initiatingPackageName == null && + packageSource == PACKAGE_SOURCE_UNSPECIFIED + supportsSafetyLabel = isStoreInstalled || isPreloadedApp } + /** The package name of the install source (usually the app store) */ + val initiatingPackageName: String? + + /** Whether packages from this install source support safety labels. */ + val supportsSafetyLabel: Boolean + + /** Whether this install source indicates that the package was preloaded onto the device. */ + val isPreloadedApp: Boolean + companion object { - val UNKNOWN_INSTALL_SOURCE = LightInstallSourceInfo(null, PACKAGE_SOURCE_UNSPECIFIED) + /** Indicates an unavailable install source. */ + val INSTALL_SOURCE_UNAVAILABLE = LightInstallSourceInfo() } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/SafetyLabelInfo.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/SafetyLabelInfo.kt index d1a2e3f25..7128e3069 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/SafetyLabelInfo.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v34/SafetyLabelInfo.kt @@ -17,18 +17,20 @@ package com.android.permissioncontroller.permission.model.livedatatypes.v34 import com.android.permission.safetylabel.SafetyLabel +import com.android.permissioncontroller.permission.model.livedatatypes.v34.LightInstallSourceInfo.Companion.INSTALL_SOURCE_UNAVAILABLE /** * A wrapping class for [SafetyLabel] class that includes the install source package name * - * @param safetyLabel The resulting [SafetyLabel], or null if none found - * @param installSourcePackageName The package name of the install source for the APK and safety - * label(usually the app store) + * @param safetyLabel the resulting [SafetyLabel], or null if none found + * @param installSourceInfo the install source information for the package */ -class SafetyLabelInfo(val safetyLabel: SafetyLabel?, val installSourcePackageName: String?) { - +class SafetyLabelInfo( + val safetyLabel: SafetyLabel?, + val installSourceInfo: LightInstallSourceInfo + ) { companion object { /** Default definition of unavailable or no safety label found */ - val UNAVAILABLE = SafetyLabelInfo(null, null) + val UNAVAILABLE = SafetyLabelInfo(null, INSTALL_SOURCE_UNAVAILABLE) } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java index 001520c1b..d5c43335c 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java @@ -547,8 +547,6 @@ public final class PermissionControllerServiceImpl extends PermissionControllerL final boolean isManagedProfile = getSystemService(UserManager.class).isManagedProfile(); DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class); - final boolean isOrganizationOwnedDevice = dpm.isOrganizationOwnedDeviceWithManagedProfile(); - int numPerms = expandedPermissions.size(); for (int i = 0; i < numPerms; i++) { String permName = expandedPermissions.get(i); @@ -565,8 +563,7 @@ public final class PermissionControllerServiceImpl extends PermissionControllerL switch (grantState) { case PERMISSION_GRANT_STATE_GRANTED: if (AdminRestrictedPermissionsUtils.mayAdminGrantPermission(perm.getName(), - canAdminGrantSensorsPermissions, isManagedProfile, - isOrganizationOwnedDevice)) { + canAdminGrantSensorsPermissions, isManagedProfile, dpm)) { perm.setPolicyFixed(true); group.grantRuntimePermissions(false, false, new String[]{permName}); autoGrantPermissionsNotifier.onPermissionAutoGranted(permName); diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt index 98c5fc975..9231dc17b 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt @@ -420,7 +420,7 @@ class SafetyLabelChangesJobService : JobService() { private suspend fun getAllStoreInstalledPackagesRequestingLocation(): Set<Pair<String, UserHandle>> = getAllPackagesRequestingLocation() - .filter { isStoreInstalledPackage(it) } + .filter { isSafetyLabelSupported(it) } .toSet() private suspend fun getAllPackagesRequestingLocation(): Set<Pair<String, UserHandle>> = @@ -437,12 +437,10 @@ class SafetyLabelChangesJobService : JobService() { private fun AppPermGroupUiInfo.isPermissionGranted() = permGrantState in setOf(PERMS_ALLOWED_ALWAYS, PERMS_ALLOWED_FOREGROUND_ONLY) - private suspend fun isStoreInstalledPackage( - pkg: Pair<String, UserHandle> - ): Boolean { + private suspend fun isSafetyLabelSupported(packageUser: Pair<String, UserHandle>): Boolean { val lightInstallSourceInfo = - LightInstallSourceInfoLiveData[pkg].getInitializedValue() - return lightInstallSourceInfo.isStoreInstalled() + LightInstallSourceInfoLiveData[packageUser].getInitializedValue() + return lightInstallSourceInfo.supportsSafetyLabel } private suspend fun postSafetyLabelChangedNotification() { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java index 1f1bf1056..cb6d21aec 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java @@ -32,8 +32,10 @@ import static com.android.permissioncontroller.PermissionControllerStatsLog.PERM import android.Manifest; import android.app.ActionBar; +import android.content.ComponentName; import android.content.Intent; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PermissionInfo; import android.os.Build; import android.os.Bundle; @@ -81,6 +83,7 @@ import com.android.permissioncontroller.permission.utils.KotlinUtils; import com.android.permissioncontroller.permission.utils.PermissionMapping; import com.android.permissioncontroller.permission.utils.Utils; +import java.util.Objects; import java.util.Random; /** @@ -123,12 +126,24 @@ public final class ManagePermissionsActivity extends SettingsActivity { + ".permissioncontroller.extra.SHOW_7_DAYS"; /** + * Name of the aliased activity. + */ + public static final String ALIAS_ACTIVITY_NAME = + "com.android.permissioncontroller.permission.ui.ManagePermissionsActivityAlias"; + + /** * The requestCode used when we decide not to use this activity, but instead launch * another activity in our place. When that activity finishes, we set it's result * as our result and then finish. */ private static final int PROXY_ACTIVITY_REQUEST_CODE = 5; + private static final String LAUNCH_PERMISSION_SETTINGS = + "android.permission.LAUNCH_PERMISSION_SETTINGS"; + + private static final String APP_PERMISSIONS_SETTINGS = + "android.settings.APP_PERMISSIONS_SETTINGS"; + @Override public void onCreate(Bundle savedInstanceState) { if (DeviceUtils.isAuto(this)) { @@ -170,6 +185,16 @@ public final class ManagePermissionsActivity extends SettingsActivity { int openFromIntentAction = APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__OPENED_FROM_INTENT; + ComponentName component = getIntent().getComponent(); + if (component != null + && Objects.equals(component.getClassName(), ALIAS_ACTIVITY_NAME) + && !Objects.equals(action, APP_PERMISSIONS_SETTINGS)) { + Log.w(LOG_TAG, ALIAS_ACTIVITY_NAME + " can only be launched with " + + APP_PERMISSIONS_SETTINGS); + finishAfterTransition(); + return; + } + String permissionName; switch (action) { case Intent.ACTION_MANAGE_PERMISSIONS: @@ -276,7 +301,25 @@ public final class ManagePermissionsActivity extends SettingsActivity { return; } - case Intent.ACTION_MANAGE_APP_PERMISSIONS: { + case Intent.ACTION_MANAGE_APP_PERMISSIONS: + case APP_PERMISSIONS_SETTINGS: { + if (Objects.equals(action, APP_PERMISSIONS_SETTINGS)) { + PermissionInfo permissionInfo; + try { + permissionInfo = getPackageManager() + .getPermissionInfo(LAUNCH_PERMISSION_SETTINGS, 0); + } catch (NameNotFoundException e) { + permissionInfo = null; + } + if (permissionInfo == null + || !Objects.equals(permissionInfo.packageName, Utils.OS_PKG)) { + Log.w(LOG_TAG, LAUNCH_PERMISSION_SETTINGS + + " must be defined by platform."); + finishAfterTransition(); + return; + } + } + String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME); if (packageName == null) { Log.i(LOG_TAG, "Missing mandatory argument EXTRA_PACKAGE_NAME"); @@ -340,7 +383,8 @@ public final class ManagePermissionsActivity extends SettingsActivity { setNavGraph(args, R.id.app_permission_groups); return; } - } break; + break; + } case Intent.ACTION_MANAGE_PERMISSION_APPS: { permissionName = getIntent().getStringExtra(Intent.EXTRA_PERMISSION_NAME); @@ -433,7 +477,9 @@ public final class ManagePermissionsActivity extends SettingsActivity { setNavGraph(UnusedAppsFragment.createArgs(sessionId), R.id.auto_revoke); return; } - } break; + + break; + } case PermissionManager.ACTION_REVIEW_PERMISSION_DECISIONS: { UserHandle userHandle = getIntent().getParcelableExtra(Intent.EXTRA_USER); 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 4e88d4c9b..24dc51bdf 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt @@ -39,6 +39,7 @@ import android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP import android.os.Build import android.os.Bundle import android.os.Process +import android.os.UserManager import android.permission.PermissionManager import android.provider.MediaStore import android.util.Log @@ -1346,10 +1347,18 @@ class GrantPermissionsViewModel( } requestInfosLiveData.update() } - activity.startActivityForResult(Intent(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP) + // A clone profile doesn't have a MediaProvider. If this user is a clone profile, open + // the photo picker in the parent profile + val userManager = activity.getSystemService(UserManager::class.java)!! + val user = if (userManager.isCloneProfile) { + userManager.getProfileParent(Process.myUserHandle()) ?: Process.myUserHandle() + } else { + Process.myUserHandle() + } + activity.startActivityForResultAsUser(Intent(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP) .putExtra(Intent.EXTRA_UID, packageInfo.uid) .setType(KotlinUtils.getMimeTypeForPermissions(unfilteredAffectedPermissions)), - PHOTO_PICKER_REQUEST_CODE) + PHOTO_PICKER_REQUEST_CODE, user) } /** 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 3fe96017a..304dc3db4 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 @@ -88,11 +88,12 @@ class PermissionRationaleViewModel( * should be shown with it. */ data class PermissionRationaleInfo( - val groupName: String, - val installSourcePackageName: String?, - val installSourceLabel: String?, - val purposeSet: Set<Int> - ) + val groupName: String, + val isPreloadedApp: Boolean, + val installSourcePackageName: String?, + val installSourceLabel: String?, + val purposeSet: Set<Int> + ) /** A [LiveData] which holds the currently pending PermissionRationaleInfo */ val permissionRationaleInfoLiveData = @@ -118,7 +119,8 @@ class PermissionRationaleViewModel( return } - val installSourcePackageName = safetyLabelInfo.installSourcePackageName + val installSourcePackageName = + safetyLabelInfo.installSourceInfo.initiatingPackageName val installSourceLabel: String? = installSourcePackageName?.let { KotlinUtils.getPackageLabel(app, it, Process.myUserHandle()) @@ -132,6 +134,7 @@ class PermissionRationaleViewModel( value = PermissionRationaleInfo( permissionGroupName, + safetyLabelInfo.installSourceInfo.isPreloadedApp, installSourcePackageName, installSourceLabel, purposes) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/PermissionRationaleActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/PermissionRationaleActivity.java index d58dfa341..606ce8157 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/PermissionRationaleActivity.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/PermissionRationaleActivity.java @@ -353,17 +353,7 @@ public class PermissionRationaleActivity extends SettingsActivity implements @StringRes int titleResId = getTitleResIdForPermissionGroup(mPermissionGroupName); setTitle(titleResId); CharSequence title = getString(titleResId); - - String installSourcePackageName = mPermissionRationaleInfo.getInstallSourcePackageName(); - CharSequence installSourceLabel = mPermissionRationaleInfo.getInstallSourceLabel(); - checkStringNotEmpty(installSourcePackageName, - "installSourcePackageName cannot be null or empty"); - checkStringNotEmpty(installSourceLabel, - "installSourceLabel cannot be null or empty"); - CharSequence dataSharingSourceMessage = createDataSharingSourceMessageWithSpans( - getText(R.string.permission_rationale_data_sharing_source_message), - installSourceLabel, - getLinkToAppStore(installSourcePackageName)); + CharSequence dataSharingSourceMessage = getDataSharingSourceMessage(); CharSequence purposeTitle = getString(getPurposeTitleResIdForPermissionGroup(mPermissionGroupName)); @@ -418,6 +408,22 @@ public class PermissionRationaleActivity extends SettingsActivity implements } } + private CharSequence getDataSharingSourceMessage() { + if (mPermissionRationaleInfo.isPreloadedApp()) { + return getText(R.string.permission_rationale_data_sharing_device_manufacturer_message); + } + + String installSourcePackageName = mPermissionRationaleInfo.getInstallSourcePackageName(); + CharSequence installSourceLabel = mPermissionRationaleInfo.getInstallSourceLabel(); + checkStringNotEmpty(installSourcePackageName, + "installSourcePackageName cannot be null or empty"); + checkStringNotEmpty(installSourceLabel, "installSourceLabel cannot be null or empty"); + return createDataSharingSourceMessageWithSpans( + getText(R.string.permission_rationale_data_sharing_source_message), + installSourceLabel, + getLinkToAppStore(installSourcePackageName)); + } + @StringRes private int getTitleResIdForPermissionGroup(String permissionGroupName) { if (LOCATION.equals(permissionGroupName)) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/v31/AdminRestrictedPermissionsUtils.java b/PermissionController/src/com/android/permissioncontroller/permission/utils/v31/AdminRestrictedPermissionsUtils.java index 9b7927c1d..770ee6c95 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/v31/AdminRestrictedPermissionsUtils.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/v31/AdminRestrictedPermissionsUtils.java @@ -18,13 +18,13 @@ package com.android.permissioncontroller.permission.utils.v31; import android.Manifest; import android.app.admin.DevicePolicyManager; +import android.app.admin.ManagedSubscriptionsPolicy; import android.content.Context; import android.os.UserHandle; 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. @@ -56,16 +56,6 @@ public final class AdminRestrictedPermissionsUtils { } /** - * A set of permissions that the non-organization owned managed Profile Owner cannot grant. - */ - private static final ArraySet<String> MANAGED_PROFILE_OWNER_RESTRICTED_PERMISSIONS = - new ArraySet<>(); - - static { - MANAGED_PROFILE_OWNER_RESTRICTED_PERMISSIONS.add(Manifest.permission.READ_SMS); - } - - /** * Returns true if the admin may grant this permission, false otherwise. */ public static boolean mayAdminGrantPermission(Context context, String permission, int userId) { @@ -75,10 +65,8 @@ public final class AdminRestrictedPermissionsUtils { Context userContext = context.createContextAsUser(UserHandle.of(userId), /* flags= */0); DevicePolicyManager dpm = userContext.getSystemService(DevicePolicyManager.class); UserManager um = userContext.getSystemService(UserManager.class); - if (um.isManagedProfile(userId) - && MANAGED_PROFILE_OWNER_RESTRICTED_PERMISSIONS.contains(permission) - && !(SdkLevel.isAtLeastU() && dpm.isOrganizationOwnedDeviceWithManagedProfile())) { - return false; + if (um.isManagedProfile(userId) && Manifest.permission.READ_SMS.equals(permission)) { + return mayManagedProfileAdminGrantReadSms(dpm); } if (!ADMIN_RESTRICTED_SENSORS_PERMISSIONS.contains(permission)) { return true; @@ -92,13 +80,12 @@ public final class AdminRestrictedPermissionsUtils { */ public static boolean mayAdminGrantPermission(String permission, boolean canAdminGrantSensorsPermissions, boolean isManagedProfile, - boolean isOrganizationOwnedDevice) { + DevicePolicyManager dpm) { if (!SdkLevel.isAtLeastS()) { return true; } - if (isManagedProfile && MANAGED_PROFILE_OWNER_RESTRICTED_PERMISSIONS.contains(permission) - && !(SdkLevel.isAtLeastU() && isOrganizationOwnedDevice)) { - return false; + if (isManagedProfile && Manifest.permission.READ_SMS.equals(permission)) { + return mayManagedProfileAdminGrantReadSms(dpm); } if (!ADMIN_RESTRICTED_SENSORS_PERMISSIONS.contains(permission)) { return true; @@ -106,4 +93,10 @@ public final class AdminRestrictedPermissionsUtils { return canAdminGrantSensorsPermissions; } + + private static boolean mayManagedProfileAdminGrantReadSms(DevicePolicyManager dpm) { + return SdkLevel.isAtLeastU() && dpm.isOrganizationOwnedDeviceWithManagedProfile() + && dpm.getManagedSubscriptionsPolicy().getPolicyType() + == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS; + } } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java index f6eaa319d..c56cce0a5 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java @@ -462,7 +462,7 @@ public class IssueCardPreference extends Preference implements ComparablePrefere public void buildAndAddToView(LinearLayout buttonList) { MaterialButton button = new MaterialButton(mContextThemeWrapper, null, getStyle()); - if (SdkLevel.isAtLeastU()) { + if (SdkLevel.isAtLeastU() && !mIsLargeScreen) { configureGroupStyleCorners(button); } setButtonColors(button); diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PreferenceHighlightManager.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PreferenceHighlightManager.kt index 68c12c767..2acd6b5a3 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PreferenceHighlightManager.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PreferenceHighlightManager.kt @@ -74,21 +74,21 @@ internal class PreferenceHighlightManager(private val fragment: PreferenceFragme /** Restore previously saved instance state from [Bundle] */ fun restoreState(savedInstanceState: Bundle?) { - if (savedInstanceState == null) { + if (!SafetyCenterUiFlags.getShowSubpages() || savedInstanceState == null) { return } - if (!SafetyCenterUiFlags.getShowSubpages()) { - preferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY, false) - } + preferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY, false) } /** Save current instance state to provided [Bundle] */ fun saveState(outState: Bundle) { if (!SafetyCenterUiFlags.getShowSubpages()) { - val highlightRequested = preferenceGroupAdapter?.isHighlightRequested - highlightRequested?.let { outState.putBoolean(SAVE_HIGHLIGHTED_KEY, it) } + return } + + val highlightRequested = preferenceGroupAdapter?.isHighlightRequested + highlightRequested?.let { outState.putBoolean(SAVE_HIGHLIGHTED_KEY, it) } } /** Scrolls to a particular preference in the recycler view and highlights it */ diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java index dd90fd559..940cb2f69 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java @@ -41,6 +41,7 @@ import android.util.Log; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; +import androidx.preference.Preference; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceGroup; @@ -65,6 +66,7 @@ public final class SafetyCenterDashboardFragment extends SafetyCenterFragment { private static final String ISSUES_GROUP_KEY = "issues_group"; private static final String ENTRIES_GROUP_KEY = "entries_group"; private static final String STATIC_ENTRIES_GROUP_KEY = "static_entries_group"; + private static final String SPACER_KEY = "spacer"; private SafetyStatusPreference mSafetyStatusPreference; private final CollapsableGroupCardHelper mCollapsableGroupCardHelper = @@ -116,6 +118,8 @@ public final class SafetyCenterDashboardFragment extends SafetyCenterFragment { 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/SafetyCenterQsFragment.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsFragment.java index 93407b023..f69746e39 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsFragment.java @@ -150,7 +150,6 @@ public class SafetyCenterQsFragment extends Fragment { ViewGroup root = (ViewGroup) inflater.inflate(R.layout.safety_center_qs, container, false); root.setVisibility(View.GONE); - root.setBackgroundColor(getResources().getColor(R.color.sc_qs_background_color)); View closeButton = root.findViewById(R.id.close_button); closeButton.setOnClickListener((v) -> requireActivity().finish()); SafetyCenterTouchTarget.configureSize( diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterScrollWrapperFragment.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterScrollWrapperFragment.kt index beda65ee3..c064b94f0 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterScrollWrapperFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterScrollWrapperFragment.kt @@ -24,18 +24,26 @@ import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.utils.Utils @RequiresApi(Build.VERSION_CODES.TIRAMISU) -internal class SafetyCenterScrollWrapperFragment : Fragment(R.layout.safety_center_scroll_wrapper) { +internal class SafetyCenterScrollWrapperFragment : + Fragment( + if (SafetyCenterUiFlags.getShowSubpages()) R.layout.safety_center_non_scroll_wrapper + else R.layout.safety_center_scroll_wrapper + ) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (savedInstanceState == null) { - childFragmentManager.beginTransaction() - .add(R.id.fragment_container, - SafetyCenterDashboardFragment.newInstance( - Utils.getOrGenerateSessionId(requireActivity().getIntent()), - /* isQuickSettingsFragment= */ false)) - .commitNow() + childFragmentManager + .beginTransaction() + .add( + R.id.fragment_container, + SafetyCenterDashboardFragment.newInstance( + Utils.getOrGenerateSessionId(requireActivity().getIntent()), + /* isQuickSettingsFragment= */ false + ) + ) + .commitNow() } } -}
\ No newline at end of file +} diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt index 8625e1959..5e45d2b3c 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt @@ -105,7 +105,9 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() { Log.w(TAG, "$sourceGroupId doesn't have any matching footer") subpageFooter.setVisible(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) } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java index 61b36fc60..0b8706a38 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java @@ -27,7 +27,6 @@ import android.os.Looper; import android.safetycenter.SafetyCenterStatus; import android.text.TextUtils; import android.util.AttributeSet; -import android.view.View; import android.widget.ImageView; import android.widget.TextView; @@ -37,7 +36,6 @@ import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; import com.android.permissioncontroller.R; -import com.android.permissioncontroller.permission.utils.KotlinUtils; import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel; import com.android.permissioncontroller.safetycenter.ui.model.StatusUiData; import com.android.permissioncontroller.safetycenter.ui.view.StatusCardView; @@ -90,7 +88,6 @@ public class SafetyStatusPreference extends Preference implements ComparablePref updateStatusText(statusCardView.getTitleView(), statusCardView.getSummaryView()); - configureSafetyProtectionView(statusCardView, context); mFirstBind = false; } @@ -121,31 +118,6 @@ public class SafetyStatusPreference extends Preference implements ComparablePref statusCardView.showButtons(mStatus); } - private void configureSafetyProtectionView(StatusCardView statusCardView, Context context) { - View safetyProtectionSectionView = - statusCardView.findViewById(R.id.safety_protection_section_view); - if (KotlinUtils.INSTANCE.shouldShowSafetyProtectionResources(context)) { - // Hide the Safety Protection branding if there are any issue cards - safetyProtectionSectionView.setVisibility( - mStatus.hasIssues() ? View.GONE : View.VISIBLE); - } - if (safetyProtectionSectionView.getVisibility() == View.GONE) { - statusCardView.setPaddingRelative( - statusCardView.getPaddingStart(), - statusCardView.getPaddingTop(), - statusCardView.getPaddingEnd(), - /* bottom= */ getContext() - .getResources() - .getDimensionPixelSize(R.dimen.sc_card_margin_bottom)); - } else { - statusCardView.setPaddingRelative( - statusCardView.getPaddingStart(), - statusCardView.getPaddingTop(), - statusCardView.getPaddingEnd(), - /* bottom= */ 0); - } - } - private void updateStatusText(TextView title, TextView summary) { if (mFirstBind) { title.setText(mStatus.getTitle()); diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt new file mode 100644 index 000000000..bb09783be --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt @@ -0,0 +1,129 @@ +/* + * 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.safetycenter.ui + +import android.content.Context +import android.util.AttributeSet +import android.view.View +import android.view.ViewGroup +import android.view.ViewTreeObserver +import androidx.preference.Preference +import androidx.preference.PreferenceScreen +import androidx.preference.PreferenceViewHolder +import com.android.permissioncontroller.R +import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity +import com.android.settingslib.widget.FooterPreference +import kotlin.math.max + +/** + * A preference that adds an empty space to the bottom of a Safety Center subpage. + * + * Due to the logic of [CollapsingToolbarBaseActivity], its content won't be scrollable if it fits + * the single page. This logic conflicts with the UX of collapsible and expandable items of Safety + * Center, and with some other use cases (i.e. opening the page from Search might scroll to bottom + * while the scroll is disabled). In such cases user won't be able to expand the collapsed toolbar + * by scrolling the screen content. + * + * [SpacerPreference] makes the page to be slightly bigger than the screen size to unlock the scroll + * regardless of the content length and to mitigate this UX problem. + * + * If a [FooterPreference] is added to the same [PreferenceScreen], its order should be decreased to + * keep it with the last visible content above the [SpacerPreference]. + */ +internal class SpacerPreference(context: Context, attrs: AttributeSet) : + Preference(context, attrs) { + + init { + setLayoutResource(R.layout.preference_spacer) + isVisible = SafetyCenterUiFlags.getShowSubpages() + // spacer should be the last item on screen + setOrder(Int.MAX_VALUE - 1) + } + + private var maxKnownToolbarHeight = 0 + override fun onBindViewHolder(holder: PreferenceViewHolder) { + super.onBindViewHolder(holder) + val spacer = holder.itemView + + // we should ensure we won't add multiple listeners to the same view, + // and Preferences API does not allow to do cleanups when onViewRecycled, + // so we are keeping a track of the added listener attaching it as a tag to the View + val listener: View.OnLayoutChangeListener = spacer.tag as? View.OnLayoutChangeListener + ?: object : View.OnLayoutChangeListener { + override fun onLayoutChange( + v: View?, + left: Int, + top: Int, + right: Int, + bottom: Int, + oldLeft: Int, + oldTop: Int, + oldRight: Int, + oldBottom: Int + ) { + adjustHeight(spacer) + }}.also { spacer.tag = it } + + spacer.removeOnLayoutChangeListener(listener) + spacer.addOnLayoutChangeListener(listener) + } + + private fun adjustHeight(spacer: View) { + val root = spacer.rootView as? ViewGroup + if (root == null) { + return + } + + val contentParent = root.findViewById<ViewGroup>(R.id.content_parent) + if (contentParent == null) { + return + } + // when opening the Subpage from Search the layout pass may be triggered + // differently due to the auto-scroll to highlight a specific item, + // and in this case we need to wait the content parent to be measured + if (contentParent.height == 0) { + val globalLayoutObserver = object : ViewTreeObserver.OnGlobalLayoutListener { + override fun onGlobalLayout() { + contentParent.viewTreeObserver.removeOnGlobalLayoutListener(this) + adjustHeight(spacer) + } + } + contentParent.viewTreeObserver.addOnGlobalLayoutListener(globalLayoutObserver) + return + } + + val collapsingToolbar = root.findViewById<View>(R.id.collapsing_toolbar) + maxKnownToolbarHeight = max(maxKnownToolbarHeight, collapsingToolbar.height) + + val contentHeight = spacer.top + maxKnownToolbarHeight + val desiredSpacerHeight = if (contentHeight > contentParent.height) { + // making it 0 height will remove if from recyclerview + 1 + } else { + // to unlock the scrolling we need spacer to go slightly beyond the screen + contentParent.height - contentHeight + 1 + } + + val layoutParams = spacer.layoutParams + if (layoutParams.height != desiredSpacerHeight) { + layoutParams.height = desiredSpacerHeight + spacer.layoutParams = layoutParams + // need to let RecyclerView to update scroll position + spacer.post(::notifyChanged) + } + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/StatusCardView.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/StatusCardView.kt index 000f773ee..013f32c85 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/StatusCardView.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/view/StatusCardView.kt @@ -25,7 +25,6 @@ import android.widget.TextView import androidx.annotation.RequiresApi import androidx.constraintlayout.widget.ConstraintLayout import com.android.permissioncontroller.R -import com.android.permissioncontroller.permission.ui.v33.widget.SafetyProtectionSectionView import com.android.permissioncontroller.safetycenter.ui.model.StatusUiData import com.google.android.material.button.MaterialButton @@ -51,9 +50,6 @@ constructor( val summaryView: TextView by lazy { findViewById(R.id.status_summary) } val reviewSettingsButton: MaterialButton by lazy { findViewById(R.id.review_settings_button) } val rescanButton: MaterialButton by lazy { findViewById(R.id.rescan_button) } - val safetyProtectionSectionView: SafetyProtectionSectionView by lazy { - findViewById(R.id.safety_protection_section_view) - } fun showButtons(statusUiData: StatusUiData) { rescanButton.isEnabled = !statusUiData.isRefreshInProgress diff --git a/PermissionController/src/com/android/permissioncontroller/safetylabel/SafetyLabelChangedBroadcastReceiver.kt b/PermissionController/src/com/android/permissioncontroller/safetylabel/SafetyLabelChangedBroadcastReceiver.kt index cc9487210..99f4adb02 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetylabel/SafetyLabelChangedBroadcastReceiver.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetylabel/SafetyLabelChangedBroadcastReceiver.kt @@ -30,8 +30,8 @@ import android.util.Log import androidx.annotation.MainThread import androidx.annotation.RequiresApi import com.android.permission.safetylabel.SafetyLabel as AppMetadataSafetyLabel -import com.android.permissioncontroller.permission.data.v34.LightInstallSourceInfoLiveData import com.android.permissioncontroller.permission.data.LightPackageInfoLiveData +import com.android.permissioncontroller.permission.data.v34.LightInstallSourceInfoLiveData import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo import com.android.permissioncontroller.permission.utils.KotlinUtils import com.android.permissioncontroller.permission.utils.PermissionMapping @@ -104,9 +104,8 @@ class SafetyLabelChangedBroadcastReceiver : BroadcastReceiver() { if (!isAppRequestingLocationPermission(lightPackageInfo)) { return } - // TODO(b/261607291): Enable safety label change notifications feature for - // preinstalled apps. - if (!isStoreInstalledPackage(Pair(packageName, user))) { + + if (!isSafetyLabelSupported(Pair(packageName, user))) { return } writeSafetyLabel(context, lightPackageInfo, user) @@ -188,12 +187,10 @@ class SafetyLabelChangedBroadcastReceiver : BroadcastReceiver() { return lightPackageInfo.requestedPermissions.any { LOCATION_PERMISSIONS.contains(it) } } - private suspend fun isStoreInstalledPackage( - packageUser: Pair<String, UserHandle> - ): Boolean { + private suspend fun isSafetyLabelSupported(packageUser: Pair<String, UserHandle>): Boolean { val lightInstallSourceInfo = LightInstallSourceInfoLiveData[packageUser].getInitializedValue() - return lightInstallSourceInfo.isStoreInstalled() + return lightInstallSourceInfo.supportsSafetyLabel } private fun isPackageAddedBroadcast(intentAction: String?) = |