diff options
79 files changed, 2995 insertions, 497 deletions
diff --git a/api/current.txt b/api/current.txt index 148efbc0443f..5997043cdd4a 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6836,7 +6836,6 @@ package android.app.admin { field public static final String DELEGATION_KEEP_UNINSTALLED_PACKAGES = "delegation-keep-uninstalled-packages"; field public static final String DELEGATION_NETWORK_LOGGING = "delegation-network-logging"; field public static final String DELEGATION_PACKAGE_ACCESS = "delegation-package-access"; - field public static final String DELEGATION_PACKAGE_INSTALLATION = "delegation-package-installation"; field public static final String DELEGATION_PERMISSION_GRANT = "delegation-permission-grant"; field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2 field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3 @@ -11483,10 +11482,12 @@ package android.content.pm { method public void setOriginatingUri(@Nullable android.net.Uri); method public void setReferrerUri(@Nullable android.net.Uri); method public void setSize(long); + method public void setWhitelistedRestrictedPermissions(@Nullable java.util.Set<java.lang.String>); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionParams> CREATOR; field public static final int MODE_FULL_INSTALL = 1; // 0x1 field public static final int MODE_INHERIT_EXISTING = 2; // 0x2 + field @NonNull public static final java.util.Set<java.lang.String> RESTRICTED_PERMISSIONS_ALL; } public class PackageItemInfo { @@ -11523,6 +11524,7 @@ package android.content.pm { method public abstract boolean addPermission(@NonNull android.content.pm.PermissionInfo); method public abstract boolean addPermissionAsync(@NonNull android.content.pm.PermissionInfo); method @Deprecated public abstract void addPreferredActivity(@NonNull android.content.IntentFilter, int, @Nullable android.content.ComponentName[], @NonNull android.content.ComponentName); + method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean addWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int); method public abstract boolean canRequestPackageInstalls(); method public abstract String[] canonicalToCurrentPackageNames(@NonNull String[]); method @CheckResult public abstract int checkPermission(@NonNull String, @NonNull String); @@ -11592,11 +11594,13 @@ package android.content.pm { method @NonNull public abstract android.graphics.drawable.Drawable getUserBadgedDrawableForDensity(@NonNull android.graphics.drawable.Drawable, @NonNull android.os.UserHandle, @Nullable android.graphics.Rect, int); method @NonNull public abstract android.graphics.drawable.Drawable getUserBadgedIcon(@NonNull android.graphics.drawable.Drawable, @NonNull android.os.UserHandle); method @NonNull public abstract CharSequence getUserBadgedLabel(@NonNull CharSequence, @NonNull android.os.UserHandle); + method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) @NonNull public java.util.Set<java.lang.String> getWhitelistedRestrictedPermissions(@NonNull String, int); method @Nullable public abstract android.content.res.XmlResourceParser getXml(@NonNull String, @XmlRes int, @Nullable android.content.pm.ApplicationInfo); method public boolean hasSigningCertificate(@NonNull String, @NonNull byte[], int); method public boolean hasSigningCertificate(int, @NonNull byte[], int); method public abstract boolean hasSystemFeature(@NonNull String); method public abstract boolean hasSystemFeature(@NonNull String, int); + method public boolean isDeviceUpgrading(); method public abstract boolean isInstantApp(); method public abstract boolean isInstantApp(@NonNull String); method public boolean isPackageSuspended(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; @@ -11613,6 +11617,7 @@ package android.content.pm { method @NonNull public abstract java.util.List<android.content.pm.PermissionInfo> queryPermissionsByGroup(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException; method @Deprecated public abstract void removePackageFromPreferred(@NonNull String); method public abstract void removePermission(@NonNull String); + method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int); method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int); method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int); method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int); @@ -11740,6 +11745,9 @@ package android.content.pm { field public static final String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct"; field public static final String FEATURE_WIFI_PASSPOINT = "android.hardware.wifi.passpoint"; field public static final String FEATURE_WIFI_RTT = "android.hardware.wifi.rtt"; + field public static final int FLAG_PERMISSION_WHITELIST_INSTALLER = 2; // 0x2 + field public static final int FLAG_PERMISSION_WHITELIST_SYSTEM = 1; // 0x1 + field public static final int FLAG_PERMISSION_WHITELIST_UPGRADE = 4; // 0x4 field public static final int GET_ACTIVITIES = 1; // 0x1 field public static final int GET_CONFIGURATIONS = 16384; // 0x4000 field @Deprecated public static final int GET_DISABLED_COMPONENTS = 512; // 0x200 @@ -11840,7 +11848,9 @@ package android.content.pm { method @Nullable public CharSequence loadDescription(@NonNull android.content.pm.PackageManager); field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.PermissionInfo> CREATOR; field public static final int FLAG_COSTS_MONEY = 1; // 0x1 + field public static final int FLAG_HARD_RESTRICTED = 4; // 0x4 field public static final int FLAG_INSTALLED = 1073741824; // 0x40000000 + field public static final int FLAG_SOFT_RESTRICTED = 8; // 0x8 field public static final int PROTECTION_DANGEROUS = 1; // 0x1 field public static final int PROTECTION_FLAG_APPOP = 64; // 0x40 field public static final int PROTECTION_FLAG_DEVELOPMENT = 32; // 0x20 diff --git a/api/system-current.txt b/api/system-current.txt index 8ac52f7f1d94..3ee6a49c1b44 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -201,6 +201,7 @@ package android { field public static final String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES"; field public static final String USER_ACTIVITY = "android.permission.USER_ACTIVITY"; field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK"; + field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS"; field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG"; field public static final String WRITE_DREAM_STATE = "android.permission.WRITE_DREAM_STATE"; field public static final String WRITE_EMBEDDED_SUBSCRIPTIONS = "android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"; @@ -1581,6 +1582,7 @@ package android.content.pm { method public boolean getInstallAsInstantApp(boolean); method public boolean getInstallAsVirtualPreload(); method public boolean getRequestDowngrade(); + method @NonNull public java.util.Set<java.lang.String> getWhitelistedRestrictedPermissions(); } public static class PackageInstaller.SessionParams implements android.os.Parcelable { @@ -1651,8 +1653,12 @@ package android.content.pm { field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS"; field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio"; field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock"; + field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000 field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20 field public static final int FLAG_PERMISSION_POLICY_FIXED = 4; // 0x4 + field public static final int FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT = 2048; // 0x800 + field public static final int FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT = 4096; // 0x1000 + field public static final int FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT = 8192; // 0x2000 field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40 field public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8 field public static final int FLAG_PERMISSION_SYSTEM_FIXED = 16; // 0x10 @@ -1708,6 +1714,7 @@ package android.content.pm { field public static final int MATCH_ANY_USER = 4194304; // 0x400000 field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000 field public static final int MATCH_INSTANT = 8388608; // 0x800000 + field public static boolean RESTRICTED_PERMISSIONS_ENABLED; field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1 field public static final int RESTRICTION_HIDE_NOTIFICATIONS = 2; // 0x2 field public static final int RESTRICTION_NONE = 0; // 0x0 @@ -1722,7 +1729,7 @@ package android.content.pm { method public void onPermissionsChanged(int); } - @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags { + @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags { } public class PermissionGroupInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { @@ -5446,7 +5453,7 @@ package android.os { } public static interface RemoteCallback.OnResultListener { - method public void onResult(android.os.Bundle); + method public void onResult(@Nullable android.os.Bundle); } public class ServiceSpecificException extends java.lang.RuntimeException { @@ -5689,6 +5696,7 @@ package android.permission { method @NonNull public abstract java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(@NonNull String); method @NonNull public abstract java.util.List<android.permission.RuntimePermissionUsageInfo> onGetPermissionUsages(boolean, long); method public abstract void onGetRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.OutputStream); + method public abstract void onGrantOrUpgradeDefaultRuntimePermissions(); method @BinderThread public abstract boolean onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle); method @BinderThread public abstract void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream); method public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String); @@ -5698,7 +5706,9 @@ package android.permission { } public final class PermissionManager { + method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public int getRuntimePermissionsVersion(); method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions(); + method @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public void setRuntimePermissionsVersion(@IntRange(from=0) int); } public static final class PermissionManager.SplitPermissionInfo { @@ -5866,6 +5876,7 @@ package android.provider { field public static final String NAMESPACE_INTELLIGENCE_ATTENTION = "intelligence_attention"; field public static final String NAMESPACE_MEDIA_NATIVE = "media_native"; field public static final String NAMESPACE_NETD_NATIVE = "netd_native"; + field public static final String NAMESPACE_PRIVACY = "privacy"; field public static final String NAMESPACE_ROLLBACK = "rollback"; field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot"; field public static final String NAMESPACE_RUNTIME = "runtime"; @@ -5886,12 +5897,6 @@ package android.provider { method public void onPropertyChanged(@NonNull String, @NonNull String, @Nullable String); } - public static interface DeviceConfig.Privacy { - field public static final String NAMESPACE = "privacy"; - field public static final String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED = "location_access_check_enabled"; - field public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled"; - } - public static class DeviceConfig.Properties { method public boolean getBoolean(@NonNull String, boolean); method public float getFloat(@NonNull String, float); @@ -6070,7 +6075,6 @@ package android.provider { field public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis"; field public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update"; field public static final String REQUIRE_PASSWORD_TO_DECRYPT = "require_password_to_decrypt"; - field public static final String SMS_ACCESS_RESTRICTION_ENABLED = "sms_access_restriction_enabled"; field public static final String THEATER_MODE_ON = "theater_mode_on"; field public static final String WEBVIEW_MULTIPROCESS = "webview_multiprocess"; field public static final String WIFI_BADGING_THRESHOLDS = "wifi_badging_thresholds"; @@ -6099,7 +6103,6 @@ package android.provider { field public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications"; field public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications"; field public static final String ODI_CAPTIONS_ENABLED = "odi_captions_enabled"; - field public static final String ODI_CAPTIONS_OPTED_OUT = "odi_captions_opted_out"; field public static final String THEME_CUSTOMIZATION_OVERLAY_PACKAGES = "theme_customization_overlay_packages"; field public static final String USER_SETUP_COMPLETE = "user_setup_complete"; field public static final int USER_SETUP_PERSONALIZATION_COMPLETE = 10; // 0xa @@ -9516,6 +9519,7 @@ package android.util { field public static final int PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED = 5; // 0x5 field public static final int PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED = 1; // 0x1 field public static final int PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED = 3; // 0x3 + field public static final int PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION = 9; // 0x9 field public static final int PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED = 2; // 0x2 field public static final int PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED = 6; // 0x6 field public static final int PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE = 7; // 0x7 diff --git a/api/test-current.txt b/api/test-current.txt index 242cac3f2333..b35b90f70749 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -655,8 +655,13 @@ package android.content.pm { ctor public LauncherApps(android.content.Context); } + public static class PackageInstaller.SessionInfo implements android.os.Parcelable { + method @NonNull public java.util.Set<java.lang.String> getWhitelistedRestrictedPermissions(); + } + public static class PackageInstaller.SessionParams implements android.os.Parcelable { method public void setEnableRollback(boolean); + method @RequiresPermission("android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS") public void setGrantedRuntimePermissions(String[]); method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setInstallAsApex(); method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setStaged(); } @@ -679,7 +684,11 @@ package android.content.pm { method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS"}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, int, int, @NonNull android.os.UserHandle); field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage"; field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption"; + field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000 field public static final int FLAG_PERMISSION_POLICY_FIXED = 4; // 0x4 + field public static final int FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT = 2048; // 0x800 + field public static final int FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT = 4096; // 0x1000 + field public static final int FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT = 8192; // 0x2000 field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40 field public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8 field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80 @@ -688,6 +697,7 @@ package android.content.pm { field public static final int FLAG_PERMISSION_USER_SET = 1; // 0x1 field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000 field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000 + field public static boolean RESTRICTED_PERMISSIONS_ENABLED; field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services"; field public static final String SYSTEM_SHARED_LIBRARY_SHARED = "android.ext.shared"; } @@ -1815,7 +1825,7 @@ package android.os { } public static interface RemoteCallback.OnResultListener { - method public void onResult(android.os.Bundle); + method public void onResult(@Nullable android.os.Bundle); } public final class StrictMode { @@ -2096,6 +2106,7 @@ package android.provider { method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(@NonNull String, @NonNull String, @Nullable String, boolean); field public static final String NAMESPACE_AUTOFILL = "autofill"; field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture"; + field public static final String NAMESPACE_PRIVACY = "privacy"; field public static final String NAMESPACE_ROLLBACK = "rollback"; field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot"; } @@ -2108,11 +2119,6 @@ package android.provider { method public void onPropertyChanged(@NonNull String, @NonNull String, @Nullable String); } - public static interface DeviceConfig.Privacy { - field public static final String NAMESPACE = "privacy"; - field public static final String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED = "location_access_check_enabled"; - } - public static class DeviceConfig.Properties { method public boolean getBoolean(@NonNull String, boolean); method public float getFloat(@NonNull String, float); @@ -2167,7 +2173,6 @@ package android.provider { field public static final String LOW_POWER_MODE = "low_power"; field public static final String LOW_POWER_MODE_STICKY = "low_power_sticky"; field public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices"; - field public static final String SMS_ACCESS_RESTRICTION_ENABLED = "sms_access_restriction_enabled"; field public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package"; } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 8986c6fd02c2..146cf0cd2da9 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -5453,6 +5453,8 @@ message PermissionGrantRequestResultReported { USER_DENIED_WITH_PREJUDICE = 7; // permission was automatically denied AUTO_DENIED = 8; + // permission request was ignored because permission is restricted + IGNORED_RESTRICTED_PERMISSION = 9; } // The result of the permission grant optional Result result = 6; diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 4b0b8cb12d5e..15982a796a7e 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -30,9 +30,7 @@ import android.app.usage.UsageStatsManager; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; -import android.database.ContentObserver; import android.media.AudioAttributes.AttributeUsage; -import android.net.Uri; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; @@ -42,7 +40,6 @@ import android.os.RemoteCallback; import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserManager; -import android.provider.Settings; import android.util.ArrayMap; import android.util.LongSparseArray; import android.util.LongSparseLongArray; @@ -70,7 +67,6 @@ import java.util.HashMap; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Supplier; @@ -1757,21 +1753,21 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, // VIBRATE AppOpsManager.MODE_ALLOWED, // READ_CONTACTS AppOpsManager.MODE_ALLOWED, // WRITE_CONTACTS - AppOpsManager.MODE_ALLOWED, // READ_CALL_LOG - AppOpsManager.MODE_ALLOWED, // WRITE_CALL_LOG + AppOpsManager.MODE_DEFAULT, // READ_CALL_LOG + AppOpsManager.MODE_DEFAULT, // WRITE_CALL_LOG AppOpsManager.MODE_ALLOWED, // READ_CALENDAR AppOpsManager.MODE_ALLOWED, // WRITE_CALENDAR AppOpsManager.MODE_ALLOWED, // WIFI_SCAN AppOpsManager.MODE_ALLOWED, // POST_NOTIFICATION AppOpsManager.MODE_ALLOWED, // NEIGHBORING_CELLS AppOpsManager.MODE_ALLOWED, // CALL_PHONE - AppOpsManager.MODE_ALLOWED, // READ_SMS + AppOpsManager.MODE_DEFAULT, // READ_SMS AppOpsManager.MODE_IGNORED, // WRITE_SMS - AppOpsManager.MODE_ALLOWED, // RECEIVE_SMS + AppOpsManager.MODE_DEFAULT, // RECEIVE_SMS AppOpsManager.MODE_ALLOWED, // RECEIVE_EMERGENCY_BROADCAST - AppOpsManager.MODE_ALLOWED, // RECEIVE_MMS - AppOpsManager.MODE_ALLOWED, // RECEIVE_WAP_PUSH - AppOpsManager.MODE_ALLOWED, // SEND_SMS + AppOpsManager.MODE_DEFAULT, // RECEIVE_MMS + AppOpsManager.MODE_DEFAULT, // RECEIVE_WAP_PUSH + AppOpsManager.MODE_DEFAULT, // SEND_SMS AppOpsManager.MODE_ALLOWED, // READ_ICC_SMS AppOpsManager.MODE_ALLOWED, // WRITE_ICC_SMS AppOpsManager.MODE_DEFAULT, // WRITE_SETTINGS @@ -1805,10 +1801,10 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, // READ_PHONE_STATE AppOpsManager.MODE_ALLOWED, // ADD_VOICEMAIL AppOpsManager.MODE_ALLOWED, // USE_SIP - AppOpsManager.MODE_ALLOWED, // PROCESS_OUTGOING_CALLS + AppOpsManager.MODE_DEFAULT, // PROCESS_OUTGOING_CALLS AppOpsManager.MODE_ALLOWED, // USE_FINGERPRINT AppOpsManager.MODE_ALLOWED, // BODY_SENSORS - AppOpsManager.MODE_ALLOWED, // READ_CELL_BROADCASTS + AppOpsManager.MODE_DEFAULT, // READ_CELL_BROADCASTS AppOpsManager.MODE_ERRORED, // MOCK_LOCATION AppOpsManager.MODE_ALLOWED, // READ_EXTERNAL_STORAGE AppOpsManager.MODE_ALLOWED, // WRITE_EXTERNAL_STORAGE @@ -2101,51 +2097,9 @@ public class AppOpsManager { * @hide */ public static @Mode int opToDefaultMode(int op) { - // STOPSHIP b/118520006: Hardcode the default values once the feature is stable. - switch (op) { - // SMS permissions - case AppOpsManager.OP_SEND_SMS: - case AppOpsManager.OP_RECEIVE_SMS: - case AppOpsManager.OP_READ_SMS: - case AppOpsManager.OP_RECEIVE_WAP_PUSH: - case AppOpsManager.OP_RECEIVE_MMS: - case AppOpsManager.OP_READ_CELL_BROADCASTS: - // CallLog permissions - case AppOpsManager.OP_READ_CALL_LOG: - case AppOpsManager.OP_WRITE_CALL_LOG: - case AppOpsManager.OP_PROCESS_OUTGOING_CALLS: { - if (sSmsAndCallLogRestrictionEnabled.get() == 1) { - return AppOpsManager.MODE_DEFAULT; - } - } - } return sOpDefaultMode[op]; } - // STOPSHIP b/118520006: Hardcode the default values once the feature is stable. - private static final AtomicInteger sSmsAndCallLogRestrictionEnabled = new AtomicInteger(-1); - - // STOPSHIP b/118520006: Hardcode the default values once the feature is stable. - static { - final Context context = ActivityThread.currentApplication(); - if (context != null) { - sSmsAndCallLogRestrictionEnabled.set(ActivityThread.currentActivityThread() - .getIntCoreSetting(Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, 0)); - - final Uri uri = - Settings.Global.getUriFor(Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED); - context.getContentResolver().registerContentObserver(uri, false, new ContentObserver( - context.getMainThreadHandler()) { - @Override - public void onChange(boolean selfChange) { - sSmsAndCallLogRestrictionEnabled.set(Settings.Global.getInt( - context.getContentResolver(), - Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, 0)); - } - }); - } - } - /** * Retrieve the default mode for the app op. * diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java index faa30f3a98b8..2e59b903f06d 100644 --- a/core/java/android/app/ApplicationLoaders.java +++ b/core/java/android/app/ApplicationLoaders.java @@ -145,8 +145,7 @@ public class ApplicationLoaders { */ public void createAndCacheNonBootclasspathSystemClassLoaders(SharedLibraryInfo[] libs) { if (mSystemLibsCacheMap != null) { - Log.wtf(TAG, "Already cached."); - return; + throw new IllegalStateException("Already cached."); } mSystemLibsCacheMap = new HashMap<String, CachedClassLoader>(); @@ -159,7 +158,8 @@ public class ApplicationLoaders { /** * Caches a single non-bootclasspath class loader. * - * All of this library's dependencies must have previously been cached. + * All of this library's dependencies must have previously been cached. Otherwise, an exception + * is thrown. */ private void createAndCacheNonBootclasspathSystemClassLoader(SharedLibraryInfo lib) { String path = lib.getPath(); @@ -174,9 +174,8 @@ public class ApplicationLoaders { CachedClassLoader cached = mSystemLibsCacheMap.get(dependencyPath); if (cached == null) { - Log.e(TAG, "Failed to find dependency " + dependencyPath - + " of cached library " + path); - return; + throw new IllegalStateException("Failed to find dependency " + dependencyPath + + " of cachedlibrary " + path); } sharedLibraries.add(cached.loader); @@ -189,8 +188,8 @@ public class ApplicationLoaders { null /*cacheKey*/, null /*classLoaderName*/, sharedLibraries /*sharedLibraries*/); if (classLoader == null) { - Log.e(TAG, "Failed to cache " + path); - return; + // bad configuration or break in classloading code + throw new IllegalStateException("Failed to cache " + path); } CachedClassLoader cached = new CachedClassLoader(); @@ -215,7 +214,7 @@ public class ApplicationLoaders { * * If there is an error or the cache is not available, this returns null. */ - private ClassLoader getCachedNonBootclasspathSystemLib(String zip, ClassLoader parent, + public ClassLoader getCachedNonBootclasspathSystemLib(String zip, ClassLoader parent, String classLoaderName, List<ClassLoader> sharedLibraries) { if (mSystemLibsCacheMap == null) { return null; diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index a906790c45ab..82a34ceea8b9 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -89,6 +89,7 @@ import android.system.OsConstants; import android.system.StructStat; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.IconDrawableFactory; import android.util.LauncherIcons; import android.util.Log; @@ -111,6 +112,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; /** @hide */ public class ApplicationPackageManager extends PackageManager { @@ -718,6 +720,43 @@ public class ApplicationPackageManager extends PackageManager { } @Override + public @NonNull Set<String> getWhitelistedRestrictedPermissions( + @NonNull String packageName, @PermissionWhitelistFlags int whitelistFlags) { + try { + final List<String> whitelist = mPM.getWhitelistedRestrictedPermissions( + packageName, whitelistFlags, getUserId()); + if (whitelist != null) { + return new ArraySet<>(whitelist); + } + return Collections.emptySet(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public boolean addWhitelistedRestrictedPermission(@NonNull String packageName, + @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags) { + try { + return mPM.addWhitelistedRestrictedPermission(packageName, permission, + whitelistFlags, getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public boolean removeWhitelistedRestrictedPermission(@NonNull String packageName, + @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags) { + try { + return mPM.removeWhitelistedRestrictedPermission(packageName, permission, + whitelistFlags, getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override @UnsupportedAppUsage public boolean shouldShowRequestPermissionRationale(String permission) { try { @@ -2634,13 +2673,15 @@ public class ApplicationPackageManager extends PackageManager { } } - /** - * @hide - */ @Override public boolean isUpgrade() { + return isDeviceUpgrading(); + } + + @Override + public boolean isDeviceUpgrading() { try { - return mPM.isUpgrade(); + return mPM.isDeviceUpgrading(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index d67bfb6c9c84..8749b108c168 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -50,6 +50,7 @@ import android.content.om.IOverlayManager; import android.content.om.OverlayManager; import android.content.pm.CrossProfileApps; import android.content.pm.ICrossProfileApps; +import android.content.pm.IPackageManager; import android.content.pm.IShortcutService; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; @@ -1239,14 +1240,16 @@ final class SystemServiceRegistry { new CachedServiceFetcher<PermissionManager>() { @Override public PermissionManager createService(ContextImpl ctx) { - return new PermissionManager(ctx.getOuterContext()); + IPackageManager packageManager = AppGlobals.getPackageManager(); + return new PermissionManager(ctx.getOuterContext(), packageManager); }}); registerService(Context.PERMISSION_CONTROLLER_SERVICE, PermissionControllerManager.class, new CachedServiceFetcher<PermissionControllerManager>() { @Override public PermissionControllerManager createService(ContextImpl ctx) { - return new PermissionControllerManager(ctx.getOuterContext()); + return new PermissionControllerManager(ctx.getOuterContext(), + ctx.getMainThreadHandler()); }}); registerService(Context.ROLE_SERVICE, RoleManager.class, diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index fb4eb5ae4fdd..3f7b29109b3d 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1730,14 +1730,6 @@ public class DevicePolicyManager { */ public static final String DELEGATION_CERT_SELECTION = "delegation-cert-selection"; - - /** - * Delegation of silent APK installation via {@link android.content.pm.PackageInstaller} APIs. - * - * <p> Can only be delegated by Device Owner. - */ - public static final String DELEGATION_PACKAGE_INSTALLATION = "delegation-package-installation"; - /** * No management for current user in-effect. This is the default. * @hide diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java index 907d1f727bd4..a8f89df8058c 100644 --- a/core/java/android/app/job/JobInfo.java +++ b/core/java/android/app/job/JobInfo.java @@ -1596,6 +1596,16 @@ public class JobInfo implements Parcelable { } return new JobInfo(this); } + + /** + * @hide + */ + public String summarize() { + final String service = (mJobService != null) + ? mJobService.flattenToShortString() + : "null"; + return "JobInfo.Builder{job:" + mJobId + "/" + service + "}"; + } } /** diff --git a/core/java/android/app/role/IRoleController.aidl b/core/java/android/app/role/IRoleController.aidl index a472eacb28a1..19762e0841b1 100644 --- a/core/java/android/app/role/IRoleController.aidl +++ b/core/java/android/app/role/IRoleController.aidl @@ -33,8 +33,6 @@ oneway interface IRoleController { void onClearRoleHolders(in String roleName, int flags, in RemoteCallback callback); - void onSmsKillSwitchToggled(boolean enabled); - void isApplicationQualifiedForRole(in String roleName, in String packageName, in RemoteCallback callback); diff --git a/core/java/android/app/role/RoleControllerManager.java b/core/java/android/app/role/RoleControllerManager.java index bd981179e2ff..027e152ee95f 100644 --- a/core/java/android/app/role/RoleControllerManager.java +++ b/core/java/android/app/role/RoleControllerManager.java @@ -121,13 +121,6 @@ public class RoleControllerManager { } /** - * @see RoleControllerService#onSmsKillSwitchToggled(boolean) - */ - public void onSmsKillSwitchToggled(boolean enabled) { - mRemoteService.scheduleAsyncRequest(new OnSmsKillSwitchToggledRequest(enabled)); - } - - /** * @see RoleControllerService#onIsApplicationQualifiedForRole(String, String) */ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) @@ -416,28 +409,6 @@ public class RoleControllerManager { } /** - * Request for {@link #onSmsKillSwitchToggled(boolean)} - */ - private static final class OnSmsKillSwitchToggledRequest - implements AbstractRemoteService.AsyncRequest<IRoleController> { - - private final boolean mEnabled; - - private OnSmsKillSwitchToggledRequest(boolean enabled) { - mEnabled = enabled; - } - - @Override - public void run(@NonNull IRoleController service) { - try { - service.onSmsKillSwitchToggled(mEnabled); - } catch (RemoteException e) { - Log.e(LOG_TAG, "Error calling onSmsKillSwitchToggled()", e); - } - } - } - - /** * Request for {@link #isApplicationQualifiedForRole(String, String, Executor, Consumer)} */ private static final class IsApplicationQualifiedForRoleRequest extends diff --git a/core/java/android/app/role/RoleControllerService.java b/core/java/android/app/role/RoleControllerService.java index 312761d43afd..2bc94560edd7 100644 --- a/core/java/android/app/role/RoleControllerService.java +++ b/core/java/android/app/role/RoleControllerService.java @@ -131,15 +131,6 @@ public abstract class RoleControllerService extends Service { roleName, flags, callback)); } - @Override - public void onSmsKillSwitchToggled(boolean enabled) { - enforceCallerSystemUid("onSmsKillSwitchToggled"); - - mWorkerHandler.sendMessage(PooledLambda.obtainMessage( - RoleControllerService::onSmsKillSwitchToggled, RoleControllerService.this, - enabled)); - } - private void enforceCallerSystemUid(@NonNull String methodName) { if (Binder.getCallingUid() != Process.SYSTEM_UID) { throw new SecurityException("Only the system process can call " + methodName @@ -259,17 +250,6 @@ public abstract class RoleControllerService extends Service { @RoleManager.ManageHoldersFlags int flags); /** - * Cleanup appop/permissions state in response to sms kill switch toggle - * - * @param enabled whether kill switch was turned on - * - * @hide - */ - //STOPSHIP: remove this api before shipping a final version - @WorkerThread - public abstract void onSmsKillSwitchToggled(boolean enabled); - - /** * Check whether an application is qualified for a role. * * @param roleName name of the role to check for diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index ddfe75568eee..71242fbac9a5 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -620,13 +620,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall private int noteProxyOp(String callingPkg, int op) { if (op != AppOpsManager.OP_NONE) { int mode = mAppOpsManager.noteProxyOp(op, callingPkg); - int nonDefaultMode = mode == MODE_DEFAULT ? interpretDefaultAppOpMode(op) : mode; - if (mode == MODE_DEFAULT && nonDefaultMode == MODE_IGNORED) { - Log.w(TAG, "Denying access for " + callingPkg + " to " + getClass().getName() - + " (" + AppOpsManager.opToName(op) - + " = " + AppOpsManager.opToName(mode) + ")"); - } - return mode == MODE_DEFAULT ? nonDefaultMode : mode; + return mode == MODE_DEFAULT ? MODE_IGNORED : mode; } return AppOpsManager.MODE_ALLOWED; @@ -654,16 +648,6 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall return mTransport.noteProxyOp(callingPkg, AppOpsManager.permissionToOpCode(permission)); } - /** - * Allows for custom interpretations of {@link AppOpsManager#MODE_DEFAULT} by individual - * content providers - * - * @hide - */ - protected int interpretDefaultAppOpMode(int op) { - return MODE_IGNORED; - } - /** {@hide} */ protected int enforceReadPermissionInner(Uri uri, String callingPkg, IBinder callerToken) throws SecurityException { diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index fb2218778c9b..cf704d52cba0 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -130,6 +130,15 @@ interface IPackageManager { void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId); + List<String> getWhitelistedRestrictedPermissions(String packageName, int flags, + int userId); + + boolean addWhitelistedRestrictedPermission(String packageName, String permission, + int whitelistFlags, int userId); + + boolean removeWhitelistedRestrictedPermission(String packageName, String permission, + int whitelistFlags, int userId); + boolean shouldShowRequestPermissionRationale(String permissionName, String packageName, int userId); @@ -643,7 +652,7 @@ interface IPackageManager { boolean isFirstBoot(); boolean isOnlyCoreApps(); - boolean isUpgrade(); + boolean isDeviceUpgrading(); void setPermissionEnforced(String permission, boolean enforced); boolean isPermissionEnforced(String permission); @@ -757,4 +766,8 @@ interface IPackageManager { List<ModuleInfo> getInstalledModules(int flags); ModuleInfo getModuleInfo(String packageName, int flags); + + int getRuntimePermissionsVersion(int userId); + + void setRuntimePermissionsVersion(int version, int userId); } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index ec2e8ca26060..7fe840c11dfa 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -30,6 +30,7 @@ import android.annotation.UnsupportedAppUsage; import android.app.ActivityManager; import android.app.AppGlobals; import android.content.Intent; +import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.PackageManager.DeleteFlags; import android.content.pm.PackageManager.InstallReason; @@ -48,6 +49,7 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.system.ErrnoException; import android.system.Os; +import android.util.ArraySet; import android.util.ExceptionUtils; import com.android.internal.util.IndentingPrintWriter; @@ -62,8 +64,10 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.security.MessageDigest; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.concurrent.Executor; /** @@ -80,8 +84,6 @@ import java.util.concurrent.Executor; * <ul> * <li>the device owner * <li>the affiliated profile owner - * <li>the device owner delegated app with - * {@link android.app.admin.DevicePolicyManager#DELEGATION_PACKAGE_INSTALLATION} * </ul> * <p> * Sessions can install brand new apps, upgrade existing apps, or add new splits @@ -534,8 +536,6 @@ public class PackageInstaller { * <li>the current "installer of record" for the package * <li>the device owner * <li>the affiliated profile owner - * <li>the device owner delegated app with - * {@link android.app.admin.DevicePolicyManager#DELEGATION_PACKAGE_INSTALLATION} * </ul> * * @param packageName The package to uninstall. @@ -1265,6 +1265,11 @@ public class PackageInstaller { */ public static final int MODE_INHERIT_EXISTING = 2; + /** + * Special constant to refer to all restricted permissions. + */ + public static final @NonNull Set<String> RESTRICTED_PERMISSIONS_ALL = new ArraySet<>(); + /** {@hide} */ public static final int UID_UNKNOWN = -1; @@ -1306,6 +1311,8 @@ public class PackageInstaller { /** {@hide} */ public String[] grantedRuntimePermissions; /** {@hide} */ + public List<String> whitelistedRestrictedPermissions; + /** {@hide} */ public String installerPackageName; /** {@hide} */ public boolean isMultiPackage; @@ -1339,6 +1346,7 @@ public class PackageInstaller { abiOverride = source.readString(); volumeUuid = source.readString(); grantedRuntimePermissions = source.readStringArray(); + whitelistedRestrictedPermissions = source.createStringArrayList(); installerPackageName = source.readString(); isMultiPackage = source.readBoolean(); isStaged = source.readBoolean(); @@ -1360,6 +1368,7 @@ public class PackageInstaller { ret.abiOverride = abiOverride; ret.volumeUuid = volumeUuid; ret.grantedRuntimePermissions = grantedRuntimePermissions; + ret.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions; ret.installerPackageName = installerPackageName; ret.isMultiPackage = isMultiPackage; ret.isStaged = isStaged; @@ -1474,6 +1483,7 @@ public class PackageInstaller { * * @hide */ + @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS) public void setGrantedRuntimePermissions(String[] permissions) { @@ -1482,6 +1492,43 @@ public class PackageInstaller { } /** + * Sets which restricted permissions to be whitelisted for the app. Whitelisting + * is not granting the permissions, rather it allows the app to hold permissions + * which are otherwise restricted. Whitelisting a non restricted permission has + * no effect. + * + * <p> Permissions can be hard restricted which means that the app cannot hold + * them or soft restricted where the app can hold the permission but in a weaker + * form. Whether a permission is {@link PermissionInfo#FLAG_HARD_RESTRICTED hard + * restricted} or {@link PermissionInfo#FLAG_SOFT_RESTRICTED soft restricted} + * depends on the permission declaration. Whitelisting a hard restricted permission + * allows the app to hold that permission and whitelisting a soft restricted + * permission allows the app to hold the permission in its full, unrestricted form. + * + * <p>The whitelisted permissions would be applied as the {@link + * PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER installer whitelist}. + * + * @param permissions The restricted permissions to whitelist. Pass + * {@link #RESTRICTED_PERMISSIONS_ALL} to whitelist all permissions and + * <code>null</code> to clear. If you want to whitelist some permissions + * (not all) the list must contain at least one permission. + * + * @see PackageManager#addWhitelistedRestrictedPermission(String, String, int) + * @see PackageManager#removeWhitelistedRestrictedPermission(String, String, int) + */ + public void setWhitelistedRestrictedPermissions(@Nullable Set<String> permissions) { + if (permissions == RESTRICTED_PERMISSIONS_ALL) { + installFlags |= PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS; + } + if (permissions != null) { + this.whitelistedRestrictedPermissions = new ArrayList<>(permissions); + } else { + installFlags &= ~PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS; + this.whitelistedRestrictedPermissions = null; + } + } + + /** * Request that rollbacks be enabled or disabled for the given upgrade. * * <p>If the parent session is staged or has rollback enabled, all children sessions @@ -1656,6 +1703,7 @@ public class PackageInstaller { pw.printPair("abiOverride", abiOverride); pw.printPair("volumeUuid", volumeUuid); pw.printPair("grantedRuntimePermissions", grantedRuntimePermissions); + pw.printPair("whitelistedRestrictedPermissions", whitelistedRestrictedPermissions); pw.printPair("installerPackageName", installerPackageName); pw.printPair("isMultiPackage", isMultiPackage); pw.printPair("isStaged", isStaged); @@ -1683,6 +1731,7 @@ public class PackageInstaller { dest.writeString(abiOverride); dest.writeString(volumeUuid); dest.writeStringArray(grantedRuntimePermissions); + dest.writeStringList(whitelistedRestrictedPermissions); dest.writeString(installerPackageName); dest.writeBoolean(isMultiPackage); dest.writeBoolean(isStaged); @@ -1794,6 +1843,8 @@ public class PackageInstaller { public Uri referrerUri; /** {@hide} */ public String[] grantedRuntimePermissions; + /** {@hide}*/ + public List<String> whitelistedRestrictedPermissions; /** {@hide} */ public int installFlags; /** {@hide} */ @@ -1847,6 +1898,8 @@ public class PackageInstaller { originatingUid = source.readInt(); referrerUri = source.readParcelable(null); grantedRuntimePermissions = source.readStringArray(); + whitelistedRestrictedPermissions = source.createStringArrayList(); + installFlags = source.readInt(); isMultiPackage = source.readBoolean(); isStaged = source.readBoolean(); @@ -2049,6 +2102,25 @@ public class PackageInstaller { } /** + * Get the value set in {@link SessionParams#setWhitelistedRestrictedPermissions(Set)}. + * Note that if all permissions are whitelisted this method returns {@link + * SessionParams#RESTRICTED_PERMISSIONS_ALL}. + * + * @hide + */ + @TestApi + @SystemApi + public @NonNull Set<String> getWhitelistedRestrictedPermissions() { + if ((installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0) { + return SessionParams.RESTRICTED_PERMISSIONS_ALL; + } + if (whitelistedRestrictedPermissions != null) { + return new ArraySet<>(whitelistedRestrictedPermissions); + } + return Collections.emptySet(); + } + + /** * Get the value set in {@link SessionParams#setAllowDowngrade(boolean)}. * * @deprecated use {@link #getRequestDowngrade()}. @@ -2277,6 +2349,7 @@ public class PackageInstaller { dest.writeInt(originatingUid); dest.writeParcelable(referrerUri, flags); dest.writeStringArray(grantedRuntimePermissions); + dest.writeStringList(whitelistedRestrictedPermissions); dest.writeInt(installFlags); dest.writeBoolean(isMultiPackage); dest.writeBoolean(isStaged); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 025d8f9507f9..83e15e8daa39 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -69,8 +69,10 @@ import dalvik.system.VMRuntime; import java.io.File; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Set; /** * Class for retrieving various kinds of information related to the application @@ -84,6 +86,11 @@ public abstract class PackageManager { /** {@hide} */ public static final boolean APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE = true; + /** {@hide} */ + @SystemApi + @TestApi + public static boolean RESTRICTED_PERMISSIONS_ENABLED = false; + /** * This exception is thrown when a given package, application, or component * name cannot be found. @@ -712,6 +719,7 @@ public abstract class PackageManager { INSTALL_ALL_USERS, INSTALL_REQUEST_DOWNGRADE, INSTALL_GRANT_RUNTIME_PERMISSIONS, + INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS, INSTALL_FORCE_VOLUME_UUID, INSTALL_FORCE_PERMISSION_PROMPT, INSTALL_INSTANT_APP, @@ -794,6 +802,16 @@ public abstract class PackageManager { */ public static final int INSTALL_GRANT_RUNTIME_PERMISSIONS = 0x00000100; + /** + * Flag parameter for {@link #installPackage} to indicate that all restricted + * permissions should be whitelisted. If {@link #INSTALL_ALL_USERS} + * is set the restricted permissions will be whitelisted for all users, otherwise + * only to the owner. + * + * @hide + */ + public static final int INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS = 0x00000200; + /** {@hide} */ public static final int INSTALL_FORCE_VOLUME_UUID = 0x00000200; @@ -3075,14 +3093,72 @@ public abstract class PackageManager { public static final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED = 1 << 9; /** - * Mask for all permission flags present in Android P + * Permission flag: The permission is restricted but the app is exempt + * from the restriction and is allowed to hold this permission in its + * full form and the exemption is provided by the installer on record. + * + * @hide + */ + @TestApi + @SystemApi + public static final int FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT = 1 << 11; + + /** + * Permission flag: The permission is restricted but the app is exempt + * from the restriction and is allowed to hold this permission in its + * full form and the exemption is provided by the system due to its + * permission policy. * - * @deprecated This constant does not contain useful information and should never have been - * exposed. When checking permission flags always flag each flag explicitly and ignore all - * flags that do not matter for this particular code. + * @hide + */ + @TestApi + @SystemApi + public static final int FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT = 1 << 12; + + /** + * Permission flag: The permission is restricted but the app is exempt + * from the restriction and is allowed to hold this permission and the + * exemption is provided by the system when upgrading from an OS version + * where the permission was not restricted to an OS version where the + * permission is restricted. * * @hide */ + @TestApi + @SystemApi + public static final int FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT = 1 << 13; + + + /** + * Permission flag: The permission is disabled but may be granted. If + * disabled the data protected by the permission should be protected + * by a no-op (empty list, default error, etc) instead of crashing the + * client. + * + * @hide + */ + @TestApi + @SystemApi + public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 1 << 14; + + + /** + * Permission flags: Bitwise or of all permission flags allowing an + * exemption for a restricted permission. + * @hide + */ + public static final int FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT = + FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT + | FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT + | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; + + /** + * Mask for all permission flags. + * + * @hide + * + * @deprecated Don't use - does not capture all flags. + */ @Deprecated @SystemApi public static final int MASK_PERMISSION_FLAGS = 0xFF; @@ -3092,7 +3168,20 @@ public abstract class PackageManager { * * @hide */ - public static final int MASK_PERMISSION_FLAGS_ALL = 0x3FF; + public static final int MASK_PERMISSION_FLAGS_ALL = FLAG_PERMISSION_USER_SET + | FLAG_PERMISSION_USER_FIXED + | FLAG_PERMISSION_POLICY_FIXED + | FLAG_PERMISSION_REVOKE_ON_UPGRADE + | FLAG_PERMISSION_SYSTEM_FIXED + | FLAG_PERMISSION_GRANTED_BY_DEFAULT + | FLAG_PERMISSION_REVIEW_REQUIRED + | FLAG_PERMISSION_REVOKE_WHEN_REQUESTED + | FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED + | FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED + | FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT + | FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT + | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT + | FLAG_PERMISSION_APPLY_RESTRICTION; /** * Injected activity in app that forwards user to setting activity of that app. @@ -3102,6 +3191,35 @@ public abstract class PackageManager { public static final String APP_DETAILS_ACTIVITY_CLASS_NAME = AppDetailsActivity.class.getName(); /** + * Permission whitelist flag: permissions whitelisted by the system. + * Permissions can also be whitelisted by the installer or on upgrade. + */ + public static final int FLAG_PERMISSION_WHITELIST_SYSTEM = 1 << 0; + + /** + * Permission whitelist flag: permissions whitelisted by the installer. + * Permissions can also be whitelisted by the system or on upgrade. + */ + public static final int FLAG_PERMISSION_WHITELIST_INSTALLER = 1 << 1; + + /** + * Permission whitelist flag: permissions whitelisted by the system + * when upgrading from an OS version where the permission was not + * restricted to an OS version where the permission is restricted. + * Permissions can also be whitelisted by the installer or the system. + */ + public static final int FLAG_PERMISSION_WHITELIST_UPGRADE = 1 << 2; + + /** @hide */ + @IntDef(flag = true, prefix = {"FLAG_PERMISSION_WHITELIST_"}, value = { + FLAG_PERMISSION_WHITELIST_SYSTEM, + FLAG_PERMISSION_WHITELIST_INSTALLER, + FLAG_PERMISSION_WHITELIST_UPGRADE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PermissionWhitelistFlags {} + + /** * This is a library that contains components apps can invoke. For * example, a services for apps to bind to, or standard chooser UI, * etc. This library is versioned and backwards compatible. Clients @@ -3824,6 +3942,10 @@ public abstract class PackageManager { /* FLAG_PERMISSION_REVOKE_WHEN_REQUESED */ + FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, + FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, + FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, + FLAG_PERMISSION_APPLY_RESTRICTION }) @Retention(RetentionPolicy.SOURCE) public @interface PermissionFlags {} @@ -3925,6 +4047,163 @@ public abstract class PackageManager { @PermissionFlags int flagValues, @NonNull UserHandle user); /** + * Gets the restricted permissions that have been whitelisted and the app + * is allowed to have them granted in their full form. + * + * <p> Permissions can be hard restricted which means that the app cannot hold + * them or soft restricted where the app can hold the permission but in a weaker + * form. Whether a permission is {@link PermissionInfo#FLAG_HARD_RESTRICTED hard + * restricted} or {@link PermissionInfo#FLAG_SOFT_RESTRICTED soft restricted} + * depends on the permission declaration. Whitelisting a hard restricted permission + * allows for the to hold that permission and whitelisting a soft restricted + * permission allows the app to hold the permission in its full, unrestricted form. + * + * <p><ol>There are three whitelists: + * + * <li>one for cases where the system permission policy whitelists a permission + * This list corresponds to the{@link #FLAG_PERMISSION_WHITELIST_SYSTEM} flag. + * Can only be accessed by pre-installed holders of a dedicated permission. + * + * <li>one for cases where the system whitelists the permission when upgrading + * from an OS version in which the permission was not restricted to an OS version + * in which the permission is restricted. This list corresponds to the {@link + * #FLAG_PERMISSION_WHITELIST_UPGRADE} flag. Can be accessed by pre-installed + * holders of a dedicated permission or the installer on record. + * + * <li>one for cases where the installer of the package whitelists a permission. + * This list corresponds to the {@link #FLAG_PERMISSION_WHITELIST_INSTALLER} flag. + * Can be accessed by pre-installed holders of a dedicated permission or the + * installer on record. + * + * @param packageName The app for which to get whitelisted permissions. + * @param whitelistFlag The flag to determine which whitelist to query. Only one flag + * can be passed.s + * @return The whitelisted permissions that are on any of the whitelists you query for. + * + * @see #addWhitelistedRestrictedPermission(String, String, int) + * @see #removeWhitelistedRestrictedPermission(String, String, int) + * @see #FLAG_PERMISSION_WHITELIST_SYSTEM + * @see #FLAG_PERMISSION_WHITELIST_UPGRADE + * @see #FLAG_PERMISSION_WHITELIST_INSTALLER + * + * @throws SecurityException if you try to access a whitelist that you have no access to. + */ + @RequiresPermission(value = Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS, + conditional = true) + public @NonNull Set<String> getWhitelistedRestrictedPermissions( + @NonNull String packageName, @PermissionWhitelistFlags int whitelistFlag) { + return Collections.emptySet(); + } + + /** + * Adds a whitelisted restricted permission for an app. + * + * <p> Permissions can be hard restricted which means that the app cannot hold + * them or soft restricted where the app can hold the permission but in a weaker + * form. Whether a permission is {@link PermissionInfo#FLAG_HARD_RESTRICTED hard + * restricted} or {@link PermissionInfo#FLAG_SOFT_RESTRICTED soft restricted} + * depends on the permission declaration. Whitelisting a hard restricted permission + * allows for the to hold that permission and whitelisting a soft restricted + * permission allows the app to hold the permission in its full, unrestricted form. + * + * <p><ol>There are three whitelists: + * + * <li>one for cases where the system permission policy whitelists a permission + * This list corresponds to the {@link #FLAG_PERMISSION_WHITELIST_SYSTEM} flag. + * Can only be modified by pre-installed holders of a dedicated permission. + * + * <li>one for cases where the system whitelists the permission when upgrading + * from an OS version in which the permission was not restricted to an OS version + * in which the permission is restricted. This list corresponds to the {@link + * #FLAG_PERMISSION_WHITELIST_UPGRADE} flag. Can be modified by pre-installed + * holders of a dedicated permission. The installer on record can only remove + * permissions from this whitelist. + * + * <li>one for cases where the installer of the package whitelists a permission. + * This list corresponds to the {@link #FLAG_PERMISSION_WHITELIST_INSTALLER} flag. + * Can be modified by pre-installed holders of a dedicated permission or the installer + * on record. + * + * <p>You need to specify the whitelists for which to set the whitelisted permissions + * which will clear the previous whitelisted permissions and replace them with the + * provided ones. + * + * @param packageName The app for which to get whitelisted permissions. + * @param permission The whitelisted permission to add. + * @param whitelistFlags The whitelists to which to add. Passing multiple flags + * updates all specified whitelists. + * @return Whether the permission was added to the whitelist. + * + * @see #getWhitelistedRestrictedPermissions(String, int) + * @see #removeWhitelistedRestrictedPermission(String, String, int) + * @see #FLAG_PERMISSION_WHITELIST_SYSTEM + * @see #FLAG_PERMISSION_WHITELIST_UPGRADE + * @see #FLAG_PERMISSION_WHITELIST_INSTALLER + * + * @throws SecurityException if you try to modify a whitelist that you have no access to. + */ + @RequiresPermission(value = Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS, + conditional = true) + public boolean addWhitelistedRestrictedPermission(@NonNull String packageName, + @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags) { + return false; + } + + /** + * Removes a whitelisted restricted permission for an app. + * + * <p> Permissions can be hard restricted which means that the app cannot hold + * them or soft restricted where the app can hold the permission but in a weaker + * form. Whether a permission is {@link PermissionInfo#FLAG_HARD_RESTRICTED hard + * restricted} or {@link PermissionInfo#FLAG_SOFT_RESTRICTED soft restricted} + * depends on the permission declaration. Whitelisting a hard restricted permission + * allows for the to hold that permission and whitelisting a soft restricted + * permission allows the app to hold the permission in its full, unrestricted form. + * + * <p><ol>There are three whitelists: + * + * <li>one for cases where the system permission policy whitelists a permission + * This list corresponds to the {@link #FLAG_PERMISSION_WHITELIST_SYSTEM} flag. + * Can only be modified by pre-installed holders of a dedicated permission. + * + * <li>one for cases where the system whitelists the permission when upgrading + * from an OS version in which the permission was not restricted to an OS version + * in which the permission is restricted. This list corresponds to the {@link + * #FLAG_PERMISSION_WHITELIST_UPGRADE} flag. Can be modified by pre-installed + * holders of a dedicated permission. The installer on record can only remove + * permissions from this whitelist. + * + * <li>one for cases where the installer of the package whitelists a permission. + * This list corresponds to the {@link #FLAG_PERMISSION_WHITELIST_INSTALLER} flag. + * Can be modified by pre-installed holders of a dedicated permission or the installer + * on record. + * + * <p>You need to specify the whitelists for which to set the whitelisted permissions + * which will clear the previous whitelisted permissions and replace them with the + * provided ones. + * + * @param packageName The app for which to get whitelisted permissions. + * @param permission The whitelisted permission to remove. + * @param whitelistFlags The whitelists from which to remove. Passing multiple flags + * updates all specified whitelists. + * @return Whether the permission was removed from the whitelist. + * + * @see #getWhitelistedRestrictedPermissions(String, int) + * @see #addWhitelistedRestrictedPermission(String, String, int) + * @see #FLAG_PERMISSION_WHITELIST_SYSTEM + * @see #FLAG_PERMISSION_WHITELIST_UPGRADE + * @see #FLAG_PERMISSION_WHITELIST_INSTALLER + * + * @throws SecurityException if you try to modify a whitelist that you have no access to. + */ + @RequiresPermission(value = Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS, + conditional = true) + public boolean removeWhitelistedRestrictedPermission(@NonNull String packageName, + @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags) { + return false; + } + + /** * Gets whether you should show UI with rationale for requesting a permission. * You should do this only if you do not have the permission and the context in * which the permission is requested does not clearly communicate to the user @@ -6515,6 +6794,13 @@ public abstract class PackageManager { public abstract boolean isUpgrade(); /** + * Returns true if the device is upgrading, such as first boot after OTA. + */ + public boolean isDeviceUpgrading() { + return false; + } + + /** * Return interface that offers the ability to install, upgrade, and remove * applications on the device. */ @@ -6739,6 +7025,10 @@ public abstract class PackageManager { case FLAG_PERMISSION_REVOKE_WHEN_REQUESTED: return "REVOKE_WHEN_REQUESTED"; case FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED: return "USER_SENSITIVE_WHEN_GRANTED"; case FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED: return "USER_SENSITIVE_WHEN_DENIED"; + case FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT: return "RESTRICTION_INSTALLER_EXEMPT"; + case FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT: return "RESTRICTION_SYSTEM_EXEMPT"; + case FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT: return "RESTRICTION_UPGRADE_EXEMPT"; + case FLAG_PERMISSION_APPLY_RESTRICTION: return "FLAG_PERMISSION_APPLY_RESTRICTION"; default: return Integer.toString(flag); } } diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index cd324afb0479..5f5473583907 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -80,6 +80,8 @@ public abstract class PackageManagerInternal { public interface PackageListObserver { /** A package was added to the system. */ void onPackageAdded(@NonNull String packageName, int uid); + /** A package was changed - either installed for a specific user or updated. */ + default void onPackageChanged(@NonNull String packageName, int uid) {} /** A package was removed from the system. */ void onPackageRemoved(@NonNull String packageName, int uid); } @@ -950,4 +952,13 @@ public abstract class PackageManagerInternal { */ public abstract void uninstallApex(String packageName, long versionCode, int userId, IntentSender intentSender); + + /** + * Whether default permission grants have been performed for a user + * since the device booted. + * + * @param userId The user id. + * @return true if default permissions + */ + public abstract boolean wereDefaultPermissionsGrantedSinceBoot(int userId); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 8981000714dd..2b23e7a2a633 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -3279,6 +3279,19 @@ public class PackageParser { perm.info.flags = sa.getInt( com.android.internal.R.styleable.AndroidManifestPermission_permissionFlags, 0); + // For now only platform runtime permissions can be restricted + if (!perm.info.isRuntime() || !"android".equals(perm.info.packageName)) { + perm.info.flags &= ~PermissionInfo.FLAG_HARD_RESTRICTED; + perm.info.flags &= ~PermissionInfo.FLAG_SOFT_RESTRICTED; + } else { + // The platform does not get to specify conflicting permissions + if ((perm.info.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0 + && (perm.info.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0) { + throw new IllegalStateException("Permission cannot be both soft and hard" + + " restricted: " + perm.info.name); + } + } + sa.recycle(); if (perm.info.protectionLevel == -1) { diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index 6a41f334827d..f838900d43d7 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -320,6 +320,27 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { public static final int FLAG_REMOVED = 1<<1; /** + * Flag for {@link #flags}, corresponding to <code>hardRestricted</code> + * value of {@link android.R.attr#permissionFlags}. + * + * <p> This permission is restricted by the platform and it would be + * grantable only to apps that meet special criteria per platform + * policy. + */ + public static final int FLAG_HARD_RESTRICTED = 1<<2; + + /** + * Flag for {@link #flags}, corresponding to <code>softRestricted</code> + * value of {@link android.R.attr#permissionFlags}. + * + * <p>This permission is restricted by the platform and it would be + * grantable in its full form to apps that meet special criteria + * per platform policy. Otherwise, a weaker form of the permission + * would be granted. The weak grant depends on the permission. + */ + public static final int FLAG_SOFT_RESTRICTED = 1<<3; + + /** * Flag for {@link #flags}, indicating that this permission has been * installed into the system's globally defined permissions. */ @@ -575,10 +596,30 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { } /** @hide */ + public boolean isHardRestricted() { + return (flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0; + } + + /** @hide */ + public boolean isSoftRestricted() { + return (flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0; + } + + /** @hide */ + public boolean isRestricted() { + return isHardRestricted() || isSoftRestricted(); + } + + /** @hide */ public boolean isAppOp() { return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0; } + /** @hide */ + public boolean isRuntime() { + return getProtection() == PROTECTION_DANGEROUS; + } + public static final @NonNull Creator<PermissionInfo> CREATOR = new Creator<PermissionInfo>() { @Override diff --git a/core/java/android/os/RemoteCallback.java b/core/java/android/os/RemoteCallback.java index 22cf404c4f57..047ba1d20056 100644 --- a/core/java/android/os/RemoteCallback.java +++ b/core/java/android/os/RemoteCallback.java @@ -29,7 +29,7 @@ import android.annotation.TestApi; public final class RemoteCallback implements Parcelable { public interface OnResultListener { - public void onResult(Bundle result); + void onResult(@Nullable Bundle result); } private final OnResultListener mListener; diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl index 4f65d244d3c1..45c01bcf255b 100644 --- a/core/java/android/permission/IPermissionController.aidl +++ b/core/java/android/permission/IPermissionController.aidl @@ -40,4 +40,5 @@ oneway interface IPermissionController { void getPermissionUsages(boolean countSystem, long numMillis, in RemoteCallback callback); void setRuntimePermissionGrantStateByDeviceAdmin(String callerPackageName, String packageName, String permission, int grantState, in RemoteCallback callback); + void grantOrUpgradeDefaultRuntimePermissions(in RemoteCallback callback); } diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java index 55fae3014666..be8973751bf8 100644 --- a/core/java/android/permission/PermissionControllerManager.java +++ b/core/java/android/permission/PermissionControllerManager.java @@ -56,7 +56,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.util.ArrayMap; import android.util.Log; -import android.util.SparseArray; +import android.util.Pair; import com.android.internal.annotations.GuardedBy; import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService; @@ -95,7 +95,8 @@ public final class PermissionControllerManager { * Global remote services (per user) used by all {@link PermissionControllerManager managers} */ @GuardedBy("sLock") - private static SparseArray<RemoteService> sRemoteServices = new SparseArray<>(1); + private static ArrayMap<Pair<Integer, Thread>, RemoteService> sRemoteServices + = new ArrayMap<>(1); /** * The key for retrieving the result from the returned bundle. @@ -217,20 +218,24 @@ public final class PermissionControllerManager { * Create a new {@link PermissionControllerManager}. * * @param context to create the manager for + * @param handler handler to schedule work * * @hide */ - public PermissionControllerManager(@NonNull Context context) { + public PermissionControllerManager(@NonNull Context context, @NonNull Handler handler) { synchronized (sLock) { - RemoteService remoteService = sRemoteServices.get(context.getUserId(), null); + Pair<Integer, Thread> key = new Pair<>(context.getUserId(), + handler.getLooper().getThread()); + RemoteService remoteService = sRemoteServices.get(key); if (remoteService == null) { Intent intent = new Intent(SERVICE_INTERFACE); intent.setPackage(context.getPackageManager().getPermissionControllerPackageName()); ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0); remoteService = new RemoteService(context.getApplicationContext(), - serviceInfo.getComponentInfo().getComponentName(), context.getUser()); - sRemoteServices.put(context.getUserId(), remoteService); + serviceInfo.getComponentInfo().getComponentName(), handler, + context.getUser()); + sRemoteServices.put(key, remoteService); } mRemoteService = remoteService; @@ -454,6 +459,23 @@ public final class PermissionControllerManager { } /** + * Grant or upgrade runtime permissions. The upgrade could be performed + * based on whether the device upgraded, whether the permission database + * version is old, or because the permission policy changed. + * + * @param executor Executor on which to invoke the callback + * @param callback Callback to receive the result + * + * @hide + */ + @RequiresPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) + public void grantOrUpgradeDefaultRuntimePermissions( + @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { + mRemoteService.scheduleRequest(new PendingGrantOrUpgradeDefaultRuntimePermissionsRequest( + mRemoteService, executor, callback)); + } + + /** * A connection to the remote service */ static final class RemoteService extends @@ -469,10 +491,10 @@ public final class PermissionControllerManager { * @param user User the remote service should be connected as */ RemoteService(@NonNull Context context, @NonNull ComponentName componentName, - @NonNull UserHandle user) { + @NonNull Handler handler, @NonNull UserHandle user) { super(context, SERVICE_INTERFACE, componentName, user.getIdentifier(), service -> Log.e(TAG, "RemoteService " + service + " died"), - context.getMainThreadHandler(), 0, false, 1); + handler, 0, false, 1); } /** @@ -1147,4 +1169,51 @@ public final class PermissionControllerManager { } } } + + /** + * Request for {@link #grantOrUpgradeDefaultRuntimePermissions(Executor, Consumer)} + */ + private static final class PendingGrantOrUpgradeDefaultRuntimePermissionsRequest extends + AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> { + private final @NonNull Consumer<Boolean> mCallback; + + private final @NonNull RemoteCallback mRemoteCallback; + + private PendingGrantOrUpgradeDefaultRuntimePermissionsRequest( + @NonNull RemoteService service, @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<Boolean> callback) { + super(service); + mCallback = callback; + + mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> { + long token = Binder.clearCallingIdentity(); + try { + callback.accept(result != null); + } finally { + Binder.restoreCallingIdentity(token); + finish(); + } + }), null); + } + + @Override + protected void onTimeout(RemoteService remoteService) { + long token = Binder.clearCallingIdentity(); + try { + mCallback.accept(false); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void run() { + try { + getService().getServiceInterface().grantOrUpgradeDefaultRuntimePermissions( + mRemoteCallback); + } catch (RemoteException e) { + Log.e(TAG, "Error granting or upgrading runtime permissions", e); + } + } + } } diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java index 2313d5972cbd..ed44367e28c6 100644 --- a/core/java/android/permission/PermissionControllerService.java +++ b/core/java/android/permission/PermissionControllerService.java @@ -178,6 +178,17 @@ public abstract class PermissionControllerService extends Service { boolean countSystem, long numMillis); /** + * Grant or upgrade runtime permissions. The upgrade could be performed + * based on whether the device upgraded, whether the permission database + * version is old, or because the permission policy changed. + * + * @see PackageManager#isDeviceUpgrading() + * @see PermissionManager#getRuntimePermissionsVersion() + * @see PermissionManager#setRuntimePermissionsVersion(int) + */ + public abstract void onGrantOrUpgradeDefaultRuntimePermissions(); + + /** * Set the runtime permission state from a device admin. * * @param callerPackageName The package name of the admin requesting the change @@ -350,6 +361,18 @@ public abstract class PermissionControllerService extends Service { PermissionControllerService.this, callerPackageName, packageName, permission, grantState, callback)); } + + @Override + public void grantOrUpgradeDefaultRuntimePermissions(@NonNull RemoteCallback callback) { + checkNotNull(callback, "callback"); + + enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, + null); + + mHandler.sendMessage(obtainMessage( + PermissionControllerService::grantOrUpgradeDefaultRuntimePermissions, + PermissionControllerService.this, callback)); + } }; } @@ -426,4 +449,9 @@ public abstract class PermissionControllerService extends Service { result.putBoolean(PermissionControllerManager.KEY_RESULT, wasSet); callback.sendResult(result); } + + private void grantOrUpgradeDefaultRuntimePermissions(@NonNull RemoteCallback callback) { + onGrantOrUpgradeDefaultRuntimePermissions(); + callback.sendResult(Bundle.EMPTY); + } } diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index 2ea706613ef8..1aa5b0656935 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -16,10 +16,19 @@ package android.permission; +import android.Manifest; +import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.annotation.TestApi; +import android.annotation.UnsupportedAppUsage; import android.content.Context; +import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; +import android.content.pm.PermissionInfo; +import android.os.RemoteException; import com.android.internal.annotations.Immutable; import com.android.server.SystemConfig; @@ -46,14 +55,53 @@ public final class PermissionManager { private final @NonNull Context mContext; + private final IPackageManager mPackageManager; + /** * Creates a new instance. * * @param context The current context in which to operate. * @hide */ - public PermissionManager(@NonNull Context context) { + public PermissionManager(@NonNull Context context, IPackageManager packageManager) { mContext = context; + mPackageManager = packageManager; + } + + /** + * Gets the version of the runtime permission database. + * + * @return The database version. + * + * @hide + */ + @TestApi + @SystemApi + @RequiresPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) + public @IntRange(from = 0) int getRuntimePermissionsVersion() { + try { + return mPackageManager.getRuntimePermissionsVersion(mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Sets the version of the runtime permission database. + * + * @param version The new version. + * + * @hide + */ + @TestApi + @SystemApi + @RequiresPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) + public void setRuntimePermissionsVersion(@IntRange(from = 0) int version) { + try { + mPackageManager.setRuntimePermissionsVersion(version, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** diff --git a/core/java/android/permission/PermissionManagerInternal.java b/core/java/android/permission/PermissionManagerInternal.java index 92dbab33a2f0..71674311965c 100644 --- a/core/java/android/permission/PermissionManagerInternal.java +++ b/core/java/android/permission/PermissionManagerInternal.java @@ -18,6 +18,7 @@ package android.permission; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.os.UserHandle; /** @@ -28,6 +29,23 @@ import android.os.UserHandle; * @hide */ public abstract class PermissionManagerInternal { + + /** + * Listener for package permission state (permissions or flags) changes. + */ + public interface OnRuntimePermissionStateChangedListener { + + /** + * Called when the runtime permission state (permissions or flags) changed. + * + * @param packageName The package for which the change happened. + * @param userId the user id for which the change happened. + */ + @Nullable + void onRuntimePermissionStateChanged(@NonNull String packageName, + @UserIdInt int userId); + } + /** * Get the state of the runtime permissions as xml file. * @@ -59,4 +77,20 @@ public abstract class PermissionManagerInternal { */ public abstract void restoreDelayedRuntimePermissions(@NonNull String packageName, @NonNull UserHandle user); + + /** + * Adds a listener for runtime permission state (permissions or flags) changes. + * + * @param listener The listener. + */ + public abstract void addOnRuntimePermissionStateChangedListener( + @NonNull OnRuntimePermissionStateChangedListener listener); + + /** + * Removes a listener for runtime permission state (permissions or flags) changes. + * + * @param listener The listener. + */ + public abstract void removeOnRuntimePermissionStateChangedListener( + @NonNull OnRuntimePermissionStateChangedListener listener); } diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 9ec77232ec28..166de3fde741 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -281,30 +281,7 @@ public final class DeviceConfig { */ @SystemApi @TestApi - public interface Privacy { - String NAMESPACE = "privacy"; - - /** - * Whether to show the Permissions Hub. - * - * @hide - */ - @SystemApi - String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled"; - - /** - * Whether to show location access check notifications. - */ - String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED = "location_access_check_enabled"; - - /** - * Whether to disable the new device identifier access restrictions. - * - * @hide - */ - String PROPERTY_DEVICE_IDENTIFIER_ACCESS_RESTRICTIONS_DISABLED = - "device_identifier_access_restrictions_disabled"; - } + public static final String NAMESPACE_PRIVACY = "privacy"; private static final Object sLock = new Object(); @GuardedBy("sLock") diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index ee0c83ea72f8..3db6b2bcaa89 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5662,17 +5662,6 @@ public final class Settings { private static final Validator ODI_CAPTIONS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; /** - * Setting to indicate that on device captions cannot be shown because the app - * which is currently playing media had opted out. - * - * @hide - */ - @SystemApi - public static final String ODI_CAPTIONS_OPTED_OUT = "odi_captions_opted_out"; - - private static final Validator ODI_CAPTIONS_OPTED_OUT_VALIDATOR = BOOLEAN_VALIDATOR; - - /** * On Android 8.0 (API level 26) and higher versions of the platform, * a 64-bit number (expressed as a hexadecimal string), unique to * each combination of app-signing key, user, and device. @@ -8998,7 +8987,6 @@ public final class Settings { VALIDATORS.put(SILENCE_CALL_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); VALIDATORS.put(SILENCE_NOTIFICATION_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); VALIDATORS.put(ODI_CAPTIONS_ENABLED, ODI_CAPTIONS_ENABLED_VALIDATOR); - VALIDATORS.put(ODI_CAPTIONS_OPTED_OUT, ODI_CAPTIONS_OPTED_OUT_VALIDATOR); } /** @@ -13391,18 +13379,6 @@ public final class Settings { "location_global_kill_switch"; /** - * If set to 1, app cannot request read sms permission unless it's the default sms handler. - * - * STOPSHIP: Remove this once we ship with the restriction enabled. - * - * @hide - */ - @SystemApi - @TestApi - public static final String SMS_ACCESS_RESTRICTION_ENABLED = - "sms_access_restriction_enabled"; - - /** * If set to 1, the device identifier check will be relaxed to the previous READ_PHONE_STATE * permission check for 3P apps. * diff --git a/core/java/android/util/StatsLogAtoms.java b/core/java/android/util/StatsLogAtoms.java index bbede5394ede..4780cb58ca06 100644 --- a/core/java/android/util/StatsLogAtoms.java +++ b/core/java/android/util/StatsLogAtoms.java @@ -78,6 +78,13 @@ public class StatsLogAtoms { /** * Possible value of {@link PermissionGrantRequestResultReported_Result}: + * permission request was ignored because it was restricted + */ + public static final int PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION = + StatsLogInternal.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION; + + /** + * Possible value of {@link PermissionGrantRequestResultReported_Result}: * permission was granted by user action */ public static final int PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED = diff --git a/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java index 206efa94b7f6..c3aa847acae1 100644 --- a/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java +++ b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java @@ -32,7 +32,6 @@ import java.util.ArrayList; * * @param <S> the concrete remote service class * @param <I> the interface of the binder service - * @hide */ public abstract class AbstractMultiplePendingRequestsRemoteService<S extends AbstractMultiplePendingRequestsRemoteService<S, I>, I extends IInterface> diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index 329c6b395bba..66bbe22c9969 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -73,6 +73,11 @@ public class ResolverDrawerLayout extends ViewGroup { */ private float mCollapseOffset; + /** + * Track fractions of pixels from drag calculations. Without this, the view offsets get + * out of sync due to frequently dropping fractions of a pixel from '(int) dy' casts. + */ + private float mDragRemainder = 0.0f; private int mCollapsibleHeight; private int mUncollapsibleHeight; private int mAlwaysShowHeight; @@ -485,6 +490,16 @@ public class ResolverDrawerLayout extends ViewGroup { mCollapsibleHeight + mUncollapsibleHeight)); if (newPos != mCollapseOffset) { dy = newPos - mCollapseOffset; + + mDragRemainder += dy - (int) dy; + if (mDragRemainder >= 1.0f) { + mDragRemainder -= 1.0f; + dy += 1.0f; + } else if (mDragRemainder <= -1.0f) { + mDragRemainder += 1.0f; + dy -= 1.0f; + } + final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index de526eff275e..b94eb1626e37 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -728,7 +728,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_sendSms" android:description="@string/permdesc_sendSms" - android:permissionFlags="costsMoney" + android:permissionFlags="costsMoney|hardRestricted" android:protectionLevel="dangerous" /> <!-- Allows an application to receive SMS messages. @@ -738,6 +738,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_receiveSms" android:description="@string/permdesc_receiveSms" + android:permissionFlags="hardRestricted" android:protectionLevel="dangerous" /> <!-- Allows an application to read SMS messages. @@ -747,6 +748,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_readSms" android:description="@string/permdesc_readSms" + android:permissionFlags="hardRestricted" android:protectionLevel="dangerous" /> <!-- Allows an application to receive WAP push messages. @@ -756,6 +758,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_receiveWapPush" android:description="@string/permdesc_receiveWapPush" + android:permissionFlags="hardRestricted" android:protectionLevel="dangerous" /> <!-- Allows an application to monitor incoming MMS messages. @@ -765,6 +768,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_receiveMms" android:description="@string/permdesc_receiveMms" + android:permissionFlags="hardRestricted" android:protectionLevel="dangerous" /> <!-- @SystemApi @TestApi Allows an application to read previously received cell broadcast @@ -783,6 +787,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_readCellBroadcasts" android:description="@string/permdesc_readCellBroadcasts" + android:permissionFlags="hardRestricted" android:protectionLevel="dangerous" /> <!-- ====================================================================== --> @@ -947,6 +952,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_readCallLog" android:description="@string/permdesc_readCallLog" + android:permissionFlags="hardRestricted" android:protectionLevel="dangerous" /> <!-- Allows an application to write (but not read) the user's @@ -966,6 +972,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_writeCallLog" android:description="@string/permdesc_writeCallLog" + android:permissionFlags="hardRestricted" android:protectionLevel="dangerous" /> <!-- Allows an application to see the number being dialed during an outgoing @@ -980,6 +987,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_processOutgoingCalls" android:description="@string/permdesc_processOutgoingCalls" + android:permissionFlags="hardRestricted" android:protectionLevel="dangerous" /> <!-- ====================================================================== --> @@ -3427,6 +3435,12 @@ <permission android:name="android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY" android:protectionLevel="signature|installer" /> + <!-- @SystemApi Allows an application to whitelist restricted permissions + on any of the whitelists. + @hide --> + <permission android:name="android.permission.WHITELIST_RESTRICTED_PERMISSIONS" + android:protectionLevel="signature|installer" /> + <!-- @hide Allows an application to observe permission changes. --> <permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" android:protectionLevel="signature|privileged" /> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 8f3f25aad182..47c243ca1e04 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -310,13 +310,24 @@ <!-- Set to indicate that this permission allows an operation that may cost the user money. Such permissions may be highlighted when shown to the user with this additional information. --> - <flag name="costsMoney" value="0x0001" /> + <flag name="costsMoney" value="0x1" /> <!-- Additional flag from base permission type: this permission has been removed and it is no longer enforced. It shouldn't be shown in the UI. Removed permissions are kept as normal permissions for backwards compatibility as apps may be checking them before calling an API. --> <flag name="removed" value="0x2" /> + <!-- This permission is restricted by the platform and it would be + grantable only to apps that meet special criteria per platform + policy. + --> + <flag name="hardRestricted" value="0x4" /> + <!-- This permission is restricted by the platform and it would be + grantable in its full form to apps that meet special criteria + per platform policy. Otherwise, a weaker form of the permission + would be granted. The weak grant depends on the permission. + --> + <flag name="softRestricted" value="0x8" /> </attr> <!-- Specified the name of a group that this permission is associated diff --git a/core/tests/coretests/src/android/app/ApplicationLoadersTest.java b/core/tests/coretests/src/android/app/ApplicationLoadersTest.java new file mode 100644 index 000000000000..4b9910c79770 --- /dev/null +++ b/core/tests/coretests/src/android/app/ApplicationLoadersTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import android.content.pm.SharedLibraryInfo; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class ApplicationLoadersTest { + + // a library installed onto the device with no dependencies + private static final String LIB_A = "/system/framework/android.hidl.base-V1.0-java.jar"; + // a library installed onto the device which only depends on A + private static final String LIB_DEP_A = "/system/framework/android.hidl.manager-V1.0-java.jar"; + + private static SharedLibraryInfo createLib(String zip) { + return new SharedLibraryInfo( + zip, null /*packageName*/, null /*codePaths*/, null /*name*/, 0 /*version*/, + SharedLibraryInfo.TYPE_BUILTIN, null /*declaringPackage*/, + null /*dependentPackages*/, null /*dependencies*/); + } + + @Test + public void testGetNonExistantLib() { + ApplicationLoaders loaders = new ApplicationLoaders(); + assertEquals(null, loaders.getCachedNonBootclasspathSystemLib( + "/system/framework/nonexistantlib.jar", null, null, null)); + } + + @Test + public void testCacheExistantLib() { + ApplicationLoaders loaders = new ApplicationLoaders(); + SharedLibraryInfo libA = createLib(LIB_A); + + loaders.createAndCacheNonBootclasspathSystemClassLoaders(new SharedLibraryInfo[]{libA}); + + assertNotEquals(null, loaders.getCachedNonBootclasspathSystemLib( + LIB_A, null, null, null)); + } + + @Test + public void testNonNullParent() { + ApplicationLoaders loaders = new ApplicationLoaders(); + SharedLibraryInfo libA = createLib(LIB_A); + + ClassLoader parent = ClassLoader.getSystemClassLoader(); + assertNotEquals(null, parent); + + loaders.createAndCacheNonBootclasspathSystemClassLoaders(new SharedLibraryInfo[]{libA}); + + assertEquals(null, loaders.getCachedNonBootclasspathSystemLib( + LIB_A, parent, null, null)); + } + + @Test + public void testNonNullClassLoaderNamespace() { + ApplicationLoaders loaders = new ApplicationLoaders(); + SharedLibraryInfo libA = createLib(LIB_A); + + loaders.createAndCacheNonBootclasspathSystemClassLoaders(new SharedLibraryInfo[]{libA}); + + assertEquals(null, loaders.getCachedNonBootclasspathSystemLib( + LIB_A, null, "other classloader", null)); + } + + @Test + public void testDifferentSharedLibraries() { + ApplicationLoaders loaders = new ApplicationLoaders(); + SharedLibraryInfo libA = createLib(LIB_A); + + // any other existant lib + ClassLoader dep = ClassLoader.getSystemClassLoader(); + ArrayList<ClassLoader> sharedLibraries = new ArrayList<>(); + sharedLibraries.add(dep); + + loaders.createAndCacheNonBootclasspathSystemClassLoaders(new SharedLibraryInfo[]{libA}); + + assertEquals(null, loaders.getCachedNonBootclasspathSystemLib( + LIB_A, null, null, sharedLibraries)); + } + + @Test + public void testDependentLibs() { + ApplicationLoaders loaders = new ApplicationLoaders(); + SharedLibraryInfo libA = createLib(LIB_A); + SharedLibraryInfo libB = createLib(LIB_DEP_A); + libB.addDependency(libA); + + loaders.createAndCacheNonBootclasspathSystemClassLoaders( + new SharedLibraryInfo[]{libA, libB}); + + ClassLoader loadA = loaders.getCachedNonBootclasspathSystemLib( + LIB_A, null, null, null); + assertNotEquals(null, loadA); + + ArrayList<ClassLoader> sharedLibraries = new ArrayList<>(); + sharedLibraries.add(loadA); + + assertNotEquals(null, loaders.getCachedNonBootclasspathSystemLib( + LIB_DEP_A, null, null, sharedLibraries)); + } + + @Test(expected = IllegalStateException.class) + public void testDependentLibsWrongOrder() { + ApplicationLoaders loaders = new ApplicationLoaders(); + SharedLibraryInfo libA = createLib(LIB_A); + SharedLibraryInfo libB = createLib(LIB_DEP_A); + libB.addDependency(libA); + + loaders.createAndCacheNonBootclasspathSystemClassLoaders( + new SharedLibraryInfo[]{libB, libA}); + } +} diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index c36ca829bb50..8cc6e37db48a 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -433,7 +433,6 @@ public class SettingsBackupTest { Settings.Global.SIGNED_CONFIG_VERSION, Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL, Settings.Global.SMART_SELECTION_UPDATE_METADATA_URL, - Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, Settings.Global.SMS_OUTGOING_CHECK_INTERVAL_MS, Settings.Global.SMS_OUTGOING_CHECK_MAX_COUNT, Settings.Global.SMS_SHORT_CODE_CONFIRMATION, @@ -650,7 +649,6 @@ public class SettingsBackupTest { Settings.Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED, Settings.Secure.ODI_CAPTIONS_ENABLED, - Settings.Secure.ODI_CAPTIONS_OPTED_OUT, Settings.Secure.PACKAGE_VERIFIER_STATE, Settings.Secure.PACKAGE_VERIFIER_USER_CONSENT, Settings.Secure.PARENTAL_CONTROL_LAST_UPDATE, diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java index 877172af1993..9c36d76cf370 100644 --- a/location/java/android/location/Location.java +++ b/location/java/android/location/Location.java @@ -599,6 +599,14 @@ public class Location implements Parcelable { * ElapsedRealtimeNanos timestamp, with the reported measurements in * nanoseconds (68% confidence). * + * This means that we have 68% confidence that the true timestamp of the + * event is within ElapsedReatimeNanos +/- uncertainty. + * + * Example : + * - getElapsedRealtimeNanos() returns 10000000 + * - getElapsedRealtimeUncertaintyNanos() returns 1000000 (equivalent to 1millisecond) + * This means that the event most likely happened between 9000000 and 11000000. + * * @return uncertainty of elapsed real-time of fix, in nanoseconds. */ public double getElapsedRealtimeUncertaintyNanos() { diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java index 9f38d48da4ed..cee4666a39ba 100755 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java @@ -139,6 +139,9 @@ public class InstallInstalling extends AlertActivity { getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME); params.installReason = PackageManager.INSTALL_REASON_USER; + // Whitelist all restricted permissions. + params.setWhitelistedRestrictedPermissions(null /*permissions*/); + File file = new File(mPackageURI.getPath()); try { PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index f597a1a85d01..c204f265f27f 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1236,11 +1236,6 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Global.SMS_SHORT_CODES_UPDATE_METADATA_URL, GlobalSettingsProto.Sms.SHORT_CODES_UPDATE_METADATA_URL); - dumpSetting(s, p, - Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, - GlobalSettingsProto.Sms.ACCESS_RESTRICTION_ENABLED); - p.end(smsToken); - final long soundsToken = p.start(GlobalSettingsProto.SOUNDS); dumpSetting(s, p, Settings.Global.CAR_DOCK_SOUND, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 296f7a144ffe..a5b78497a645 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -3610,7 +3610,7 @@ public class SettingsProvider extends ContentProvider { final boolean isUpgrade; try { - isUpgrade = mPackageManager.isUpgrade(); + isUpgrade = mPackageManager.isDeviceUpgrading(); } catch (RemoteException e) { throw new IllegalStateException("Package manager not available"); } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 2a9456dd723c..d1b7c7572361 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -99,6 +99,7 @@ <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" /> <uses-permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS" /> <uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" /> + <uses-permission android:name="android.permission.WHITELIST_RESTRICTED_PERMISSIONS" /> <uses-permission android:name="android.permission.SET_KEYBOARD_LAYOUT" /> <uses-permission android:name="android.permission.GET_DETAILED_TASKS" /> <uses-permission android:name="android.permission.SET_SCREEN_COMPATIBILITY" /> diff --git a/packages/SystemUI/res/layout/global_actions_grid_item.xml b/packages/SystemUI/res/layout/global_actions_grid_item.xml index 999c7b8c34b3..e92ec0f75fc1 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_item.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_item.xml @@ -43,6 +43,9 @@ android:id="@*android:id/message" android:layout_width="match_parent" android:layout_height="wrap_content" + android:ellipsize="marquee" + android:marqueeRepeatLimit="marquee_forever" + android:singleLine="true" android:gravity="center" android:textSize="12dp" android:textAppearance="?android:attr/textAppearanceSmall" diff --git a/packages/SystemUI/res/layout/global_actions_wrapped.xml b/packages/SystemUI/res/layout/global_actions_wrapped.xml index f932303473bd..d4410702a7d1 100644 --- a/packages/SystemUI/res/layout/global_actions_wrapped.xml +++ b/packages/SystemUI/res/layout/global_actions_wrapped.xml @@ -15,7 +15,7 @@ <!-- Global actions is right-aligned to be physically near power button --> <LinearLayout android:id="@android:id/list" - android:layout_width="@dimen/global_actions_panel_width" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top|right" android:gravity="center" @@ -26,7 +26,7 @@ <!-- For separated button--> <FrameLayout android:id="@+id/separated_button" - android:layout_width="@dimen/global_actions_panel_width" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top|right" android:layout_marginTop="6dp" diff --git a/packages/SystemUI/res/values-sw410dp/dimens.xml b/packages/SystemUI/res/values-sw410dp/dimens.xml index 73d1857c0063..fea1ef11e139 100644 --- a/packages/SystemUI/res/values-sw410dp/dimens.xml +++ b/packages/SystemUI/res/values-sw410dp/dimens.xml @@ -35,7 +35,7 @@ <dimen name="global_actions_grid_item_icon_width">24dp</dimen> <dimen name="global_actions_grid_item_icon_height">24dp</dimen> - <dimen name="global_actions_grid_item_icon_top_margin">14dp</dimen> + <dimen name="global_actions_grid_item_icon_top_margin">18dp</dimen> <dimen name="global_actions_grid_item_icon_side_margin">24dp</dimen> <dimen name="global_actions_grid_item_icon_bottom_margin">4dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index a4870d4b7e50..b1e22127cd4f 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -325,12 +325,11 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe * Adds or updates a bubble associated with the provided notification entry. * * @param notif the notification associated with this bubble. - * @param updatePosition whether this update should promote the bubble to the top of the stack. */ - public void updateBubble(NotificationEntry notif, boolean updatePosition) { + void updateBubble(NotificationEntry notif) { if (mStackView != null && mBubbleData.getBubble(notif.key) != null) { // It's an update - mStackView.updateBubble(notif, updatePosition); + mStackView.updateBubble(notif); } else { if (mStackView == null) { mStackView = new BubbleStackView(mContext, mBubbleData, mSurfaceSynchronizer); @@ -403,7 +402,7 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe return; } if (entry.isBubble() && mNotificationInterruptionStateProvider.shouldBubbleUp(entry)) { - updateBubble(entry, true /* updatePosition */); + updateBubble(entry); } } @@ -416,7 +415,7 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe && alertAgain(entry, entry.notification.getNotification())) { entry.setShowInShadeWhenBubble(true); entry.setBubbleDismissed(false); // updates come back as bubbles even if dismissed - updateBubble(entry, true /* updatePosition */); + updateBubble(entry); mStackView.updateDotVisibility(entry.key); } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index a4e1ad723720..53e65e662fb8 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -595,15 +595,13 @@ public class BubbleStackView extends FrameLayout { /** * Updates a bubble in the stack. - * - * @param entry the entry to update in the stack. - * @param updatePosition whether this bubble should be moved to top of the stack. + * @param entry the entry to update in the stack. */ - public void updateBubble(NotificationEntry entry, boolean updatePosition) { + public void updateBubble(NotificationEntry entry) { Bubble b = mBubbleData.getBubble(entry.key); mBubbleData.updateBubble(entry.key, entry); - if (updatePosition && !mIsExpanded) { + if (!mIsExpanded) { // If alerting it gets promoted to top of the stack. if (mBubbleContainer.indexOfChild(b.iconView) != 0) { mBubbleContainer.moveViewTo(b.iconView, 0); diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index e22b24e2ed46..7a3f3bef8f5e 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -505,6 +505,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } TextView messageView = v.findViewById(R.id.message); messageView.setTextColor(textColor); + messageView.setSelected(true); // necessary for marquee to work ImageView icon = (ImageView) v.findViewById(R.id.icon); icon.getDrawable().setTint(textColor); return v; @@ -1137,6 +1138,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, ImageView icon = (ImageView) v.findViewById(R.id.icon); TextView messageView = (TextView) v.findViewById(R.id.message); + messageView.setSelected(true); // necessary for marquee to work TextView statusView = (TextView) v.findViewById(R.id.status); final String status = getStatus(); @@ -1240,6 +1242,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, if (messageView != null) { messageView.setText(mMessageResId); messageView.setEnabled(enabled); + messageView.setSelected(true); // necessary for marquee to work } boolean on = ((mState == State.On) || (mState == State.TurningOn)); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index a3db533bb7d0..69d2e31135e8 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -284,9 +284,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa @Override public boolean isCaptionStreamOptedOut() { - int currentValue = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ODI_CAPTIONS_OPTED_OUT, 0); - return currentValue == 1; + // TODO(b/129768185): Removing secure setting, to be replaced by sound event listener + return false; } public void getCaptionsComponentState(boolean fromTooltip) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index 5e16721fe548..20f539bbebe5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -160,7 +160,7 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testAddBubble() { - mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */); + mBubbleController.updateBubble(mRow.getEntry()); assertTrue(mBubbleController.hasBubbles()); verify(mBubbleStateChangeListener).onHasBubblesChanged(true); @@ -169,13 +169,13 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testHasBubbles() { assertFalse(mBubbleController.hasBubbles()); - mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */); + mBubbleController.updateBubble(mRow.getEntry()); assertTrue(mBubbleController.hasBubbles()); } @Test public void testRemoveBubble() { - mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */); + mBubbleController.updateBubble(mRow.getEntry()); assertTrue(mBubbleController.hasBubbles()); verify(mBubbleStateChangeListener).onHasBubblesChanged(true); @@ -189,8 +189,8 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testDismissStack() { - mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */); - mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */); + mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mRow2.getEntry()); assertTrue(mBubbleController.hasBubbles()); mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE); @@ -206,7 +206,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Mark it as a bubble and add it explicitly mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */); + mBubbleController.updateBubble(mRow.getEntry()); // We should have bubbles & their notifs should show in the shade assertTrue(mBubbleController.hasBubbles()); @@ -235,8 +235,8 @@ public class BubbleControllerTest extends SysuiTestCase { // Mark it as a bubble and add it explicitly mEntryListener.onPendingEntryAdded(mRow.getEntry()); mEntryListener.onPendingEntryAdded(mRow2.getEntry()); - mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */); - mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */); + mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mRow2.getEntry()); // We should have bubbles & their notifs should show in the shade assertTrue(mBubbleController.hasBubbles()); @@ -272,7 +272,7 @@ public class BubbleControllerTest extends SysuiTestCase { public void testExpansionRemovesShowInShade() { // Mark it as a bubble and add it explicitly mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */); + mBubbleController.updateBubble(mRow.getEntry()); // We should have bubbles & their notifs should show in the shade assertTrue(mBubbleController.hasBubbles()); @@ -293,8 +293,8 @@ public class BubbleControllerTest extends SysuiTestCase { // Mark it as a bubble and add it explicitly mEntryListener.onPendingEntryAdded(mRow.getEntry()); mEntryListener.onPendingEntryAdded(mRow2.getEntry()); - mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */); - mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */); + mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mRow2.getEntry()); verify(mBubbleStateChangeListener).onHasBubblesChanged(true); // Expand @@ -333,7 +333,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Add the auto expand bubble mEntryListener.onPendingEntryAdded(mAutoExpandRow.getEntry()); - mBubbleController.updateBubble(mAutoExpandRow.getEntry(), true /* updatePosition */); + mBubbleController.updateBubble(mAutoExpandRow.getEntry()); // Expansion shouldn't change verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */, @@ -371,7 +371,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Add the auto expand bubble mEntryListener.onPendingEntryAdded(mAutoExpandRow.getEntry()); - mBubbleController.updateBubble(mAutoExpandRow.getEntry(), true /* updatePosition */); + mBubbleController.updateBubble(mAutoExpandRow.getEntry()); // Expansion should change verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */, @@ -387,7 +387,7 @@ public class BubbleControllerTest extends SysuiTestCase { public void testSuppressNotif_FailsNotForeground() { // Add the suppress notif bubble mEntryListener.onPendingEntryAdded(mSuppressNotifRow.getEntry()); - mBubbleController.updateBubble(mSuppressNotifRow.getEntry(), true /* updatePosition */); + mBubbleController.updateBubble(mSuppressNotifRow.getEntry()); // Should show in shade because we weren't forground assertTrue(mSuppressNotifRow.getEntry().showInShadeWhenBubble()); @@ -423,7 +423,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Add the suppress notif bubble mEntryListener.onPendingEntryAdded(mSuppressNotifRow.getEntry()); - mBubbleController.updateBubble(mSuppressNotifRow.getEntry(), true /* updatePosition */); + mBubbleController.updateBubble(mSuppressNotifRow.getEntry()); // Should NOT show in shade because we were foreground assertFalse(mSuppressNotifRow.getEntry().showInShadeWhenBubble()); @@ -438,7 +438,7 @@ public class BubbleControllerTest extends SysuiTestCase { final String key = mRow.getEntry().key; mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */); + mBubbleController.updateBubble(mRow.getEntry()); // Simulate notification cancellation. mEntryListener.onEntryRemoved(mRow.getEntry(), null /* notificationVisibility (unused) */, @@ -464,22 +464,22 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */); + mBubbleController.updateBubble(mRow.getEntry()); mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_AGED); verify(mDeleteIntent, never()).send(); } @Test public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */); + mBubbleController.updateBubble(mRow.getEntry()); mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_USER_GESTURE); verify(mDeleteIntent, times(1)).send(); } @Test public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */); - mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */); + mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mRow2.getEntry()); mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE); verify(mDeleteIntent, times(2)).send(); } diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto index c256fb270fd9..fdbc2ac97098 100644 --- a/proto/src/wifi.proto +++ b/proto/src/wifi.proto @@ -536,6 +536,15 @@ message WifiLog { // WifiLock statistics optional WifiLockStats wifi_lock_stats = 143; + + // Stats on number of times Wi-Fi is turned on/off though the WifiManager#setWifiEnabled API + optional WifiToggleStats wifi_toggle_stats = 144; + + // Number of times WifiManager#addOrUpdateNetwork is called. + optional int32 num_add_or_update_network_calls = 145; + + // Number of times WifiManager#enableNetwork is called. + optional int32 num_enable_network_calls = 146; } // Information that gets logged for every WiFi connection. @@ -2510,3 +2519,18 @@ message WifiLockStats { // This means the lock takes effect and the device takes the actions required for this mode repeated HistogramBucketInt32 low_latency_active_session_duration_sec_histogram = 6; } + +// Stats on number of times Wi-Fi is turned on/off though the WifiManager#setWifiEnabled API +message WifiToggleStats { + // Number of time Wi-Fi is turned on by privileged apps + optional int32 num_toggle_on_privileged = 1; + + // Number of time Wi-Fi is turned off by privileged apps + optional int32 num_toggle_off_privileged = 2; + + // Number of time Wi-Fi is turned on by normal apps + optional int32 num_toggle_on_normal = 3; + + // Number of time Wi-Fi is turned off by normal apps + optional int32 num_toggle_off_normal = 4; +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index feffe2f72b6f..0c681df036b4 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -77,6 +77,7 @@ public class PackageWatchdog { private static final String ATTR_VERSION = "version"; private static final String ATTR_NAME = "name"; private static final String ATTR_DURATION = "duration"; + private static final String ATTR_EXPLICIT_HEALTH_CHECK_DURATION = "health-check-duration"; private static final String ATTR_PASSED_HEALTH_CHECK = "passed-health-check"; private static PackageWatchdog sPackageWatchdog; @@ -95,20 +96,22 @@ public class PackageWatchdog { private final ArrayMap<String, ObserverInternal> mAllObservers = new ArrayMap<>(); // File containing the XML data of monitored packages /data/system/package-watchdog.xml private final AtomicFile mPolicyFile; - // Runnable to prune monitored packages that have expired - private final Runnable mPackageCleanup; private final ExplicitHealthCheckController mHealthCheckController; // Flag to control whether explicit health checks are supported or not @GuardedBy("mLock") private boolean mIsHealthCheckEnabled = true; @GuardedBy("mLock") private boolean mIsPackagesReady; - // Last SystemClock#uptimeMillis a package clean up was executed. - // 0 if mPackageCleanup not running. - private long mUptimeAtLastRescheduleMs; - // Duration a package cleanup was last scheduled for. - // 0 if mPackageCleanup not running. - private long mDurationAtLastReschedule; + // SystemClock#uptimeMillis when we last executed #pruneObservers. + // 0 if no prune is scheduled. + @GuardedBy("mLock") + private long mUptimeAtLastPruneMs; + // Duration in millis that the last prune was scheduled for. + // Used along with #mUptimeAtLastPruneMs after scheduling a prune to determine the remaining + // duration before #pruneObservers will be executed. + // 0 if no prune is scheduled. + @GuardedBy("mLock") + private long mDurationAtLastPrune; private PackageWatchdog(Context context) { // Needs to be constructed inline @@ -129,7 +132,6 @@ public class PackageWatchdog { mPolicyFile = policyFile; mShortTaskHandler = shortTaskHandler; mLongTaskHandler = longTaskHandler; - mPackageCleanup = this::rescheduleCleanup; mHealthCheckController = controller; loadFromFile(); } @@ -171,9 +173,9 @@ public class PackageWatchdog { if (internalObserver != null) { internalObserver.mRegisteredObserver = observer; } - if (mDurationAtLastReschedule == 0) { - // Nothing running, schedule - rescheduleCleanup(); + if (mDurationAtLastPrune == 0) { + // Nothing running, prune + pruneAndSchedule(); } } } @@ -208,6 +210,7 @@ public class PackageWatchdog { List<MonitoredPackage> packages = new ArrayList<>(); for (int i = 0; i < packageNames.size(); i++) { + // Health checks not available yet so health check state will start INACTIVE packages.add(new MonitoredPackage(packageNames.get(i), durationMs, false)); } @@ -225,9 +228,9 @@ public class PackageWatchdog { } } registerHealthObserver(observer); - // Always reschedule because we may need to expire packages - // earlier than we are already scheduled for - rescheduleCleanup(); + // Always prune because we may have received packges requiring an earlier + // schedule than we are currently scheduled for. + pruneAndSchedule(); Slog.i(TAG, "Syncing health check requests, observing packages " + packageNames); syncRequestsAsync(); saveToFileAsync(); @@ -312,15 +315,18 @@ public class PackageWatchdog { }); } - // TODO(b/120598832): Optimize write? Maybe only write a separate smaller file? + // TODO(b/120598832): Optimize write? Maybe only write a separate smaller file? Also + // avoid holding lock? // This currently adds about 7ms extra to shutdown thread /** Writes the package information to file during shutdown. */ public void writeNow() { - if (!mAllObservers.isEmpty()) { - mLongTaskHandler.removeCallbacks(this::saveToFile); - pruneObservers(SystemClock.uptimeMillis() - mUptimeAtLastRescheduleMs); - saveToFile(); - Slog.i(TAG, "Last write to update package durations"); + synchronized (mLock) { + if (!mAllObservers.isEmpty()) { + mLongTaskHandler.removeCallbacks(this::saveToFile); + pruneObservers(SystemClock.uptimeMillis() - mUptimeAtLastPruneMs); + saveToFile(); + Slog.i(TAG, "Last write to update package durations"); + } } } @@ -450,9 +456,10 @@ public class PackageWatchdog { private void onSupportedPackages(List<String> supportedPackages) { boolean shouldUpdateFile = false; + boolean shouldPrune = false; synchronized (mLock) { - Slog.i(TAG, "Received supported packages " + supportedPackages); + Slog.d(TAG, "Received supported packages " + supportedPackages); Iterator<ObserverInternal> oit = mAllObservers.values().iterator(); while (oit.hasNext()) { ObserverInternal observer = oit.next(); @@ -461,12 +468,31 @@ public class PackageWatchdog { while (pit.hasNext()) { MonitoredPackage monitoredPackage = pit.next(); String packageName = monitoredPackage.mName; - if (!monitoredPackage.mHasPassedHealthCheck - && !supportedPackages.contains(packageName)) { - // Hasn't passed health check but health check is not supported - Slog.i(TAG, packageName + " does not support health checks, passing"); + int healthCheckState = monitoredPackage.getHealthCheckState(); + + if (healthCheckState != MonitoredPackage.STATE_PASSED) { + // Have to update file, we will either transition state or reduce + // health check duration shouldUpdateFile = true; - monitoredPackage.mHasPassedHealthCheck = true; + + if (supportedPackages.contains(packageName)) { + // Supports health check, transition to ACTIVE if not already. + // We need to prune packages earlier than already scheduled. + shouldPrune = true; + + // TODO: Get healthCheckDuration from supportedPackages + long healthCheckDuration = monitoredPackage.mDurationMs; + monitoredPackage.mHealthCheckDurationMs = Math.min(healthCheckDuration, + monitoredPackage.mDurationMs); + Slog.i(TAG, packageName + " health check state is now: ACTIVE(" + + monitoredPackage.mHealthCheckDurationMs + "ms)"); + } else { + // Does not support health check, transistion to PASSED + monitoredPackage.mHasPassedHealthCheck = true; + Slog.i(TAG, packageName + " health check state is now: PASSED"); + } + } else { + Slog.i(TAG, packageName + " does not support health check, state: PASSED"); } } } @@ -475,6 +501,9 @@ public class PackageWatchdog { if (shouldUpdateFile) { saveToFileAsync(); } + if (shouldPrune) { + pruneAndSchedule(); + } } private Set<String> getPackagesPendingHealthChecksLocked() { @@ -496,59 +525,64 @@ public class PackageWatchdog { return packages; } - /** Reschedules handler to prune expired packages from observers. */ - private void rescheduleCleanup() { + /** Executes {@link #pruneObservers} and schedules the next execution. */ + private void pruneAndSchedule() { synchronized (mLock) { - long nextDurationToScheduleMs = getEarliestPackageExpiryLocked(); + long nextDurationToScheduleMs = getNextPruneScheduleMillisLocked(); if (nextDurationToScheduleMs == Long.MAX_VALUE) { - Slog.i(TAG, "No monitored packages, ending package cleanup"); - mDurationAtLastReschedule = 0; - mUptimeAtLastRescheduleMs = 0; + Slog.i(TAG, "No monitored packages, ending prune"); + mDurationAtLastPrune = 0; + mUptimeAtLastPruneMs = 0; return; } long uptimeMs = SystemClock.uptimeMillis(); - // O if mPackageCleanup not running - long elapsedDurationMs = mUptimeAtLastRescheduleMs == 0 - ? 0 : uptimeMs - mUptimeAtLastRescheduleMs; - // Less than O if mPackageCleanup unexpectedly didn't run yet even though - // and we are past the last duration scheduled to run - long remainingDurationMs = mDurationAtLastReschedule - elapsedDurationMs; - if (mUptimeAtLastRescheduleMs == 0 + // O if not running + long elapsedDurationMs = mUptimeAtLastPruneMs == 0 + ? 0 : uptimeMs - mUptimeAtLastPruneMs; + // Less than O if unexpectedly didn't run yet even though + // we are past the last duration scheduled to run + long remainingDurationMs = mDurationAtLastPrune - elapsedDurationMs; + if (mUptimeAtLastPruneMs == 0 || remainingDurationMs <= 0 || nextDurationToScheduleMs < remainingDurationMs) { // First schedule or an earlier reschedule pruneObservers(elapsedDurationMs); - mShortTaskHandler.removeCallbacks(mPackageCleanup); - mShortTaskHandler.postDelayed(mPackageCleanup, nextDurationToScheduleMs); - mDurationAtLastReschedule = nextDurationToScheduleMs; - mUptimeAtLastRescheduleMs = uptimeMs; + // We don't use Handler#hasCallbacks because we want to update the schedule delay + mShortTaskHandler.removeCallbacks(this::pruneAndSchedule); + mShortTaskHandler.postDelayed(this::pruneAndSchedule, nextDurationToScheduleMs); + mDurationAtLastPrune = nextDurationToScheduleMs; + mUptimeAtLastPruneMs = uptimeMs; } } } /** - * Returns the earliest time a package should expire. + * Returns the next time in millis to schedule a prune. + * * @returns Long#MAX_VALUE if there are no observed packages. */ - private long getEarliestPackageExpiryLocked() { + private long getNextPruneScheduleMillisLocked() { long shortestDurationMs = Long.MAX_VALUE; for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) { ArrayMap<String, MonitoredPackage> packages = mAllObservers.valueAt(oIndex).mPackages; for (int pIndex = 0; pIndex < packages.size(); pIndex++) { - long duration = packages.valueAt(pIndex).mDurationMs; + MonitoredPackage mp = packages.valueAt(pIndex); + long duration = Math.min(mp.mDurationMs, mp.mHealthCheckDurationMs); if (duration < shortestDurationMs) { shortestDurationMs = duration; } } } - Slog.v(TAG, "Earliest package time is " + shortestDurationMs); + Slog.i(TAG, "Next prune will be scheduled in " + shortestDurationMs + "ms"); return shortestDurationMs; } /** * Removes {@code elapsedMs} milliseconds from all durations on monitored packages. - * Discards expired packages and discards observers without any packages. + * + * <p> Prunes all observers with {@link ObserverInternal#prunePackages} and discards observers + * without any packages left. */ private void pruneObservers(long elapsedMs) { if (elapsedMs == 0) { @@ -559,8 +593,8 @@ public class PackageWatchdog { Iterator<ObserverInternal> it = mAllObservers.values().iterator(); while (it.hasNext()) { ObserverInternal observer = it.next(); - List<MonitoredPackage> failedPackages = - observer.updateMonitoringDurations(elapsedMs); + Set<MonitoredPackage> failedPackages = + observer.prunePackages(elapsedMs); if (!failedPackages.isEmpty()) { onHealthCheckFailed(observer, failedPackages); } @@ -570,32 +604,34 @@ public class PackageWatchdog { } } } - Slog.i(TAG, "Syncing health check requests pruned packages"); + Slog.i(TAG, "Syncing health check requests, pruned observers"); syncRequestsAsync(); saveToFileAsync(); } private void onHealthCheckFailed(ObserverInternal observer, - List<MonitoredPackage> failedPackages) { + Set<MonitoredPackage> failedPackages) { mLongTaskHandler.post(() -> { synchronized (mLock) { PackageHealthObserver registeredObserver = observer.mRegisteredObserver; if (registeredObserver != null) { PackageManager pm = mContext.getPackageManager(); - for (int i = 0; i < failedPackages.size(); i++) { - String packageName = failedPackages.get(i).mName; + Iterator<MonitoredPackage> it = failedPackages.iterator(); + while (it.hasNext()) { + String failedPackage = it.next().mName; long versionCode = 0; - Slog.i(TAG, "Explicit health check failed for package " + packageName); + Slog.i(TAG, "Explicit health check failed for package " + failedPackage); try { versionCode = pm.getPackageInfo( - packageName, 0 /* flags */).getLongVersionCode(); + failedPackage, 0 /* flags */).getLongVersionCode(); } catch (PackageManager.NameNotFoundException e) { Slog.w(TAG, "Explicit health check failed but could not find package " - + packageName); + + failedPackage); // TODO(b/120598832): Skip. We only continue to pass tests for now since // the tests don't install any packages } - registeredObserver.execute(new VersionedPackage(packageName, versionCode)); + registeredObserver.execute( + new VersionedPackage(failedPackage, versionCode)); } } } @@ -670,34 +706,38 @@ public class PackageWatchdog { } private void saveToFileAsync() { - // TODO(b/120598832): Use Handler#hasCallbacks instead of removing and posting - mLongTaskHandler.removeCallbacks(this::saveToFile); - mLongTaskHandler.post(this::saveToFile); + if (!mLongTaskHandler.hasCallbacks(this::saveToFile)) { + mLongTaskHandler.post(this::saveToFile); + } } /** * Represents an observer monitoring a set of packages along with the failure thresholds for * each package. + * + * <p> Note, the PackageWatchdog#mLock must always be held when reading or writing + * instances of this class. */ - static class ObserverInternal { + //TODO(b/120598832): Remove 'm' from non-private fields + private static class ObserverInternal { public final String mName; //TODO(b/120598832): Add getter for mPackages - public final ArrayMap<String, MonitoredPackage> mPackages; + @GuardedBy("mLock") + public final ArrayMap<String, MonitoredPackage> mPackages = new ArrayMap<>(); @Nullable + @GuardedBy("mLock") public PackageHealthObserver mRegisteredObserver; ObserverInternal(String name, List<MonitoredPackage> packages) { mName = name; - mPackages = new ArrayMap<>(); updatePackages(packages); } /** - * Writes important details to file. Doesn't persist any package failure thresholds. - * - * <p>Note that this method is <b>not</b> thread safe. It should only be called from - * #saveToFile which runs on a single threaded handler. + * Writes important {@link MonitoredPackage} details for this observer to file. + * Does not persist any package failure thresholds. */ + @GuardedBy("mLock") public boolean write(XmlSerializer out) { try { out.startTag(null, TAG_OBSERVER); @@ -707,6 +747,8 @@ public class PackageWatchdog { out.startTag(null, TAG_PACKAGE); out.attribute(null, ATTR_NAME, p.mName); out.attribute(null, ATTR_DURATION, String.valueOf(p.mDurationMs)); + out.attribute(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION, + String.valueOf(p.mHealthCheckDurationMs)); out.attribute(null, ATTR_PASSED_HEALTH_CHECK, String.valueOf(p.mHasPassedHealthCheck)); out.endTag(null, TAG_PACKAGE); @@ -719,56 +761,68 @@ public class PackageWatchdog { } } + @GuardedBy("mLock") public void updatePackages(List<MonitoredPackage> packages) { - synchronized (mName) { - for (int pIndex = 0; pIndex < packages.size(); pIndex++) { - MonitoredPackage p = packages.get(pIndex); - mPackages.put(p.mName, p); - } + for (int pIndex = 0; pIndex < packages.size(); pIndex++) { + MonitoredPackage p = packages.get(pIndex); + mPackages.put(p.mName, p); } } /** * Reduces the monitoring durations of all packages observed by this observer by - * {@code elapsedMs}. If any duration is less than 0, the package is removed from - * observation. + * {@code elapsedMs}. If any duration is less than 0, the package is removed from + * observation. If any health check duration is less than 0, the health check result + * is evaluated. * - * @returns a {@link List} of packages that were removed from the observer without explicit + * @returns a {@link Set} of packages that were removed from the observer without explicit * health check passing, or an empty list if no package expired for which an explicit health * check was still pending */ - public List<MonitoredPackage> updateMonitoringDurations(long elapsedMs) { - List<MonitoredPackage> removedPackages = new ArrayList<>(); - synchronized (mName) { - Iterator<MonitoredPackage> it = mPackages.values().iterator(); - while (it.hasNext()) { - MonitoredPackage p = it.next(); - long newDuration = p.mDurationMs - elapsedMs; - if (newDuration > 0) { - p.mDurationMs = newDuration; - } else { - if (!p.mHasPassedHealthCheck) { - removedPackages.add(p); - } - it.remove(); + @GuardedBy("mLock") + private Set<MonitoredPackage> prunePackages(long elapsedMs) { + Set<MonitoredPackage> failedPackages = new ArraySet<>(); + Iterator<MonitoredPackage> it = mPackages.values().iterator(); + while (it.hasNext()) { + MonitoredPackage p = it.next(); + int healthCheckState = p.getHealthCheckState(); + + // Handle health check timeouts + if (healthCheckState == MonitoredPackage.STATE_ACTIVE) { + // Only reduce duration if state is active + p.mHealthCheckDurationMs -= elapsedMs; + // Check duration after reducing duration + if (p.mHealthCheckDurationMs <= 0) { + failedPackages.add(p); } } - return removedPackages; + + // Handle package expiry + p.mDurationMs -= elapsedMs; + // Check duration after reducing duration + if (p.mDurationMs <= 0) { + if (healthCheckState == MonitoredPackage.STATE_INACTIVE) { + Slog.w(TAG, "Package " + p.mName + + " expiring without starting health check, failing"); + failedPackages.add(p); + } + it.remove(); + } } + return failedPackages; } /** * Increments failure counts of {@code packageName}. * @returns {@code true} if failure threshold is exceeded, {@code false} otherwise */ + @GuardedBy("mLock") public boolean onPackageFailure(String packageName) { - synchronized (mName) { - MonitoredPackage p = mPackages.get(packageName); - if (p != null) { - return p.onFailure(); - } - return false; + MonitoredPackage p = mPackages.get(packageName); + if (p != null) { + return p.onFailure(); } + return false; } /** @@ -796,11 +850,14 @@ public class PackageWatchdog { String packageName = parser.getAttributeValue(null, ATTR_NAME); long duration = Long.parseLong( parser.getAttributeValue(null, ATTR_DURATION)); + long healthCheckDuration = Long.parseLong( + parser.getAttributeValue(null, + ATTR_EXPLICIT_HEALTH_CHECK_DURATION)); boolean hasPassedHealthCheck = Boolean.parseBoolean( parser.getAttributeValue(null, ATTR_PASSED_HEALTH_CHECK)); if (!TextUtils.isEmpty(packageName)) { packages.add(new MonitoredPackage(packageName, duration, - hasPassedHealthCheck)); + healthCheckDuration, hasPassedHealthCheck)); } } catch (NumberFormatException e) { Slog.wtf(TAG, "Skipping package for observer " + observerName, e); @@ -819,21 +876,50 @@ public class PackageWatchdog { } } - /** Represents a package along with the time it should be monitored for. */ - static class MonitoredPackage { + /** + * Represents a package along with the time it should be monitored for. + * + * <p> Note, the PackageWatchdog#mLock must always be held when reading or writing + * instances of this class. + */ + //TODO(b/120598832): Remove 'm' from non-private fields + private static class MonitoredPackage { + // Health check states + // mName has not passed health check but has requested a health check + public static int STATE_ACTIVE = 0; + // mName has not passed health check and has not requested a health check + public static int STATE_INACTIVE = 1; + // mName has passed health check + public static int STATE_PASSED = 2; + public final String mName; // Whether an explicit health check has passed + @GuardedBy("mLock") public boolean mHasPassedHealthCheck; // System uptime duration to monitor package + @GuardedBy("mLock") public long mDurationMs; + // System uptime duration to check the result of an explicit health check + // Initially, MAX_VALUE until we get a value from the health check service + // and request health checks. + @GuardedBy("mLock") + public long mHealthCheckDurationMs = Long.MAX_VALUE; // System uptime of first package failure + @GuardedBy("mLock") private long mUptimeStartMs; // Number of failures since mUptimeStartMs + @GuardedBy("mLock") private int mFailures; MonitoredPackage(String name, long durationMs, boolean hasPassedHealthCheck) { + this(name, durationMs, Long.MAX_VALUE, hasPassedHealthCheck); + } + + MonitoredPackage(String name, long durationMs, long healthCheckDurationMs, + boolean hasPassedHealthCheck) { mName = name; mDurationMs = durationMs; + mHealthCheckDurationMs = healthCheckDurationMs; mHasPassedHealthCheck = hasPassedHealthCheck; } @@ -842,7 +928,8 @@ public class PackageWatchdog { * * @return {@code true} if failure count exceeds a threshold, {@code false} otherwise */ - public synchronized boolean onFailure() { + @GuardedBy("mLock") + public boolean onFailure() { final long now = SystemClock.uptimeMillis(); final long duration = now - mUptimeStartMs; if (duration > TRIGGER_DURATION_MS) { @@ -860,5 +947,20 @@ public class PackageWatchdog { } return failed; } + + /** + * Returns any of the health check states of {@link #STATE_ACTIVE}, + * {@link #STATE_INACTIVE} or {@link #STATE_PASSED} + */ + @GuardedBy("mLock") + public int getHealthCheckState() { + if (mHasPassedHealthCheck) { + return STATE_PASSED; + } else if (mHealthCheckDurationMs == Long.MAX_VALUE) { + return STATE_INACTIVE; + } else { + return STATE_ACTIVE; + } + } } } diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index cb587de01f14..b053d84de7d6 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -74,7 +74,6 @@ final class CoreSettingsObserver extends ContentObserver { sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS_GLES, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYER_APP, String.class); - sGlobalSettingToTypeMap.put(Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, int.class); sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_ALL_APPS, int.class); sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_OPT_IN_APPS, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_OPT_OUT_APPS, String.class); diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 2bd91985eafb..8847e32d0fb5 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -2269,7 +2269,7 @@ class UserController implements Handler.Callback { boolean isFirstBootOrUpgrade() { IPackageManager pm = AppGlobals.getPackageManager(); try { - return pm.isFirstBoot() || pm.isUpgrade(); + return pm.isFirstBoot() || pm.isDeviceUpgrading(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java index 64c23affcf8c..4ef37a2e484d 100644 --- a/services/core/java/com/android/server/job/JobStore.java +++ b/services/core/java/com/android/server/job/JobStore.java @@ -543,6 +543,9 @@ public final class JobStore { if (jobStatus.hasBatteryNotLowConstraint()) { out.attribute(null, "battery-not-low", Boolean.toString(true)); } + if (jobStatus.hasStorageNotLowConstraint()) { + out.attribute(null, "storage-not-low", Boolean.toString(true)); + } out.endTag(null, XML_TAG_PARAMS_CONSTRAINTS); } @@ -903,6 +906,15 @@ public final class JobStore { jobBuilder.setExtras(extras); parser.nextTag(); // Consume </extras> + final JobInfo builtJob; + try { + builtJob = jobBuilder.build(); + } catch (Exception e) { + Slog.w(TAG, "Unable to build job from XML, ignoring: " + + jobBuilder.summarize()); + return null; + } + // Migrate sync jobs forward from earlier, incomplete representation if ("android".equals(sourcePackageName) && extras != null @@ -986,6 +998,14 @@ public final class JobStore { if (val != null) { jobBuilder.setRequiresCharging(true); } + val = parser.getAttributeValue(null, "battery-not-low"); + if (val != null) { + jobBuilder.setRequiresBatteryNotLow(true); + } + val = parser.getAttributeValue(null, "storage-not-low"); + if (val != null) { + jobBuilder.setRequiresStorageNotLow(true); + } } /** diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index 1c9028d10ca2..9094e1bf4c5a 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -353,7 +353,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub { throw new IllegalStateException("Should not be ota-dexopting when trying to move."); } - if (!mPackageManagerService.isUpgrade()) { + if (!mPackageManagerService.isDeviceUpgrading()) { Slog.d(TAG, "No upgrade, skipping A/B artifacts check."); return; } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index e4cb283fe864..7f48970b445a 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -531,6 +531,16 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag"); } + // Only system components can circumvent restricted whitelisting when installing. + if ((params.installFlags + & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0 + && mContext.checkCallingOrSelfPermission(Manifest.permission + .WHITELIST_RESTRICTED_PERMISSIONS) == PackageManager.PERMISSION_DENIED) { + throw new SecurityException("You need the " + + "android.permission.WHITELIST_RESTRICTED_PERMISSIONS permission to" + + " use the PackageManager.INSTALL_WHITELIST_RESTRICTED_PERMISSIONS flag"); + } + // Defensively resize giant app icons if (params.appIcon != null) { final ActivityManager am = (ActivityManager) mContext.getSystemService( diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 3306ccdf2a22..a935c652be53 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -139,6 +139,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { static final String TAG_SESSION = "session"; static final String TAG_CHILD_SESSION = "childSession"; private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission"; + private static final String TAG_WHITELISTED_RESTRICTED_PERMISSION = + "whitelisted-restricted-permission"; private static final String ATTR_SESSION_ID = "sessionId"; private static final String ATTR_USER_ID = "userId"; private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName"; @@ -486,6 +488,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { info.originatingUid = params.originatingUid; info.referrerUri = params.referrerUri; info.grantedRuntimePermissions = params.grantedRuntimePermissions; + info.whitelistedRestrictedPermissions = params.whitelistedRestrictedPermissions; info.installFlags = params.installFlags; info.isMultiPackage = params.isMultiPackage; info.isStaged = params.isStaged; @@ -2331,6 +2334,19 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + private static void writeWhitelistedRestrictedPermissionsLocked(@NonNull XmlSerializer out, + @Nullable List<String> whitelistedRestrictedPermissions) throws IOException { + if (whitelistedRestrictedPermissions != null) { + final int permissionCount = whitelistedRestrictedPermissions.size(); + for (int i = 0; i < permissionCount; i++) { + out.startTag(null, TAG_WHITELISTED_RESTRICTED_PERMISSION); + writeStringAttribute(out, ATTR_NAME, whitelistedRestrictedPermissions.get(i)); + out.endTag(null, TAG_WHITELISTED_RESTRICTED_PERMISSION); + } + } + } + + private static File buildAppIconFile(int sessionId, @NonNull File sessionsDir) { return new File(sessionsDir, "app_icon." + sessionId + ".png"); } @@ -2392,6 +2408,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason); writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions); + writeWhitelistedRestrictedPermissionsLocked(out, + params.whitelistedRestrictedPermissions); // Persist app icon if changed since last written File appIconFile = buildAppIconFile(sessionId, sessionsDir); @@ -2510,7 +2528,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Store the current depth. We should stop parsing when we reach an end tag at the same // depth. - List<String> permissions = new ArrayList<>(); + List<String> grantedRuntimePermissions = new ArrayList<>(); + List<String> whitelistedRestrictedPermissions = new ArrayList<>(); List<Integer> childSessionIds = new ArrayList<>(); int outerDepth = in.getDepth(); int type; @@ -2520,15 +2539,24 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { continue; } if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) { - permissions.add(readStringAttribute(in, ATTR_NAME)); + grantedRuntimePermissions.add(readStringAttribute(in, ATTR_NAME)); + } + if (TAG_WHITELISTED_RESTRICTED_PERMISSION.equals(in.getName())) { + whitelistedRestrictedPermissions.add(readStringAttribute(in, ATTR_NAME)); + } if (TAG_CHILD_SESSION.equals(in.getName())) { childSessionIds.add(readIntAttribute(in, ATTR_SESSION_ID, SessionInfo.INVALID_ID)); } } - if (permissions.size() > 0) { - params.grantedRuntimePermissions = permissions.stream().toArray(String[]::new); + if (grantedRuntimePermissions.size() > 0) { + params.grantedRuntimePermissions = grantedRuntimePermissions + .stream().toArray(String[]::new); + } + + if (whitelistedRestrictedPermissions.size() > 0) { + params.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions; } int[] childSessionIdsArray; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 444d2fa8d665..d5b1ca3e72a1 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -170,6 +170,7 @@ import android.content.pm.PackageList; import android.content.pm.PackageManager; import android.content.pm.PackageManager.LegacyPackageDeleteObserver; import android.content.pm.PackageManager.ModuleInfoFlags; +import android.content.pm.PackageManager.PermissionWhitelistFlags; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManagerInternal.CheckPermissionDelegate; import android.content.pm.PackageManagerInternal.PackageListObserver; @@ -745,6 +746,9 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mPackages") final private ArraySet<PackageListObserver> mPackageListObservers = new ArraySet<>(); + @GuardedBy("mPackages") + private final SparseIntArray mDefaultPermissionsGrantedUsers = new SparseIntArray(); + private final ModuleInfoProvider mModuleInfoProvider; private final ApexManager mApexManager; @@ -1487,10 +1491,15 @@ public class PackageManagerService extends IPackageManager.Stub final boolean virtualPreload = ((args.installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0); final String[] grantedPermissions = args.installGrantPermissions; + final List<String> whitelistedRestrictedPermissions = ((args.installFlags + & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0 + && parentRes.pkg != null) + ? parentRes.pkg.requestedPermissions : null; // Handle the parent package - handlePackagePostInstall(parentRes, grantPermissions, killApp, - virtualPreload, grantedPermissions, didRestore, + handlePackagePostInstall(parentRes, grantPermissions, + killApp, virtualPreload, grantedPermissions, + whitelistedRestrictedPermissions, didRestore, args.installerPackageName, args.observer); // Handle the child packages @@ -1498,8 +1507,9 @@ public class PackageManagerService extends IPackageManager.Stub ? parentRes.addedChildPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageInstalledInfo childRes = parentRes.addedChildPackages.valueAt(i); - handlePackagePostInstall(childRes, grantPermissions, killApp, - virtualPreload, grantedPermissions, false /*didRestore*/, + handlePackagePostInstall(childRes, grantPermissions, + killApp, virtualPreload, grantedPermissions, + whitelistedRestrictedPermissions, false /*didRestore*/, args.installerPackageName, args.observer); } @@ -1777,7 +1787,8 @@ public class PackageManagerService extends IPackageManager.Stub }; private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions, - boolean killApp, boolean virtualPreload, String[] grantedPermissions, + boolean killApp, boolean virtualPreload, + String[] grantedPermissions, List<String> whitelistedRestrictedPermissions, boolean launchedForRestore, String installerPackage, IPackageInstallObserver2 installObserver) { if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { @@ -1786,6 +1797,16 @@ public class PackageManagerService extends IPackageManager.Stub res.removedInfo.sendPackageRemovedBroadcasts(killApp); } + // Whitelist any restricted permissions first as some may be runtime + // that the installer requested to be granted at install time. + if (whitelistedRestrictedPermissions != null + && !whitelistedRestrictedPermissions.isEmpty()) { + mPermissionManager.setWhitelistedRestrictedPermissions( + res.pkg, res.newUsers, whitelistedRestrictedPermissions, + Process.myUid(), PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER, + mPermissionCallback); + } + // Now that we successfully installed the package, grant runtime // permissions if requested before broadcasting the install. Also // for legacy apps in permission review mode we clear the permission @@ -1999,6 +2020,8 @@ public class PackageManagerService extends IPackageManager.Stub if (allNewUsers && !update) { notifyPackageAdded(packageName, res.uid); + } else { + notifyPackageChanged(packageName, res.uid); } // Log current value of "unknown sources" setting @@ -2308,6 +2331,7 @@ public class PackageManagerService extends IPackageManager.Stub public void onDefaultRuntimePermissionsGranted(int userId) { synchronized(mPackages) { mSettings.onDefaultRuntimePermissionsGrantedLPr(userId); + mDefaultPermissionsGrantedUsers.put(userId, userId); } } }, mPackages /*externalLock*/); @@ -3475,7 +3499,7 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public boolean isUpgrade() { + public boolean isDeviceUpgrading() { // allow instant applications // The system property allows testing ota flow when upgraded to the same image. return mIsUpgrade || SystemProperties.getBoolean( @@ -5527,7 +5551,7 @@ public class PackageManagerService extends IPackageManager.Stub == PackageManager.PERMISSION_GRANTED); mPermissionManager.revokeRuntimePermission(permName, packageName, overridePolicy, - getCallingUid(), userId, mPermissionCallback); + userId, mPermissionCallback); } @Override @@ -5619,6 +5643,193 @@ public class PackageManagerService extends IPackageManager.Stub } @Override + public @Nullable List<String> getWhitelistedRestrictedPermissions(@NonNull String packageName, + @PermissionWhitelistFlags int whitelistFlags, @UserIdInt int userId) { + Preconditions.checkNotNull(packageName); + Preconditions.checkFlagsArgument(whitelistFlags, + PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE + | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM + | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER); + Preconditions.checkArgumentNonNegative(userId, null); + + if (UserHandle.getCallingUserId() != userId) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS, + "getWhitelistedRestrictedPermissions for user " + userId); + } + + synchronized (mPackages) { + final PackageSetting packageSetting = mSettings.mPackages.get(packageName); + if (packageSetting == null) { + Slog.w(TAG, "Unknown package: " + packageName); + return null; + } + + final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission( + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) + == PackageManager.PERMISSION_GRANTED; + final PackageSetting installerPackageSetting = mSettings.mPackages.get( + packageSetting.installerPackageName); + final boolean isCallerInstallerOnRecord = installerPackageSetting != null + && UserHandle.isSameApp(installerPackageSetting.appId, Binder.getCallingUid()); + + if ((whitelistFlags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0 + && !isCallerPrivileged) { + throw new SecurityException("Querying system whitelist requires " + + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); + } + + if ((whitelistFlags & (PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE + | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER)) != 0) { + if (!isCallerPrivileged && !isCallerInstallerOnRecord) { + throw new SecurityException("Querying upgrade or installer whitelist" + + " requires being installer on record or " + + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); + } + } + + if (filterAppAccessLPr(packageSetting, Binder.getCallingUid(), + UserHandle.getCallingUserId())) { + return null; + } + + final long identity = Binder.clearCallingIdentity(); + try { + return mPermissionManager.getWhitelistedRestrictedPermissions( + packageSetting.pkg, whitelistFlags, userId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + } + + @Override + public boolean addWhitelistedRestrictedPermission(@NonNull String packageName, + @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags, + @UserIdInt int userId) { + // Other argument checks are done in get/setWhitelistedRestrictedPermissions + Preconditions.checkNotNull(permission); + + List<String> permissions = getWhitelistedRestrictedPermissions(packageName, + whitelistFlags, userId); + if (permissions == null) { + permissions = new ArrayList<>(1); + } + if (permissions.indexOf(permission) < 0) { + permissions.add(permission); + setWhitelistedRestrictedPermissions(packageName, permissions, + whitelistFlags, userId); + return true; + } + return false; + } + + @Override + public boolean removeWhitelistedRestrictedPermission(@NonNull String packageName, + @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags, + @UserIdInt int userId) { + // Other argument checks are done in get/setWhitelistedRestrictedPermissions + Preconditions.checkNotNull(permission); + + final List<String> permissions = getWhitelistedRestrictedPermissions(packageName, + whitelistFlags, userId); + if (permissions != null && permissions.remove(permission)) { + setWhitelistedRestrictedPermissions(packageName, permissions, + whitelistFlags, userId); + return true; + } + return false; + } + + private void setWhitelistedRestrictedPermissions(@NonNull String packageName, + @Nullable List<String> permissions, @PermissionWhitelistFlags int whitelistFlag, + @UserIdInt int userId) { + Preconditions.checkNotNull(packageName); + Preconditions.checkFlagsArgument(whitelistFlag, + PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE + | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM + | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER); + Preconditions.checkArgument(Integer.bitCount(whitelistFlag) == 1); + Preconditions.checkArgumentNonNegative(userId, null); + + if (UserHandle.getCallingUserId() != userId) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.INTERACT_ACROSS_USERS, + "setWhitelistedRestrictedPermissions for user " + userId); + } + + synchronized (mPackages) { + final PackageSetting packageSetting = mSettings.mPackages.get(packageName); + if (packageSetting == null) { + Slog.w(TAG, "Unknown package: " + packageName); + return; + } + + final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission( + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) + == PackageManager.PERMISSION_GRANTED; + final PackageSetting installerPackageSetting = mSettings.mPackages.get( + packageSetting.installerPackageName); + final boolean isCallerInstallerOnRecord = installerPackageSetting != null + && UserHandle.isSameApp(installerPackageSetting.appId, Binder.getCallingUid()); + + if ((whitelistFlag & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0 + && !isCallerPrivileged) { + throw new SecurityException("Modifying system whitelist requires " + + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); + } + + if ((whitelistFlag & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) { + if (!isCallerPrivileged && !isCallerInstallerOnRecord) { + throw new SecurityException("Modifying upgrade whitelist requires" + + " being installer on record or " + + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); + } + final List<String> whitelistedPermissions = getWhitelistedRestrictedPermissions( + packageName, whitelistFlag, userId); + if (permissions == null || permissions.isEmpty()) { + if (whitelistedPermissions == null || whitelistedPermissions.isEmpty()) { + return; + } + } else { + // Only the system can add and remove while the installer can only remove. + final int permissionCount = permissions.size(); + for (int i = 0; i < permissionCount; i++) { + if ((whitelistedPermissions == null + || !whitelistedPermissions.contains(permissions.get(i))) + && !isCallerPrivileged) { + throw new SecurityException("Adding to upgrade whitelist requires" + + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); + } + } + } + } + + if ((whitelistFlag & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) { + if (!isCallerPrivileged && !isCallerInstallerOnRecord) { + throw new SecurityException("Modifying installer whitelist requires" + + " being installer on record or " + + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); + } + } + + if (filterAppAccessLPr(packageSetting, Binder.getCallingUid(), + UserHandle.getCallingUserId())) { + return; + } + + final long identity = Binder.clearCallingIdentity(); + try { + mPermissionManager.setWhitelistedRestrictedPermissions(packageSetting.pkg, + new int[]{userId}, permissions, Process.myUid(), whitelistFlag, + mPermissionCallback); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + } + + @Override public boolean shouldShowRequestPermissionRationale(String permissionName, String packageName, int userId) { if (UserHandle.getCallingUserId() != userId) { @@ -8676,7 +8887,7 @@ public class PackageManagerService extends IPackageManager.Stub private void maybeClearProfilesForUpgradesLI( @Nullable PackageSetting originalPkgSetting, @NonNull PackageParser.Package currentPkg) { - if (originalPkgSetting == null || !isUpgrade()) { + if (originalPkgSetting == null || !isDeviceUpgrading()) { return; } if (originalPkgSetting.versionCode == currentPkg.mVersionCode) { @@ -9163,7 +9374,7 @@ public class PackageManagerService extends IPackageManager.Stub enforceSystemOrRoot("Only the system can request package update"); // We need to re-extract after an OTA. - boolean causeUpgrade = isUpgrade(); + boolean causeUpgrade = isDeviceUpgrading(); // First boot or factory reset. // Note: we also handle devices that are upgrading to N right now as if it is their @@ -9224,7 +9435,7 @@ public class PackageManagerService extends IPackageManager.Stub boolean useProfileForDexopt = false; - if ((isFirstBoot() || isUpgrade()) && isSystemApp(pkg)) { + if ((isFirstBoot() || isDeviceUpgrading()) && isSystemApp(pkg)) { // Copy over initial preopt profiles since we won't get any JIT samples for methods // that are already compiled. File profileFile = new File(getPrebuildProfilePath(pkg)); @@ -12551,6 +12762,22 @@ public class PackageManagerService extends IPackageManager.Stub } } + @Override + public void notifyPackageChanged(String packageName, int uid) { + final PackageListObserver[] observers; + synchronized (mPackages) { + if (mPackageListObservers.size() == 0) { + return; + } + final PackageListObserver[] observerArray = + new PackageListObserver[mPackageListObservers.size()]; + observers = mPackageListObservers.toArray(observerArray); + } + for (int i = observers.length - 1; i >= 0; --i) { + observers[i].onPackageChanged(packageName, uid); + } + } + private static final Comparator<ProviderInfo> sProviderInitOrderSorter = (p1, p2) -> { final int v1 = p1.initOrder; final int v2 = p2.initOrder; @@ -14510,6 +14737,7 @@ public class PackageManagerService extends IPackageManager.Stub int mRet; final String packageAbiOverride; final String[] grantedRuntimePermissions; + final List<String> whitelistedRestrictedPermissions; final VerificationInfo verificationInfo; final PackageParser.SigningDetails signingDetails; final int installReason; @@ -14519,8 +14747,8 @@ public class PackageManagerService extends IPackageManager.Stub InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer, int installFlags, String installerPackageName, String volumeUuid, VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride, - String[] grantedPermissions, PackageParser.SigningDetails signingDetails, - int installReason) { + String[] grantedPermissions, List<String> whitelistedRestrictedPermissions, + PackageParser.SigningDetails signingDetails, int installReason) { super(user); this.origin = origin; this.move = move; @@ -14531,6 +14759,7 @@ public class PackageManagerService extends IPackageManager.Stub this.verificationInfo = verificationInfo; this.packageAbiOverride = packageAbiOverride; this.grantedRuntimePermissions = grantedPermissions; + this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions; this.signingDetails = signingDetails; this.installReason = installReason; } @@ -14558,8 +14787,10 @@ public class PackageManagerService extends IPackageManager.Stub installerPackageName = activeInstallSession.getInstallerPackageName(); volumeUuid = activeInstallSession.getSessionParams().volumeUuid; packageAbiOverride = activeInstallSession.getSessionParams().abiOverride; - grantedRuntimePermissions = - activeInstallSession.getSessionParams().grantedRuntimePermissions; + grantedRuntimePermissions = activeInstallSession.getSessionParams() + .grantedRuntimePermissions; + whitelistedRestrictedPermissions = activeInstallSession.getSessionParams() + .whitelistedRestrictedPermissions; signingDetails = activeInstallSession.getSigningDetails(); } @@ -15016,6 +15247,7 @@ public class PackageManagerService extends IPackageManager.Stub final UserHandle user; final String abiOverride; final String[] installGrantPermissions; + final List<String> whitelistedRestrictedPermissions; /** If non-null, drop an async trace when the install completes */ final String traceMethod; final int traceCookie; @@ -15032,6 +15264,7 @@ public class PackageManagerService extends IPackageManager.Stub int installFlags, String installerPackageName, String volumeUuid, UserHandle user, String[] instructionSets, String abiOverride, String[] installGrantPermissions, + List<String> whitelistedRestrictedPermissions, String traceMethod, int traceCookie, SigningDetails signingDetails, int installReason, MultiPackageInstallParams multiPackageInstallParams) { @@ -15045,6 +15278,7 @@ public class PackageManagerService extends IPackageManager.Stub this.instructionSets = instructionSets; this.abiOverride = abiOverride; this.installGrantPermissions = installGrantPermissions; + this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions; this.traceMethod = traceMethod; this.traceCookie = traceCookie; this.signingDetails = signingDetails; @@ -15135,7 +15369,7 @@ public class PackageManagerService extends IPackageManager.Stub super(params.origin, params.move, params.observer, params.installFlags, params.installerPackageName, params.volumeUuid, params.getUser(), null /*instructionSets*/, params.packageAbiOverride, - params.grantedRuntimePermissions, + params.grantedRuntimePermissions, params.whitelistedRestrictedPermissions, params.traceMethod, params.traceCookie, params.signingDetails, params.installReason, params.mParentInstallParams); } @@ -15143,7 +15377,7 @@ public class PackageManagerService extends IPackageManager.Stub /** Existing install */ FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) { super(OriginInfo.fromNothing(), null, null, 0, null, null, null, instructionSets, - null, null, null, 0, PackageParser.SigningDetails.UNKNOWN, + null, null, null, null, 0, PackageParser.SigningDetails.UNKNOWN, PackageManager.INSTALL_REASON_UNKNOWN, null /* parent */); this.codeFile = (codePath != null) ? new File(codePath) : null; this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null; @@ -15334,7 +15568,7 @@ public class PackageManagerService extends IPackageManager.Stub super(params.origin, params.move, params.observer, params.installFlags, params.installerPackageName, params.volumeUuid, params.getUser(), null /* instruction sets */, params.packageAbiOverride, - params.grantedRuntimePermissions, + params.grantedRuntimePermissions, params.whitelistedRestrictedPermissions, params.traceMethod, params.traceCookie, params.signingDetails, params.installReason, params.mParentInstallParams); } @@ -20960,6 +21194,7 @@ public class PackageManagerService extends IPackageManager.Stub } } } + sUserManager.systemReady(); // If we upgraded grant all default permissions before kicking off. for (int userId : grantPermissionsUserIds) { @@ -22809,7 +23044,8 @@ public class PackageManagerService extends IPackageManager.Stub final InstallParams params = new InstallParams(origin, move, installObserver, installFlags, installerPackageName, volumeUuid, null /*verificationInfo*/, user, packageAbiOverride, null /*grantedPermissions*/, - PackageParser.SigningDetails.UNKNOWN, PackageManager.INSTALL_REASON_UNKNOWN); + null /*whitelistedRestrictedPermissions*/, PackageParser.SigningDetails.UNKNOWN, + PackageManager.INSTALL_REASON_UNKNOWN); params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params)); msg.obj = params; @@ -23904,7 +24140,7 @@ public class PackageManagerService extends IPackageManager.Stub public void revokeRuntimePermission(String packageName, String permName, int userId, boolean overridePolicy) { mPermissionManager.revokeRuntimePermission( - permName, packageName, overridePolicy, getCallingUid(), userId, + permName, packageName, overridePolicy, userId, mPermissionCallback); } @@ -24285,6 +24521,13 @@ public class PackageManagerService extends IPackageManager.Stub null); } } + + @Override + public boolean wereDefaultPermissionsGrantedSinceBoot(int userId) { + synchronized (mPackages) { + return mDefaultPermissionPolicy.wereDefaultPermissionsGrantedSinceBoot(userId); + } + } } @GuardedBy("mPackages") @@ -24331,6 +24574,29 @@ public class PackageManagerService extends IPackageManager.Stub } @Override + public int getRuntimePermissionsVersion(@UserIdInt int userId) { + Preconditions.checkArgumentNonnegative(userId); + mContext.enforceCallingOrSelfPermission( + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, + "setRuntimePermissionVersion"); + synchronized (mPackages) { + return mSettings.getDefaultRuntimePermissionsVersionLPr(userId); + } + } + + @Override + public void setRuntimePermissionsVersion(int version, @UserIdInt int userId) { + Preconditions.checkArgumentNonnegative(version); + Preconditions.checkArgumentNonnegative(userId); + mContext.enforceCallingOrSelfPermission( + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, + "setRuntimePermissionVersion"); + synchronized (mPackages) { + mSettings.setDefaultRuntimePermissionsVersionLPr(version, userId); + } + } + + @Override public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId) { enforceSystemOrPhoneCaller("grantPermissionsToEnabledCarrierApps"); synchronized (mPackages) { @@ -24795,5 +25061,6 @@ interface PackageSender { void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted, boolean includeStopped, int appId, int[] userIds, int[] instantUserIds); void notifyPackageAdded(String packageName, int uid); + void notifyPackageChanged(String packageName, int uid); void notifyPackageRemoved(String packageName, int uid); } diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 2d1afa7e87a7..4e4a0e420d86 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -2347,6 +2347,9 @@ class PackageManagerShellCommand extends ShellCommand { break; case "-g": sessionParams.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS; + case "-w": + sessionParams.installFlags |= + PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS; break; case "--dont-kill": sessionParams.installFlags |= PackageManager.INSTALL_DONT_KILL_APP; @@ -2950,7 +2953,7 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" [--user USER_ID] INTENT"); pw.println(" Prints all broadcast receivers that can handle the given INTENT."); pw.println(""); - pw.println(" install [-lrtsfdg] [-i PACKAGE] [--user USER_ID|all|current]"); + pw.println(" install [-lrtsfdgw] [-i PACKAGE] [--user USER_ID|all|current]"); pw.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]"); pw.println(" [--install-reason 0/1/2/3/4] [--originating-uri URI]"); pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]"); @@ -2969,6 +2972,7 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" -d: allow version code downgrade (debuggable packages only)"); pw.println(" -p: partial application install (new split on top of existing pkg)"); pw.println(" -g: grant all runtime permissions"); + pw.println(" -w: whitelist all restricted permissions"); pw.println(" -S: size in bytes of package, required for stdin"); pw.println(" --user: install under the given user."); pw.println(" --dont-kill: installing a new feature split, don't kill running app"); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 4f81fd9f7a9f..41a8a776e3f7 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -1313,7 +1313,7 @@ public final class Settings { boolean areDefaultRuntimePermissionsGrantedLPr(int userId) { return mRuntimePermissionsPersistence - .areDefaultRuntimPermissionsGrantedLPr(userId); + .areDefaultRuntimePermissionsGrantedLPr(userId); } void onDefaultRuntimePermissionsGrantedLPr(int userId) { @@ -1321,6 +1321,14 @@ public final class Settings { .onDefaultRuntimePermissionsGrantedLPr(userId); } + int getDefaultRuntimePermissionsVersionLPr(int userId) { + return mRuntimePermissionsPersistence.getVersionLPr(userId); + } + + void setDefaultRuntimePermissionsVersionLPr(int version, int userId) { + mRuntimePermissionsPersistence.setVersionLPr(version, userId); + } + public VersionInfo findOrCreateVersion(String volumeUuid) { VersionInfo ver = mVersion.get(volumeUuid); if (ver == null) { @@ -4727,7 +4735,13 @@ public final class Settings { && !permissionNames.contains(perm)) { continue; } - pw.print(prefix); pw.print(" "); pw.println(perm); + pw.print(prefix); pw.print(" "); pw.print(perm); + final BasePermission bp = mPermissions.getPermission(perm); + if (bp != null && bp.isRestricted()) { + pw.println(": restricted=true"); + } else { + pw.println(); + } } } @@ -5023,7 +5037,10 @@ public final class Settings { final int flag = 1 << Integer.numberOfTrailingZeros(flags); flags &= ~flag; flagsString.append(PackageManager.permissionFlagToString(flag)); - flagsString.append(' '); + if (flags != 0) { + flagsString.append('|'); + } + } if (flagsString != null) { flagsString.append(']'); @@ -5085,6 +5102,8 @@ public final class Settings { private static final long WRITE_PERMISSIONS_DELAY_MILLIS = 200; private static final long MAX_WRITE_PERMISSIONS_DELAY_MILLIS = 2000; + private static final int INITIAL_VERSION = 0; + private final Handler mHandler = new MyHandler(); private final Object mPersistenceLock; @@ -5098,6 +5117,10 @@ public final class Settings { @GuardedBy("mLock") // The mapping keys are user ids. + private final SparseIntArray mVersions = new SparseIntArray(); + + @GuardedBy("mLock") + // The mapping keys are user ids. private final SparseArray<String> mFingerprints = new SparseArray<>(); @GuardedBy("mLock") @@ -5109,7 +5132,18 @@ public final class Settings { } @GuardedBy("Settings.this.mLock") - public boolean areDefaultRuntimPermissionsGrantedLPr(int userId) { + int getVersionLPr(int userId) { + return mVersions.get(userId); + } + + @GuardedBy("Settings.this.mLock") + void setVersionLPr(int version, int userId) { + mVersions.put(userId, version); + writePermissionsForUserAsyncLPr(userId); + } + + @GuardedBy("Settings.this.mLock") + public boolean areDefaultRuntimePermissionsGrantedLPr(int userId) { return mDefaultPermissionsGranted.get(userId); } @@ -5206,6 +5240,9 @@ public final class Settings { serializer.startTag(null, TAG_RUNTIME_PERMISSIONS); + final int version = mVersions.get(userId, INITIAL_VERSION); + serializer.attribute(null, ATTR_VERSION, Integer.toString(version)); + String fingerprint = mFingerprints.get(userId); if (fingerprint != null) { serializer.attribute(null, ATTR_FINGERPRINT, fingerprint); @@ -5263,6 +5300,7 @@ public final class Settings { } mDefaultPermissionsGranted.delete(userId); + mVersions.delete(userId); mFingerprints.remove(userId); } @@ -5326,6 +5364,9 @@ public final class Settings { switch (parser.getName()) { case TAG_RUNTIME_PERMISSIONS: { + int version = XmlUtils.readIntAttribute(parser, ATTR_VERSION, + INITIAL_VERSION); + mVersions.put(userId, version); String fingerprint = parser.getAttributeValue(null, ATTR_FINGERPRINT); mFingerprints.put(userId, fingerprint); final boolean defaultsGranted = Build.FINGERPRINT.equals(fingerprint); diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java index 1957eb89fd0c..490c647c22b1 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/BasePermission.java @@ -193,6 +193,22 @@ public final class BasePermission { && (perm.info.flags & PermissionInfo.FLAG_REMOVED) != 0; } + public boolean isSoftRestricted() { + return perm != null && perm.info != null + && (perm.info.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0; + } + + public boolean isHardRestricted() { + return perm != null && perm.info != null + && (perm.info.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0; + } + + public boolean isRestricted() { + return perm != null && perm.info != null + && (perm.info.flags & (PermissionInfo.FLAG_HARD_RESTRICTED + | PermissionInfo.FLAG_SOFT_RESTRICTED)) != 0; + } + public boolean isSignature() { return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) == PermissionInfo.PROTECTION_SIGNATURE; diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 5c386b47b1b1..5df2f86d6ad7 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -62,8 +62,10 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Slog; +import android.util.SparseIntArray; import android.util.Xml; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.ArrayUtils; import com.android.internal.util.XmlUtils; import com.android.server.LocalServices; @@ -116,6 +118,7 @@ public final class DefaultPermissionGrantPolicy { private static final String ATTR_PACKAGE = "package"; private static final String ATTR_NAME = "name"; private static final String ATTR_FIXED = "fixed"; + private static final String ATTR_WHITELISTED = "whitelisted"; private static final Set<String> PHONE_PERMISSIONS = new ArraySet<>(); @@ -213,12 +216,16 @@ public final class DefaultPermissionGrantPolicy { private final PackageManagerInternal mServiceInternal; private final PermissionManagerService mPermissionManager; private final DefaultPermissionGrantedCallback mPermissionGrantedCallback; + + @GuardedBy("mLock") + private SparseIntArray mDefaultPermissionsGrantedUsers = new SparseIntArray(); + public interface DefaultPermissionGrantedCallback { /** Callback when permissions have been granted */ void onDefaultRuntimePermissionsGranted(int userId); } - public DefaultPermissionGrantPolicy(Context context, Looper looper, + DefaultPermissionGrantPolicy(Context context, Looper looper, @Nullable DefaultPermissionGrantedCallback callback, @NonNull PermissionManagerService permissionManager) { mContext = context; @@ -288,10 +295,19 @@ public final class DefaultPermissionGrantPolicy { } } + public boolean wereDefaultPermissionsGrantedSinceBoot(int userId) { + synchronized (mLock) { + return mDefaultPermissionsGrantedUsers.indexOfKey(userId) >= 0; + } + } + public void grantDefaultPermissions(int userId) { grantPermissionsToSysComponentsAndPrivApps(userId); grantDefaultSystemHandlerPermissions(userId); grantDefaultPermissionExceptions(userId); + synchronized (mLock) { + mDefaultPermissionsGrantedUsers.put(userId, userId); + } } private void grantRuntimePermissionsForSystemPackage(int userId, PackageInfo pkg) { @@ -306,7 +322,7 @@ public final class DefaultPermissionGrantPolicy { } } if (!permissions.isEmpty()) { - grantRuntimePermissions(pkg, permissions, true, userId); + grantRuntimePermissions(pkg, permissions, true /*systemFixed*/, userId); } } @@ -334,8 +350,8 @@ public final class DefaultPermissionGrantPolicy { @SafeVarargs private final void grantIgnoringSystemPackage(String packageName, int userId, Set<String>... permissionGroups) { - grantPermissionsToPackage( - packageName, userId, true /* ignoreSystemPackage */, permissionGroups); + grantPermissionsToPackage(packageName, userId, true /* ignoreSystemPackage */, + true /*whitelistRestrictedPermissions*/, permissionGroups); } @SafeVarargs @@ -359,24 +375,30 @@ public final class DefaultPermissionGrantPolicy { return; } grantPermissionsToPackage(getSystemPackageInfo(packageName), - userId, systemFixed, false /* ignoreSystemPackage */, permissionGroups); + userId, systemFixed, false /* ignoreSystemPackage */, + true /*whitelistRestrictedPermissions*/, permissionGroups); } @SafeVarargs private final void grantPermissionsToPackage(String packageName, int userId, - boolean ignoreSystemPackage, Set<String>... permissionGroups) { + boolean ignoreSystemPackage, boolean whitelistRestrictedPermissions, + Set<String>... permissionGroups) { grantPermissionsToPackage(getPackageInfo(packageName), - userId, false /* systemFixed */, ignoreSystemPackage, permissionGroups); + userId, false /* systemFixed */, ignoreSystemPackage, + whitelistRestrictedPermissions, permissionGroups); } @SafeVarargs - private final void grantPermissionsToPackage(PackageInfo packageName, int userId, - boolean systemFixed, boolean ignoreSystemPackage, Set<String>... permissionGroups) { - if (packageName == null) return; - if (doesPackageSupportRuntimePermissions(packageName)) { + private final void grantPermissionsToPackage(PackageInfo packageInfo, int userId, + boolean systemFixed, boolean ignoreSystemPackage, + boolean whitelistRestrictedPermissions, Set<String>... permissionGroups) { + if (packageInfo == null) { + return; + } + if (doesPackageSupportRuntimePermissions(packageInfo)) { for (Set<String> permissionGroup : permissionGroups) { - grantRuntimePermissions(packageName, permissionGroup, systemFixed, - ignoreSystemPackage, userId); + grantRuntimePermissions(packageInfo, permissionGroup, systemFixed, + ignoreSystemPackage, whitelistRestrictedPermissions, userId); } } } @@ -443,6 +465,12 @@ public final class DefaultPermissionGrantPolicy { getDefaultSystemHandlerActivityPackage(MediaStore.ACTION_IMAGE_CAPTURE, userId), userId, CAMERA_PERMISSIONS, MICROPHONE_PERMISSIONS, STORAGE_PERMISSIONS); + // Sound recorder + grantPermissionsToSystemPackage( + getDefaultSystemHandlerActivityPackage( + MediaStore.Audio.Media.RECORD_SOUND_ACTION, userId), + userId, MICROPHONE_PERMISSIONS); + // Media provider grantSystemFixedPermissionsToSystemPackage( getDefaultProviderAuthorityPackage(MediaStore.AUTHORITY, userId), userId, @@ -585,8 +613,8 @@ public final class DefaultPermissionGrantPolicy { browserPackage = null; } } - grantPermissionsToPackage(browserPackage, userId, - false /* ignoreSystemPackage */, LOCATION_PERMISSIONS); + grantPermissionsToPackage(browserPackage, userId, false /* ignoreSystemPackage */, + true /*whitelistRestrictedPermissions*/, LOCATION_PERMISSIONS); // Voice interaction if (voiceInteractPackageNames != null) { @@ -809,7 +837,7 @@ public final class DefaultPermissionGrantPolicy { } Log.i(TAG, "Granting permissions to sim call manager for user:" + userId); grantPermissionsToPackage(packageName, userId, false /* ignoreSystemPackage */, - PHONE_PERMISSIONS, MICROPHONE_PERMISSIONS); + true /*whitelistRestrictedPermissions*/, PHONE_PERMISSIONS, MICROPHONE_PERMISSIONS); } private void grantDefaultPermissionsToDefaultSystemSimCallManager( @@ -854,7 +882,6 @@ public final class DefaultPermissionGrantPolicy { grantSystemFixedPermissionsToSystemPackage(packageName, userId, PHONE_PERMISSIONS, LOCATION_PERMISSIONS); } - } public void revokeDefaultPermissionsFromDisabledTelephonyDataServices( @@ -980,7 +1007,8 @@ public final class DefaultPermissionGrantPolicy { private void grantRuntimePermissions(PackageInfo pkg, Set<String> permissions, boolean systemFixed, int userId) { - grantRuntimePermissions(pkg, permissions, systemFixed, false, userId); + grantRuntimePermissions(pkg, permissions, systemFixed, false, + true /*whitelistRestrictedPermissions*/, userId); } private void revokeRuntimePermissions(String packageName, Set<String> permissions, @@ -1065,11 +1093,10 @@ public final class DefaultPermissionGrantPolicy { } } - private void grantRuntimePermissions(PackageInfo pkg, - Set<String> permissionsWithoutSplits, boolean systemFixed, boolean ignoreSystemPackage, - int userId) { - UserHandle user = UserHandle.of(userId); - + private void grantRuntimePermissions(PackageInfo pkg, Set<String> permissionsWithoutSplits, + boolean systemFixed, boolean ignoreSystemPackage, + boolean whitelistRestrictedPermissions, int userId) { + UserHandle user = UserHandle.of(userId); if (pkg == null) { return; } @@ -1203,6 +1230,10 @@ public final class DefaultPermissionGrantPolicy { .grantRuntimePermission(pkg.packageName, permission, user); } + if (whitelistRestrictedPermissions && isPermissionRestricted(permission)) { + newFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; + } + mContext.getPackageManager().updatePermissionFlags(permission, pkg.packageName, newFlags, newFlags, user); @@ -1354,7 +1385,11 @@ public final class DefaultPermissionGrantPolicy { permissions.clear(); } permissions.add(permissionGrant.name); - grantRuntimePermissions(pkg, permissions, permissionGrant.fixed, userId); + + + grantRuntimePermissions(pkg, permissions, permissionGrant.fixed, + permissionGrant.whitelisted, true /*whitelistRestrictedPermissions*/, + userId); } } } @@ -1510,8 +1545,10 @@ public final class DefaultPermissionGrantPolicy { } final boolean fixed = XmlUtils.readBooleanAttribute(parser, ATTR_FIXED); + final boolean whitelisted = XmlUtils.readBooleanAttribute(parser, ATTR_WHITELISTED); - DefaultPermissionGrant exception = new DefaultPermissionGrant(name, fixed); + DefaultPermissionGrant exception = new DefaultPermissionGrant( + name, fixed, whitelisted); outPackageExceptions.add(exception); } else { Log.e(TAG, "Unknown tag " + parser.getName() + "under <exception>"); @@ -1524,6 +1561,14 @@ public final class DefaultPermissionGrantPolicy { && pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1; } + private boolean isPermissionRestricted(String name) { + try { + return mContext.getPackageManager().getPermissionInfo(name, 0).isRestricted(); + } catch (NameNotFoundException e) { + return false; + } + } + private boolean isPermissionDangerous(String name) { try { final PermissionInfo pi = mContext.getPackageManager().getPermissionInfo(name, 0); @@ -1537,10 +1582,13 @@ public final class DefaultPermissionGrantPolicy { private static final class DefaultPermissionGrant { final String name; final boolean fixed; + final boolean whitelisted; - public DefaultPermissionGrant(String name, boolean fixed) { + public DefaultPermissionGrant(String name, boolean fixed, + boolean whitelisted) { this.name = name; this.fixed = fixed; + this.whitelisted = whitelisted; } } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 86a399465ee0..9336c55d4a79 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -24,6 +24,8 @@ import static android.app.AppOpsManager.MODE_FOREGROUND; import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.permissionToOp; import static android.app.AppOpsManager.permissionToOpCode; +import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT; +import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION; import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; @@ -33,6 +35,10 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET; import static android.content.pm.PackageManager.MASK_PERMISSION_FLAGS_ALL; +import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER; +import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM; +import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE; +import static android.content.pm.PackageManager.RESTRICTED_PERMISSIONS_ENABLED; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.os.UserHandle.getAppId; import static android.os.UserHandle.getUid; @@ -54,6 +60,7 @@ import android.app.AppOpsManager; import android.app.AppOpsManagerInternal; import android.content.Context; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.PermissionWhitelistFlags; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageParser; import android.content.pm.PackageParser.Package; @@ -62,6 +69,7 @@ import android.content.pm.PermissionInfo; import android.metrics.LogMaker; import android.os.Binder; import android.os.Build; +import android.os.Debug; import android.os.Handler; import android.os.HandlerThread; import android.os.Process; @@ -75,6 +83,7 @@ import android.permission.PermissionControllerManager; import android.permission.PermissionManager; import android.permission.PermissionManagerInternal; import android.provider.Settings; +import android.permission.PermissionManagerInternal.OnRuntimePermissionStateChangedListener; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -89,6 +98,8 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.RoSystemProperties; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.function.pooled.PooledLambda; +import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemConfig; @@ -216,6 +227,11 @@ public class PermissionManagerService { @GuardedBy("mLock") private final SparseBooleanArray mHasNoDelayedPermBackup = new SparseBooleanArray(); + /** Listeners for permission state (granting and flags) changes */ + @GuardedBy("mLock") + final private ArrayList<OnRuntimePermissionStateChangedListener> + mRuntimePermissionStateChangedListeners = new ArrayList<>(); + PermissionManagerService(Context context, @Nullable DefaultPermissionGrantedCallback defaultGrantCallback, @NonNull Object externalLock) { @@ -437,6 +453,42 @@ public class PermissionManagerService { } } + private void addOnRuntimePermissionStateChangedListener(@NonNull + OnRuntimePermissionStateChangedListener listener) { + synchronized (mLock) { + mRuntimePermissionStateChangedListeners.add(listener); + } + } + + private void removeOnRuntimePermissionStateChangedListener(@NonNull + OnRuntimePermissionStateChangedListener listener) { + synchronized (mLock) { + mRuntimePermissionStateChangedListeners.remove(listener); + } + } + + private void notifyRuntimePermissionStateChanged(@NonNull String packageName, + @UserIdInt int userId) { + FgThread.getHandler().sendMessage(PooledLambda.obtainMessage + (PermissionManagerService::doNotifyRuntimePermissionStateChanged, + PermissionManagerService.this, packageName, userId)); + } + + private void doNotifyRuntimePermissionStateChanged(@NonNull String packageName, + @UserIdInt int userId) { + final ArrayList<OnRuntimePermissionStateChangedListener> listeners; + synchronized (mLock) { + if (mRuntimePermissionStateChangedListeners.isEmpty()) { + return; + } + listeners = new ArrayList<>(mRuntimePermissionStateChangedListeners); + } + final int listenerCount = listeners.size(); + for (int i = 0; i < listenerCount; i++) { + listeners.get(i).onRuntimePermissionStateChanged(packageName, userId); + } + } + /** * Returns {@code true} if the permission can be implied from another granted permission. * <p>Some permissions, such as ACCESS_FINE_LOCATION, imply other permissions, @@ -611,7 +663,7 @@ public class PermissionManagerService { try { revokeRuntimePermission(permissionName, packageName, false, - Process.SYSTEM_UID, userId, permissionCallback); + userId, permissionCallback); } catch (IllegalArgumentException e) { Slog.e(TAG, "Could not revoke " + permissionName + " from " + packageName, e); @@ -955,9 +1007,10 @@ public class PermissionManagerService { } if (grant != GRANT_DENIED) { - if (!ps.isSystem() && ps.areInstallPermissionsFixed()) { + if (!ps.isSystem() && ps.areInstallPermissionsFixed() && !bp.isRuntime()) { // If this is an existing, non-system package, then - // we can't add any new permissions to it. + // we can't add any new permissions to it. Runtime + // permissions can be added any time - they ad dynamic. if (!allowedSig && !origPermissions.hasInstallPermission(perm)) { // Except... if this is a permission that was added // to the platform (note: need to only do this when @@ -994,6 +1047,9 @@ public class PermissionManagerService { } break; case GRANT_RUNTIME: { + boolean hardRestricted = bp.isHardRestricted(); + boolean softRestricted = bp.isSoftRestricted(); + for (int userId : currentUserIds) { PermissionState permState = origPermissions .getRuntimePermissionState(perm, userId); @@ -1001,7 +1057,36 @@ public class PermissionManagerService { boolean wasChanged = false; + boolean restrictionExempt = !RESTRICTED_PERMISSIONS_ENABLED + || (origPermissions.getPermissionFlags(bp.name, userId) + & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0; + boolean restrictionApplied = (origPermissions.getPermissionFlags( + bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0; + if (appSupportsRuntimePermissions) { + // If hard restricted we don't allow holding it + if (hardRestricted) { + if (!restrictionExempt) { + if (permState != null && permState.isGranted() + && permissionsState.revokeRuntimePermission( + bp, userId) != PERMISSION_OPERATION_FAILURE) { + wasChanged = true; + } + if (!restrictionApplied) { + flags |= FLAG_PERMISSION_APPLY_RESTRICTION; + wasChanged = true; + } + } + // If soft restricted we allow holding in a restricted form + } else if (softRestricted) { + // Regardless if granted set the restriction flag as it + // may affect app treatment based on this permission. + if (!restrictionExempt && !restrictionApplied) { + flags |= FLAG_PERMISSION_APPLY_RESTRICTION; + wasChanged = true; + } + } + // Remove review flag as it is not necessary anymore if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED; @@ -1011,7 +1096,8 @@ public class PermissionManagerService { if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) { flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE; wasChanged = true; - } else { + // Hard restricted permissions cannot be held. + } else if (!hardRestricted || restrictionExempt) { if (permState != null && permState.isGranted()) { if (permissionsState.grantRuntimePermission(bp, userId) == PERMISSION_OPERATION_FAILURE) { @@ -1032,8 +1118,29 @@ public class PermissionManagerService { } } - if (permissionsState.grantRuntimePermission(bp, userId) - != PERMISSION_OPERATION_FAILURE) { + if (!permissionsState.hasRuntimePermission(bp.name, userId) + && permissionsState.grantRuntimePermission(bp, userId) + != PERMISSION_OPERATION_FAILURE) { + wasChanged = true; + } + + // If legacy app always grant the permission but if restricted + // and not exempt take a note a restriction should be applied. + if ((hardRestricted || softRestricted) + && !restrictionExempt && !restrictionApplied) { + flags |= FLAG_PERMISSION_APPLY_RESTRICTION; + wasChanged = true; + } + } + + // If unrestricted or restriction exempt, don't apply restriction. + if (!(hardRestricted || softRestricted) || restrictionExempt) { + if (restrictionApplied) { + flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION; + // Dropping restriction on a legacy app requires a review. + if (!appSupportsRuntimePermissions) { + flags |= FLAG_PERMISSION_REVIEW_REQUIRED; + } wasChanged = true; } } @@ -1058,14 +1165,47 @@ public class PermissionManagerService { if (origPermissions.revokeInstallPermission(bp) != PERMISSION_OPERATION_FAILURE) { origPermissions.updatePermissionFlags(bp, UserHandle.USER_ALL, - MASK_PERMISSION_FLAGS_ALL, 0); + (MASK_PERMISSION_FLAGS_ALL + & ~FLAG_PERMISSION_APPLY_RESTRICTION), 0); changedInstallPermission = true; } + boolean hardRestricted = bp.isHardRestricted(); + boolean softRestricted = bp.isSoftRestricted(); + for (int userId : currentUserIds) { boolean wasChanged = false; + boolean restrictionExempt = !RESTRICTED_PERMISSIONS_ENABLED + || (origPermissions.getPermissionFlags(bp.name, userId) + & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0; + boolean restrictionApplied = (origPermissions.getPermissionFlags( + bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0; + if (appSupportsRuntimePermissions) { + // If hard restricted we don't allow holding it + if (hardRestricted) { + if (!restrictionExempt) { + if (permState != null && permState.isGranted() + && permissionsState.revokeRuntimePermission( + bp, userId) != PERMISSION_OPERATION_FAILURE) { + wasChanged = true; + } + if (!restrictionApplied) { + flags |= FLAG_PERMISSION_APPLY_RESTRICTION; + wasChanged = true; + } + } + // If soft restricted we allow holding in a restricted form + } else if (softRestricted) { + // Regardless if granted set the restriction flag as it + // may affect app treatment based on this permission. + if (!restrictionExempt && !restrictionApplied) { + flags |= FLAG_PERMISSION_APPLY_RESTRICTION; + wasChanged = true; + } + } + // Remove review flag as it is not necessary anymore if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED; @@ -1075,18 +1215,40 @@ public class PermissionManagerService { if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) { flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE; wasChanged = true; - } else { + // Hard restricted permissions cannot be held. + } else if (!hardRestricted || restrictionExempt) { if (permissionsState.grantRuntimePermission(bp, userId) != PERMISSION_OPERATION_FAILURE) { wasChanged = true; } } } else { - if (permissionsState.grantRuntimePermission(bp, userId) != - PERMISSION_OPERATION_FAILURE) { + if (!permissionsState.hasRuntimePermission(bp.name, userId) + && permissionsState.grantRuntimePermission(bp, + userId) != PERMISSION_OPERATION_FAILURE) { flags |= FLAG_PERMISSION_REVIEW_REQUIRED; wasChanged = true; } + + // If legacy app always grant the permission but if restricted + // and not exempt take a note a restriction should be applied. + if ((hardRestricted || softRestricted) + && !restrictionExempt && !restrictionApplied) { + flags |= FLAG_PERMISSION_APPLY_RESTRICTION; + wasChanged = true; + } + } + + // If unrestricted or restriction exempt, don't apply restriction. + if (!(hardRestricted || softRestricted) || restrictionExempt) { + if (restrictionApplied) { + flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION; + // Dropping restriction on a legacy app requires a review. + if (!appSupportsRuntimePermissions) { + flags |= FLAG_PERMISSION_REVIEW_REQUIRED; + } + wasChanged = true; + } } if (wasChanged) { @@ -1150,6 +1312,7 @@ public class PermissionManagerService { updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origPermissions, permissionsState, pkg, updatedUserIds); + // TODO: Move to PermissionPolicyService setAppOpsLocked(permissionsState, pkg); } @@ -1159,6 +1322,10 @@ public class PermissionManagerService { if (callback != null) { callback.onPermissionUpdated(updatedUserIds, runtimePermissionsRevoked); } + + for (int userId : updatedUserIds) { + notifyRuntimePermissionStateChanged(pkg.packageName, userId); + } } /** @@ -1882,6 +2049,54 @@ public class PermissionManagerService { } } + private @Nullable List<String> getWhitelistedRestrictedPermissions( + @NonNull PackageParser.Package pkg, @PermissionWhitelistFlags int whitelistFlags, + @UserIdInt int userId) { + final PackageSetting packageSetting = (PackageSetting) pkg.mExtras; + if (packageSetting == null) { + return null; + } + + final PermissionsState permissionsState = packageSetting.getPermissionsState(); + + int queryFlags = 0; + if ((whitelistFlags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0) { + queryFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; + } + if ((whitelistFlags & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) { + queryFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; + } + if ((whitelistFlags & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) { + queryFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; + } + + ArrayList<String> whitelistedPermissions = null; + + final int permissionCount = pkg.requestedPermissions.size(); + for (int i = 0; i < permissionCount; i++) { + final String permissionName = pkg.requestedPermissions.get(i); + final int currentFlags = permissionsState.getPermissionFlags(permissionName, userId); + if ((currentFlags & queryFlags) != 0) { + if (whitelistedPermissions == null) { + whitelistedPermissions = new ArrayList<>(); + } + whitelistedPermissions.add(permissionName); + } + } + + return whitelistedPermissions; + } + + private void setWhitelistedRestrictedPermissions(@NonNull PackageParser.Package pkg, + @NonNull int[] userIds, @Nullable List<String> permissions, int callingUid, + @PackageManager.PermissionWhitelistFlags int whitelistFlags, + @NonNull PermissionCallback callback) { + for (int userId : userIds) { + setWhitelistedRestrictedPermissionsForUser(pkg, userId, permissions, + callingUid, whitelistFlags, callback); + } + } + private void grantRequestedRuntimePermissionsForUser(PackageParser.Package pkg, int userId, String[] grantedPermissions, int callingUid, PermissionCallback callback) { PackageSetting ps = (PackageSetting) pkg.mExtras; @@ -1979,12 +2194,21 @@ public class PermissionManagerService { final int flags = permissionsState.getPermissionFlags(permName, userId); if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) { - throw new SecurityException("Cannot grant system fixed permission " + Log.e(TAG, "Cannot grant system fixed permission " + permName + " for package " + packageName); + return; } if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) { - throw new SecurityException("Cannot grant policy fixed permission " + Log.e(TAG, "Cannot grant policy fixed permission " + permName + " for package " + packageName); + return; + } + + if (RESTRICTED_PERMISSIONS_ENABLED && bp.isRestricted() + && (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) { + Log.e(TAG, "Cannot grant restricted non-exempt permission " + + permName + " for package " + packageName); + return; } if (bp.isDevelopment()) { @@ -2031,6 +2255,10 @@ public class PermissionManagerService { callback.onPermissionGranted(uid, userId); } + if (bp.isRuntime()) { + notifyRuntimePermissionStateChanged(packageName, userId); + } + // Only need to do this if user is initialized. Otherwise it's a new user // and there are no processes running as the user yet and there's no need // to make an expensive call to remount processes for the changed permissions. @@ -2051,7 +2279,7 @@ public class PermissionManagerService { } private void revokeRuntimePermission(String permName, String packageName, - boolean overridePolicy, int callingUid, int userId, PermissionCallback callback) { + boolean overridePolicy, int userId, PermissionCallback callback) { if (!mUserManagerInt.exists(userId)) { Log.e(TAG, "No such user:" + userId); return; @@ -2067,8 +2295,6 @@ public class PermissionManagerService { false, // requirePermissionWhenSameUser "revokeRuntimePermission"); - final int appId; - final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName); if (pkg == null || pkg.mExtras == null) { throw new IllegalArgumentException("Unknown package: " + packageName); @@ -2129,9 +2355,126 @@ public class PermissionManagerService { } if (callback != null) { - final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid); callback.onPermissionRevoked(pkg.applicationInfo.uid, userId); } + + if (bp.isRuntime()) { + notifyRuntimePermissionStateChanged(packageName, userId); + } + } + + private void setWhitelistedRestrictedPermissionsForUser(@NonNull PackageParser.Package pkg, + @UserIdInt int userId, @Nullable List<String> permissions, int callingUid, + @PermissionWhitelistFlags int whitelistFlags, PermissionCallback callback) { + final PackageSetting ps = (PackageSetting) pkg.mExtras; + if (ps == null) { + return; + } + + final PermissionsState permissionsState = ps.getPermissionsState(); + + ArraySet<String> oldGrantedRestrictedPermissions = null; + boolean updatePermissions = false; + + final int permissionCount = pkg.requestedPermissions.size(); + for (int i = 0; i < permissionCount; i++) { + final String permissionName = pkg.requestedPermissions.get(i); + + final BasePermission bp = mSettings.getPermissionLocked(permissionName); + if (bp == null) { + Slog.w(TAG, "Cannot whitelist unknown permission: " + permissionName); + continue; + } + + if (!bp.isRestricted()) { + continue; + } + + if (permissionsState.hasPermission(permissionName, userId)) { + if (oldGrantedRestrictedPermissions == null) { + oldGrantedRestrictedPermissions = new ArraySet<>(); + } + oldGrantedRestrictedPermissions.add(permissionName); + } + + final int oldFlags = permissionsState.getPermissionFlags(permissionName, userId); + + int newFlags = oldFlags; + int mask = 0; + int whitelistFlagsCopy = whitelistFlags; + while (whitelistFlagsCopy != 0) { + final int flag = 1 << Integer.numberOfTrailingZeros(whitelistFlagsCopy); + whitelistFlagsCopy &= ~flag; + switch (flag) { + case FLAG_PERMISSION_WHITELIST_SYSTEM: { + mask |= PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; + if (permissions != null && permissions.contains(permissionName)) { + newFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; + } else { + newFlags &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; + } + } break; + case FLAG_PERMISSION_WHITELIST_UPGRADE: { + mask |= PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; + if (permissions != null && permissions.contains(permissionName)) { + newFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; + } else { + newFlags &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; + } + } break; + case FLAG_PERMISSION_WHITELIST_INSTALLER: { + mask |= PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; + if (permissions != null && permissions.contains(permissionName)) { + newFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; + } else { + newFlags &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; + } + } break; + } + } + + if (oldFlags == newFlags) { + continue; + } + + updatePermissions = true; + + // If the permission is policy fixed as granted but it is no longer + // on any of the whitelists we need to clear the policy fixed flag + // as whitelisting trumps policy i.e. policy cannot grant a non + // grantable permission. + if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) { + final boolean isWhitelisted = (newFlags + & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0; + final boolean isGranted = permissionsState.hasPermission(permissionName, userId); + if (!isWhitelisted && isGranted) { + mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED; + newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED; + } + } + + updatePermissionFlags(permissionName, pkg.packageName, mask, newFlags, + callingUid, userId, false, null /*callback*/); + } + + if (updatePermissions) { + // Update app permissions to take into account the new whitelist state. + updatePermissions(pkg.packageName, pkg, getVolumeUuidForPackage(pkg), + 0 /*flags*/, null /*allPackages*/, callback); + + // If this resulted in losing a permission we need to kill the app. + if (oldGrantedRestrictedPermissions != null) { + final int oldGrantedCount = oldGrantedRestrictedPermissions.size(); + for (int i = 0; i < oldGrantedCount; i++) { + final String permission = oldGrantedRestrictedPermissions.valueAt(i); + // Sometimes we create a new permission state instance during update. + if (!ps.getPermissionsState().hasPermission(permission, userId)) { + callback.onPermissionRevoked(pkg.applicationInfo.uid, userId); + break; + } + } + } + } } @GuardedBy("mLock") @@ -2504,11 +2847,16 @@ public class PermissionManagerService { flagMask &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; flagValues &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; flagValues &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; + flagValues &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; + flagValues &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; + flagValues &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; + flagValues &= ~PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION; } final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName); if (pkg == null || pkg.mExtras == null) { - throw new IllegalArgumentException("Unknown package: " + packageName); + Log.e(TAG, "Unknown package: " + packageName); + return; } if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { throw new IllegalArgumentException("Unknown package: " + packageName); @@ -2528,6 +2876,9 @@ public class PermissionManagerService { permissionsState.getRuntimePermissionState(permName, userId) != null; final boolean permissionUpdated = permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues); + if (permissionUpdated && bp.isRuntime()) { + notifyRuntimePermissionStateChanged(packageName, userId); + } if (permissionUpdated && callback != null) { // Install and runtime permissions are stored in different places, // so figure out what permission changed and persist the change. @@ -2766,6 +3117,20 @@ public class PermissionManagerService { pkg, userIds, grantedPermissions, callingUid, callback); } @Override + public List<String> getWhitelistedRestrictedPermissions(PackageParser.Package pkg, + @PackageManager.PermissionWhitelistFlags int whitelistFlags, int userId) { + return PermissionManagerService.this.getWhitelistedRestrictedPermissions(pkg, + whitelistFlags, userId); + } + @Override + public void setWhitelistedRestrictedPermissions(@NonNull PackageParser.Package pkg, + @NonNull int[] userIds, @Nullable List<String> permissions, int callingUid, + @PackageManager.PermissionWhitelistFlags int whitelistFlags, + @NonNull PermissionCallback callback) { + PermissionManagerService.this.setWhitelistedRestrictedPermissions( + pkg, userIds, permissions, callingUid, whitelistFlags, callback); + } + @Override public void grantRuntimePermissionsGrantedToDisabledPackage(PackageParser.Package pkg, int callingUid, PermissionCallback callback) { PermissionManagerService.this.grantRuntimePermissionsGrantedToDisabledPackageLocked( @@ -2773,10 +3138,9 @@ public class PermissionManagerService { } @Override public void revokeRuntimePermission(String permName, String packageName, - boolean overridePolicy, int callingUid, int userId, - PermissionCallback callback) { + boolean overridePolicy, int userId, PermissionCallback callback) { PermissionManagerService.this.revokeRuntimePermission(permName, packageName, - overridePolicy, callingUid, userId, callback); + overridePolicy, userId, callback); } @Override public void updatePermissions(String packageName, Package pkg, boolean replaceGrant, @@ -2894,5 +3258,19 @@ public class PermissionManagerService { @NonNull UserHandle user) { PermissionManagerService.this.restoreDelayedRuntimePermissions(packageName, user); } + + @Override + public void addOnRuntimePermissionStateChangedListener( + OnRuntimePermissionStateChangedListener listener) { + PermissionManagerService.this.addOnRuntimePermissionStateChangedListener( + listener); + } + + @Override + public void removeOnRuntimePermissionStateChangedListener( + OnRuntimePermissionStateChangedListener listener) { + PermissionManagerService.this.removeOnRuntimePermissionStateChangedListener( + listener); + } } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 6c09fa0241db..34f922e11d08 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -18,6 +18,7 @@ package com.android.server.pm.permission; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.pm.PackageManager; import android.content.pm.PackageManager.PermissionInfoFlags; import android.content.pm.PackageParser; import android.content.pm.PermissionGroupInfo; @@ -76,8 +77,16 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager @NonNull PackageParser.Package pkg, @NonNull int[] userIds, @NonNull String[] grantedPermissions, int callingUid, @Nullable PermissionCallback callback); + public abstract @Nullable List<String> getWhitelistedRestrictedPermissions( + @NonNull PackageParser.Package pkg, + @PackageManager.PermissionWhitelistFlags int whitelistFlags, int userId); + public abstract void setWhitelistedRestrictedPermissions( + @NonNull PackageParser.Package pkg, @NonNull int[] userIds, + @NonNull List<String> permissions, int callingUid, + @PackageManager.PermissionWhitelistFlags int whitelistFlags, + @Nullable PermissionCallback callback); public abstract void revokeRuntimePermission(@NonNull String permName, - @NonNull String packageName, boolean overridePolicy, int callingUid, int userId, + @NonNull String packageName, boolean overridePolicy, int userId, @Nullable PermissionCallback callback); public abstract void updatePermissions(@Nullable String packageName, diff --git a/services/core/java/com/android/server/pm/permission/PermissionsState.java b/services/core/java/com/android/server/pm/permission/PermissionsState.java index c615ee502fa9..505a0e22eac4 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionsState.java +++ b/services/core/java/com/android/server/pm/permission/PermissionsState.java @@ -607,7 +607,7 @@ public final class PermissionsState { private int grantPermission(BasePermission permission, int userId) { if (hasPermission(permission.getName(), userId)) { - return PERMISSION_OPERATION_FAILURE; + return PERMISSION_OPERATION_SUCCESS; } final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId)); @@ -632,7 +632,7 @@ public final class PermissionsState { private int revokePermission(BasePermission permission, int userId) { final String permName = permission.getName(); if (!hasPermission(permName, userId)) { - return PERMISSION_OPERATION_FAILURE; + return PERMISSION_OPERATION_SUCCESS; } final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId)); diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java new file mode 100644 index 000000000000..3011808d281c --- /dev/null +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2019 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.server.policy; + +import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION; + +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; +import android.content.pm.PackageManagerInternal.PackageListObserver; +import android.content.pm.PackageParser; +import android.content.pm.PermissionInfo; +import android.os.UserHandle; +import android.os.UserManagerInternal; +import android.permission.PermissionControllerManager; +import android.permission.PermissionManagerInternal; +import android.util.Slog; +import android.util.SparseArray; +import android.util.SparseIntArray; + +import com.android.internal.util.function.QuadConsumer; +import com.android.internal.util.function.TriConsumer; +import com.android.server.FgThread; +import com.android.server.LocalServices; +import com.android.server.SystemService; + +import java.util.concurrent.CountDownLatch; + +/** + * This is a permission policy that governs over all permission mechanism + * such as permissions, app ops, etc. For example, the policy ensures that + * permission state and app ops is synchronized for cases where there is a + * dependency between permission state (permissions or permission flags) + * and app ops - and vise versa. + */ +public final class PermissionPolicyService extends SystemService { + + private static final String LOG_TAG = PermissionPolicyService.class.getSimpleName(); + + public PermissionPolicyService(@NonNull Context context) { + super(context); + } + + @Override + public void onStart() { + final PackageManagerInternal packageManagerInternal = LocalServices.getService( + PackageManagerInternal.class); + packageManagerInternal.getPackageList(new PackageListObserver() { + @Override + public void onPackageAdded(String packageName, int uid) { + synchronizePackagePermissionsAndAppOpsForUser(getContext(), packageName, + UserHandle.getUserId(uid)); + } + + @Override + public void onPackageChanged(String packageName, int uid) { + synchronizePackagePermissionsAndAppOpsForUser(getContext(), packageName, + UserHandle.getUserId(uid)); + } + + @Override + public void onPackageRemoved(String packageName, int uid) { + /* do nothing */ + } + }); + } + + @Override + public void onStartUser(@UserIdInt int userId) { + grantOrUpgradeDefaultRuntimePermissionsInNeeded(getContext(), userId); + synchronizePermissionsAndAppOpsForUser(getContext(), userId); + startWatchingRuntimePermissionChanges(getContext(), userId); + } + + private static void grantOrUpgradeDefaultRuntimePermissionsInNeeded(@NonNull Context context, + @UserIdInt int userId) { + final PackageManagerInternal packageManagerInternal = LocalServices.getService( + PackageManagerInternal.class); + if (packageManagerInternal.wereDefaultPermissionsGrantedSinceBoot(userId)) { + // Now call into the permission controller to apply policy around permissions + final CountDownLatch latch = new CountDownLatch(1); + + // We need to create a local manager that does not schedule work on the main + // there as we are on the main thread and want to block until the work is + // completed or we time out. + final PermissionControllerManager permissionControllerManager = + new PermissionControllerManager(context, FgThread.getHandler()); + permissionControllerManager.grantOrUpgradeDefaultRuntimePermissions( + FgThread.getExecutor(), + (Boolean success) -> { + if (!success) { + // We are in an undefined state now, let us crash and have + // rescue party suggest a wipe to recover to a good one. + final String message = "Error granting/upgrading runtime permissions"; + Slog.wtf(LOG_TAG, message); + throw new IllegalStateException(message); + } + latch.countDown(); + } + ); + try { + latch.await(); + } catch (InterruptedException e) { + /* ignore */ + } + } + } + + private static void onRestrictedPermissionEnabledChange(@NonNull Context context) { + final PermissionManagerInternal permissionManagerInternal = LocalServices + .getService(PermissionManagerInternal.class); + final UserManagerInternal userManagerInternal = LocalServices.getService( + UserManagerInternal.class); + for (int userId : userManagerInternal.getUserIds()) { + synchronizePermissionsAndAppOpsForUser(context, userId); + } + } + + private static void startWatchingRuntimePermissionChanges(@NonNull Context context, + int userId) { + final PermissionManagerInternal permissionManagerInternal = LocalServices.getService( + PermissionManagerInternal.class); + permissionManagerInternal.addOnRuntimePermissionStateChangedListener( + (packageName, changedUserId) -> { + if (userId == changedUserId) { + synchronizePackagePermissionsAndAppOpsForUser(context, packageName, userId); + } + }); + } + + private static void synchronizePackagePermissionsAndAppOpsForUser(@NonNull Context context, + @NonNull String packageName, @UserIdInt int userId) { + final PackageManagerInternal packageManagerInternal = LocalServices.getService( + PackageManagerInternal.class); + final PackageParser.Package pkg = packageManagerInternal + .getPackage(packageName); + if (pkg != null) { + PermissionToOpSynchronizer.syncPackage(context, pkg, userId); + } + } + + private static void synchronizePermissionsAndAppOpsForUser(@NonNull Context context, + @UserIdInt int userId) { + final PackageManagerInternal packageManagerInternal = LocalServices.getService( + PackageManagerInternal.class); + final PermissionToOpSynchronizer synchronizer = new PermissionToOpSynchronizer(context); + packageManagerInternal.forEachPackage((pkg) -> + synchronizer.addPackage(context, pkg, userId)); + synchronizer.syncPackages(); + } + + private static class PermissionToOpSynchronizer { + private final @NonNull Context mContext; + + private final @NonNull SparseArray<String> mPackageNames = new SparseArray<>(); + private final @NonNull SparseIntArray mAllowedUidOps = new SparseIntArray(); + private final @NonNull SparseIntArray mDefaultUidOps = new SparseIntArray(); + + PermissionToOpSynchronizer(@NonNull Context context) { + mContext = context; + } + + private void addPackage(@NonNull Context context, + @NonNull PackageParser.Package pkg, @UserIdInt int userId) { + addPackage(context, pkg, userId, this::addAllowedEntry, this::addIgnoredEntry); + } + + void syncPackages() { + final AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class); + final int allowedCount = mAllowedUidOps.size(); + for (int i = 0; i < allowedCount; i++) { + final int opCode = mAllowedUidOps.keyAt(i); + final int uid = mAllowedUidOps.valueAt(i); + final String packageName = mPackageNames.valueAt(i); + setUidModeAllowed(appOpsManager, opCode, uid, packageName); + } + final int defaultCount = mDefaultUidOps.size(); + for (int i = 0; i < defaultCount; i++) { + final int opCode = mDefaultUidOps.keyAt(i); + final int uid = mDefaultUidOps.valueAt(i); + setUidModeDefault(appOpsManager, opCode, uid); + } + } + + static void syncPackage(@NonNull Context context, @NonNull PackageParser.Package pkg, + @UserIdInt int userId) { + addPackage(context, pkg, userId, PermissionToOpSynchronizer::setUidModeAllowed, + PermissionToOpSynchronizer::setUidModeDefault); + } + + private static void addPackage(@NonNull Context context, + @NonNull PackageParser.Package pkg, @UserIdInt int userId, + @NonNull QuadConsumer<AppOpsManager, Integer, Integer, String> allowedConsumer, + @NonNull TriConsumer<AppOpsManager, Integer, Integer> defaultConsumer) { + final PackageManager packageManager = context.getPackageManager(); + final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); + + final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.applicationInfo.uid)); + final UserHandle userHandle = UserHandle.of(userId); + + final int permissionCount = pkg.requestedPermissions.size(); + for (int i = 0; i < permissionCount; i++) { + final String permission = pkg.requestedPermissions.get(i); + + final int opCode = AppOpsManager.permissionToOpCode(permission); + if (opCode == AppOpsManager.OP_NONE) { + continue; + } + + final PermissionInfo permissionInfo; + try { + permissionInfo = packageManager.getPermissionInfo(permission, 0); + } catch (PackageManager.NameNotFoundException e) { + continue; + } + + if (!permissionInfo.isRestricted()) { + continue; + } + + final boolean applyRestriction = PackageManager.RESTRICTED_PERMISSIONS_ENABLED + && (packageManager.getPermissionFlags(permission, pkg.packageName, + userHandle) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0; + + if (permissionInfo.isHardRestricted()) { + if (applyRestriction) { + defaultConsumer.accept(appOpsManager, opCode, uid); + } else { + allowedConsumer.accept(appOpsManager, opCode, uid, pkg.packageName); + } + } else if (permissionInfo.isSoftRestricted()) { + //TODO: Implement soft restrictions like storage here. + } + } + } + + @SuppressWarnings("unused") + private void addAllowedEntry(@NonNull AppOpsManager appOpsManager, int opCode, + int uid, @NonNull String packageName) { + mPackageNames.put(opCode, packageName); + mAllowedUidOps.put(opCode, uid); + } + + @SuppressWarnings("unused") + private void addIgnoredEntry(@NonNull AppOpsManager appOpsManager, + int opCode, int uid) { + mDefaultUidOps.put(opCode, uid); + } + + private static void setUidModeAllowed(@NonNull AppOpsManager appOpsManager, + int opCode, int uid, @NonNull String packageName) { + final int currentMode = appOpsManager.unsafeCheckOpRaw(AppOpsManager + .opToPublicName(opCode), uid, packageName); + if (currentMode == AppOpsManager.MODE_DEFAULT) { + appOpsManager.setUidMode(opCode, uid, AppOpsManager.MODE_ALLOWED); + } + } + + private static void setUidModeDefault(@NonNull AppOpsManager appOpsManager, + int opCode, int uid) { + appOpsManager.setUidMode(opCode, uid, AppOpsManager.MODE_DEFAULT); + } + } +} diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 0e0fc120dbfd..b3f1c553ba82 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -4830,7 +4830,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return true; } }; - if (mContext.getPackageManager().isUpgrade()) { + if (mContext.getPackageManager().isDeviceUpgrading()) { mBootMsgDialog.setTitle(R.string.android_upgrading_title); } else { mBootMsgDialog.setTitle(R.string.android_start_title); diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index 6d9e09761e52..325ba071b428 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -38,9 +38,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; import android.content.pm.Signature; -import android.database.ContentObserver; import android.database.CursorWindow; -import android.net.Uri; import android.os.AsyncTask; import android.os.Binder; import android.os.Bundle; @@ -52,7 +50,6 @@ import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.UserHandle; import android.os.UserManagerInternal; -import android.provider.Settings; import android.service.sms.FinancialSmsService; import android.telephony.IFinancialSmsCallback; import android.text.TextUtils; @@ -205,23 +202,6 @@ public class RoleManagerService extends SystemService implements RoleUserState.C () -> performInitialGrantsIfNecessaryAsync(userId)); } }, UserHandle.ALL, intentFilter, null, null); - - getContext().getContentResolver().registerContentObserver( - Settings.Global.getUriFor(Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED), false, - new ContentObserver(getContext().getMainThreadHandler()) { - @Override - public void onChange(boolean selfChange, Uri uri, int userId) { - boolean killSwitchEnabled = Settings.Global.getInt( - getContext().getContentResolver(), - Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, 0) == 1; - for (int user : mUserManagerInternal.getUserIds()) { - if (mUserManagerInternal.isUserRunning(user)) { - getOrCreateControllerService(user) - .onSmsKillSwitchToggled(killSwitchEnabled); - } - } - } - }, UserHandle.USER_ALL); } @Override diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index d014c0a34755..8f2a2d2dae67 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -47,7 +47,6 @@ import static android.app.admin.DevicePolicyManager.DELEGATION_INSTALL_EXISTING_ import static android.app.admin.DevicePolicyManager.DELEGATION_KEEP_UNINSTALLED_PACKAGES; import static android.app.admin.DevicePolicyManager.DELEGATION_NETWORK_LOGGING; import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_ACCESS; -import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_INSTALLATION; import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT; import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO; import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI; @@ -379,13 +378,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { DELEGATION_KEEP_UNINSTALLED_PACKAGES, DELEGATION_NETWORK_LOGGING, DELEGATION_CERT_SELECTION, - DELEGATION_PACKAGE_INSTALLATION }; // Subset of delegations that can only be delegated by Device Owner. private static final List<String> DEVICE_OWNER_DELEGATIONS = Arrays.asList(new String[] { DELEGATION_NETWORK_LOGGING, - DELEGATION_PACKAGE_INSTALLATION }); // Subset of delegations that only one single package within a given user can hold @@ -11271,10 +11268,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // device owner or a profile owner affiliated with the device owner return true; } - if (DevicePolicyManagerService.this.isCallerDelegate(callerPackage, callerUid, - DELEGATION_PACKAGE_INSTALLATION)) { - return true; - } return false; } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 106e64222c00..0cd730b68c72 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -32,6 +32,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.content.res.Configuration; import android.content.res.Resources.Theme; import android.database.sqlite.SQLiteCompatibilityWalFlags; @@ -124,6 +125,7 @@ import com.android.server.pm.OtaDexoptService; import com.android.server.pm.PackageManagerService; import com.android.server.pm.ShortcutService; import com.android.server.pm.UserManagerService; +import com.android.server.policy.PermissionPolicyService; import com.android.server.policy.PhoneWindowManager; import com.android.server.policy.role.LegacyRoleResolutionPolicy; import com.android.server.power.PowerManagerService; @@ -543,7 +545,7 @@ public final class SystemServer { } private boolean isFirstBootOrUpgrade() { - return mPackageManagerService.isFirstBoot() || mPackageManagerService.isUpgrade(); + return mPackageManagerService.isFirstBoot() || mPackageManagerService.isDeviceUpgrading(); } private void reportWtf(String msg, Throwable e) { @@ -1988,6 +1990,11 @@ public final class SystemServer { mSystemServiceManager.startBootPhase(SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY); traceEnd(); + // Permission policy service + traceBeginAndSlog("StartPermissionPolicyService"); + mSystemServiceManager.startService(PermissionPolicyService.class); + traceEnd(); + // These are needed to propagate to the runnable below. final NetworkManagementService networkManagementF = networkManagement; final NetworkStatsService networkStatsF = networkStats; diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java index 0792414fef95..97a6e6657b7a 100644 --- a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java +++ b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java @@ -225,6 +225,24 @@ public class IPackageManagerStub implements IPackageManager { } @Override + public List<String> getWhitelistedRestrictedPermissions(String packageName, int flags, + int userId) throws RemoteException { + return null; + } + + @Override + public boolean addWhitelistedRestrictedPermission(String packageName, String permission, + int whitelistFlags, int userId) throws RemoteException { + return false; + } + + @Override + public boolean removeWhitelistedRestrictedPermission(String packageName, String permission, + int whitelistFlags, int userId) throws RemoteException { + return false; + } + + @Override public boolean shouldShowRequestPermissionRationale(String permissionName, String packageName, int userId) throws RemoteException { return false; @@ -858,7 +876,7 @@ public class IPackageManagerStub implements IPackageManager { } @Override - public boolean isUpgrade() throws RemoteException { + public boolean isDeviceUpgrading() throws RemoteException { return false; } @@ -1183,6 +1201,16 @@ public class IPackageManagerStub implements IPackageManager { } @Override + public int getRuntimePermissionsVersion(int userId) throws RemoteException { + return 0; + } + + @Override + public void setRuntimePermissionsVersion(int version, int userId) throws RemoteException { + + } + + @Override public IBinder asBinder() { return null; } diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java index 717275232a81..5fb9c435a31c 100644 --- a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java +++ b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java @@ -951,6 +951,11 @@ public class PackageManagerStub extends PackageManager { } @Override + public boolean isDeviceUpgrading() { + return false; + } + + @Override public PackageInstaller getPackageInstaller() { return null; } diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java index 3a0f4c24ee29..604637a2a8bc 100644 --- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java @@ -379,6 +379,82 @@ public class JobStoreTest { .build()); } + @Test + public void testPersistedIdleConstraint() throws Exception { + JobInfo.Builder b = new Builder(8, mComponent) + .setRequiresDeviceIdle(true) + .setPersisted(true); + JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null); + + mTaskStoreUnderTest.add(taskStatus); + waitForPendingIo(); + + final JobSet jobStatusSet = new JobSet(); + mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true); + assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size()); + JobStatus loaded = jobStatusSet.getAllJobs().iterator().next(); + assertEquals("Idle constraint not persisted correctly.", + loaded.getJob().isRequireDeviceIdle(), + taskStatus.getJob().isRequireDeviceIdle()); + } + + @Test + public void testPersistedChargingConstraint() throws Exception { + JobInfo.Builder b = new Builder(8, mComponent) + .setRequiresCharging(true) + .setPersisted(true); + JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null); + + mTaskStoreUnderTest.add(taskStatus); + waitForPendingIo(); + + final JobSet jobStatusSet = new JobSet(); + mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true); + assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size()); + JobStatus loaded = jobStatusSet.getAllJobs().iterator().next(); + assertEquals("Charging constraint not persisted correctly.", + loaded.getJob().isRequireCharging(), + taskStatus.getJob().isRequireCharging()); + } + + @Test + public void testPersistedStorageNotLowConstraint() throws Exception { + JobInfo.Builder b = new Builder(8, mComponent) + .setRequiresStorageNotLow(true) + .setPersisted(true); + JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null); + + mTaskStoreUnderTest.add(taskStatus); + waitForPendingIo(); + + final JobSet jobStatusSet = new JobSet(); + mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true); + assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size()); + JobStatus loaded = jobStatusSet.getAllJobs().iterator().next(); + assertEquals("Storage-not-low constraint not persisted correctly.", + loaded.getJob().isRequireStorageNotLow(), + taskStatus.getJob().isRequireStorageNotLow()); + } + + @Test + public void testPersistedBatteryNotLowConstraint() throws Exception { + JobInfo.Builder b = new Builder(8, mComponent) + .setRequiresBatteryNotLow(true) + .setPersisted(true); + JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null); + + mTaskStoreUnderTest.add(taskStatus); + waitForPendingIo(); + + final JobSet jobStatusSet = new JobSet(); + mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true); + assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size()); + JobStatus loaded = jobStatusSet.getAllJobs().iterator().next(); + assertEquals("Battery-not-low constraint not persisted correctly.", + loaded.getJob().isRequireBatteryNotLow(), + taskStatus.getJob().isRequireBatteryNotLow()); + } + /** * Helper function to kick a {@link JobInfo} through a persistence cycle and * assert that it's unchanged. diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java index 4cfd098936fb..95ec3d9c0917 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java @@ -60,6 +60,11 @@ public class PackageManagerServiceTest { } @Override + public void notifyPackageChanged(String packageName, int uid) { + + } + + @Override public void notifyPackageRemoved(String packageName, int uid) { } } diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java index c9b038c7c7d6..80fb58d45078 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java @@ -54,6 +54,12 @@ public final class TelephonyPermissions { private static final Supplier<ITelephony> TELEPHONY_SUPPLIER = () -> ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE)); + /** + * Whether to disable the new device identifier access restrictions. + */ + private static final String PROPERTY_DEVICE_IDENTIFIER_ACCESS_RESTRICTIONS_DISABLED = + "device_identifier_access_restrictions_disabled"; + // Contains a mapping of packages that did not meet the new requirements to access device // identifiers and the methods they were attempting to invoke; used to prevent duplicate // reporting of packages / methods. @@ -446,8 +452,8 @@ public final class TelephonyPermissions { * Returns true if the new device identifier access restrictions are disabled. */ private static boolean isIdentifierCheckDisabled() { - return Boolean.parseBoolean(DeviceConfig.getProperty(DeviceConfig.Privacy.NAMESPACE, - DeviceConfig.Privacy.PROPERTY_DEVICE_IDENTIFIER_ACCESS_RESTRICTIONS_DISABLED)); + return DeviceConfig.getInt(DeviceConfig.NAMESPACE_PRIVACY, + PROPERTY_DEVICE_IDENTIFIER_ACCESS_RESTRICTIONS_DISABLED, 0) == 1; } /** diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java index 226c0b8c07d6..14b847fa9ff6 100644 --- a/test-mock/src/android/test/mock/MockPackageManager.java +++ b/test-mock/src/android/test/mock/MockPackageManager.java @@ -56,6 +56,7 @@ import android.os.UserHandle; import android.os.storage.VolumeInfo; import java.util.List; +import java.util.Set; /** * A mock {@link android.content.pm.PackageManager} class. All methods are non-functional and throw @@ -296,6 +297,27 @@ public class MockPackageManager extends PackageManager { /** @hide */ @Override + public @NonNull Set<String> getWhitelistedRestrictedPermissions( + @NonNull String packageName, @PermissionWhitelistFlags int whitelistFlags) { + throw new UnsupportedOperationException(); + } + + /** @hide */ + @Override + public boolean addWhitelistedRestrictedPermission(@NonNull String packageName, + @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags) { + throw new UnsupportedOperationException(); + } + + /** @hide */ + @Override + public boolean removeWhitelistedRestrictedPermission(@NonNull String packageName, + @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags) { + throw new UnsupportedOperationException(); + } + + /** @hide */ + @Override public boolean shouldShowRequestPermissionRationale(String permission) { throw new UnsupportedOperationException(); } @@ -1107,6 +1129,14 @@ public class MockPackageManager extends PackageManager { * @hide */ @Override + public boolean isDeviceUpgrading() { + throw new UnsupportedOperationException(); + } + + /** + * @hide + */ + @Override public void addCrossProfileIntentFilter(IntentFilter filter, int sourceUserId, int targetUserId, int flags) { throw new UnsupportedOperationException(); diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index 33bb4cceb3a9..b308982c2343 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -661,8 +661,10 @@ public class PackageWatchdogTest { if (mIsEnabled) { packages.retainAll(mSupportedPackages); mRequestedPackages.addAll(packages); + mSupportedConsumer.accept(mSupportedPackages); + } else { + mSupportedConsumer.accept(Collections.emptyList()); } - mSupportedConsumer.accept(mSupportedPackages); } public void setSupportedPackages(List<String> packages) { diff --git a/tests/utils/testutils/java/com/android/internal/util/test/BidirectionalAsyncChannelServer.java b/tests/utils/testutils/java/com/android/internal/util/test/BidirectionalAsyncChannelServer.java index 49c833228b6c..ecf271601b6b 100644 --- a/tests/utils/testutils/java/com/android/internal/util/test/BidirectionalAsyncChannelServer.java +++ b/tests/utils/testutils/java/com/android/internal/util/test/BidirectionalAsyncChannelServer.java @@ -27,6 +27,7 @@ import com.android.internal.util.AsyncChannel; import java.util.HashMap; import java.util.Map; +import java.util.Set; /** * Provides an interface for the server side implementation of a bidirectional channel as described @@ -83,4 +84,8 @@ public class BidirectionalAsyncChannelServer { return mMessenger; } + public Set<Messenger> getClientMessengers() { + return mClients.keySet(); + } + } diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index fa17db1e956c..b75a1acf87c9 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -36,6 +36,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyList; @@ -53,6 +54,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.net.DhcpInfo; import android.net.wifi.WifiManager.LocalOnlyHotspotCallback; import android.net.wifi.WifiManager.LocalOnlyHotspotObserver; import android.net.wifi.WifiManager.LocalOnlyHotspotReservation; @@ -62,6 +64,7 @@ import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback; import android.net.wifi.WifiManager.OnWifiUsabilityStatsListener; import android.net.wifi.WifiManager.SoftApCallback; import android.net.wifi.WifiManager.TrafficStateCallback; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -92,6 +95,7 @@ public class WifiManagerTest { private static final int ERROR_NOT_SET = -1; private static final int ERROR_TEST_REASON = 5; private static final int TEST_UID = 14553; + private static final int TEST_NETWORK_ID = 143; private static final String TEST_PACKAGE_NAME = "TestPackage"; private static final String TEST_COUNTRY_CODE = "US"; private static final String[] TEST_MAC_ADDRESSES = {"da:a1:19:0:0:0"}; @@ -118,6 +122,7 @@ public class WifiManagerTest { MockitoAnnotations.initMocks(this); mLooper = new TestLooper(); mHandler = spy(new Handler(mLooper.getLooper())); + mApplicationInfo.targetSdkVersion = Build.VERSION_CODES.Q; when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo); when(mContext.getOpPackageName()).thenReturn(TEST_PACKAGE_NAME); @@ -1438,4 +1443,200 @@ i * Verify that a call to cancel WPS immediately returns a failure. .thenReturn(new Long(~WifiManager.WIFI_FEATURE_DPP)); assertFalse(mWifiManager.isEasyConnectSupported()); } + + /** + * Test behavior of {@link WifiManager#addNetwork(WifiConfiguration)} + * @throws Exception + */ + @Test + public void testAddNetwork() throws Exception { + WifiConfiguration configuration = new WifiConfiguration(); + when(mWifiService.addOrUpdateNetwork(any(), anyString())) + .thenReturn(TEST_NETWORK_ID); + + assertEquals(mWifiManager.addNetwork(configuration), TEST_NETWORK_ID); + verify(mWifiService).addOrUpdateNetwork(configuration, mContext.getOpPackageName()); + + // send a null config + assertEquals(mWifiManager.addNetwork(null), -1); + } + + /** + * Test behavior of {@link WifiManager#addNetwork(WifiConfiguration)} + * @throws Exception + */ + @Test + public void testUpdateNetwork() throws Exception { + WifiConfiguration configuration = new WifiConfiguration(); + when(mWifiService.addOrUpdateNetwork(any(), anyString())) + .thenReturn(TEST_NETWORK_ID); + + configuration.networkId = TEST_NETWORK_ID; + assertEquals(mWifiManager.updateNetwork(configuration), TEST_NETWORK_ID); + verify(mWifiService).addOrUpdateNetwork(configuration, mContext.getOpPackageName()); + + // config with invalid network ID + configuration.networkId = -1; + assertEquals(mWifiManager.updateNetwork(configuration), -1); + + // send a null config + assertEquals(mWifiManager.updateNetwork(null), -1); + } + + /** + * Test behavior of {@link WifiManager#enableNetwork(int, boolean)} + * @throws Exception + */ + @Test + public void testEnableNetwork() throws Exception { + when(mWifiService.enableNetwork(anyInt(), anyBoolean(), anyString())) + .thenReturn(true); + assertTrue(mWifiManager.enableNetwork(TEST_NETWORK_ID, true)); + verify(mWifiService).enableNetwork(TEST_NETWORK_ID, true, mContext.getOpPackageName()); + } + + /** + * Test behavior of {@link WifiManager#disableNetwork(int)} + * @throws Exception + */ + @Test + public void testDisableNetwork() throws Exception { + when(mWifiService.disableNetwork(anyInt(), anyString())) + .thenReturn(true); + assertTrue(mWifiManager.disableNetwork(TEST_NETWORK_ID)); + verify(mWifiService).disableNetwork(TEST_NETWORK_ID, mContext.getOpPackageName()); + } + + /** + * Test behavior of {@link WifiManager#disconnect()} + * @throws Exception + */ + @Test + public void testDisconnect() throws Exception { + when(mWifiService.disconnect(anyString())).thenReturn(true); + assertTrue(mWifiManager.disconnect()); + verify(mWifiService).disconnect(mContext.getOpPackageName()); + } + + /** + * Test behavior of {@link WifiManager#reconnect()} + * @throws Exception + */ + @Test + public void testReconnect() throws Exception { + when(mWifiService.reconnect(anyString())).thenReturn(true); + assertTrue(mWifiManager.reconnect()); + verify(mWifiService).reconnect(mContext.getOpPackageName()); + } + + /** + * Test behavior of {@link WifiManager#reassociate()} + * @throws Exception + */ + @Test + public void testReassociate() throws Exception { + when(mWifiService.reassociate(anyString())).thenReturn(true); + assertTrue(mWifiManager.reassociate()); + verify(mWifiService).reassociate(mContext.getOpPackageName()); + } + + /** + * Test behavior of {@link WifiManager#getSupportedFeatures()} + * @throws Exception + */ + @Test + public void testGetSupportedFeatures() throws Exception { + long supportedFeatures = + WifiManager.WIFI_FEATURE_SCANNER + | WifiManager.WIFI_FEATURE_PASSPOINT + | WifiManager.WIFI_FEATURE_P2P; + when(mWifiService.getSupportedFeatures()) + .thenReturn(Long.valueOf(supportedFeatures)); + + assertTrue(mWifiManager.isWifiScannerSupported()); + assertTrue(mWifiManager.isPasspointSupported()); + assertTrue(mWifiManager.isP2pSupported()); + assertFalse(mWifiManager.isPortableHotspotSupported()); + assertFalse(mWifiManager.is5GHzBandSupported()); + assertFalse(mWifiManager.isDeviceToDeviceRttSupported()); + assertFalse(mWifiManager.isDeviceToApRttSupported()); + assertFalse(mWifiManager.isPreferredNetworkOffloadSupported()); + assertFalse(mWifiManager.isAdditionalStaSupported()); + assertFalse(mWifiManager.isTdlsSupported()); + assertFalse(mWifiManager.isOffChannelTdlsSupported()); + assertFalse(mWifiManager.isEnhancedPowerReportingSupported()); + } + + /** + * Test behavior of {@link WifiManager#getControllerActivityEnergyInfo()} + * @throws Exception + */ + @Test + public void testGetControllerActivityEnergyInfo() throws Exception { + WifiActivityEnergyInfo activityEnergyInfo = + new WifiActivityEnergyInfo(5, 3, 3, new long[]{5L, 5L, 5L}, 5, 5, 5, 5); + when(mWifiService.reportActivityInfo()).thenReturn(activityEnergyInfo); + + assertEquals(activityEnergyInfo, mWifiManager.getControllerActivityEnergyInfo()); + } + + /** + * Test behavior of {@link WifiManager#getConnectionInfo()} + * @throws Exception + */ + @Test + public void testGetConnectionInfo() throws Exception { + WifiInfo wifiInfo = new WifiInfo(); + when(mWifiService.getConnectionInfo(anyString())).thenReturn(wifiInfo); + + assertEquals(wifiInfo, mWifiManager.getConnectionInfo()); + } + + /** + * Test behavior of {@link WifiManager#isDualModeSupported()} ()} + * @throws Exception + */ + @Test + public void testIsDualModeSupported() throws Exception { + when(mWifiService.needs5GHzToAnyApBandConversion()).thenReturn(true); + assertTrue(mWifiManager.isDualModeSupported()); + verify(mWifiService).needs5GHzToAnyApBandConversion(); + } + + /** + * Test behavior of {@link WifiManager#isDualBandSupported()} + * @throws Exception + */ + @Test + public void testIsDualBandSupported() throws Exception { + when(mWifiService.isDualBandSupported()).thenReturn(true); + assertTrue(mWifiManager.isDualBandSupported()); + verify(mWifiService).isDualBandSupported(); + } + + /** + * Test behavior of {@link WifiManager#getDhcpInfo()} + * @throws Exception + */ + @Test + public void testGetDhcpInfo() throws Exception { + DhcpInfo dhcpInfo = new DhcpInfo(); + + when(mWifiService.getDhcpInfo()).thenReturn(dhcpInfo); + assertEquals(dhcpInfo, mWifiManager.getDhcpInfo()); + verify(mWifiService).getDhcpInfo(); + } + + /** + * Test behavior of {@link WifiManager#setWifiEnabled(boolean)} + * @throws Exception + */ + @Test + public void testSetWifiEnabled() throws Exception { + when(mWifiService.setWifiEnabled(anyString(), anyBoolean())).thenReturn(true); + assertTrue(mWifiManager.setWifiEnabled(true)); + verify(mWifiService).setWifiEnabled(mContext.getOpPackageName(), true); + assertTrue(mWifiManager.setWifiEnabled(false)); + verify(mWifiService).setWifiEnabled(mContext.getOpPackageName(), false); + } } diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java index 76bfff065f4d..dd05b47fbd4f 100644 --- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java @@ -18,10 +18,16 @@ package android.net.wifi; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.validateMockitoUsage; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; @@ -29,7 +35,10 @@ import android.net.wifi.WifiScanner.PnoSettings; import android.net.wifi.WifiScanner.PnoSettings.PnoNetwork; import android.net.wifi.WifiScanner.ScanData; import android.net.wifi.WifiScanner.ScanSettings; +import android.os.Bundle; import android.os.Handler; +import android.os.Message; +import android.os.Messenger; import android.os.Parcel; import android.os.test.TestLooper; @@ -40,6 +49,7 @@ import com.android.internal.util.test.BidirectionalAsyncChannelServer; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -71,6 +81,7 @@ public class WifiScannerTest { private WifiScanner mWifiScanner; private TestLooper mLooper; private Handler mHandler; + private BidirectionalAsyncChannelServer mBidirectionalAsyncChannelServer; /** * Setup before tests. @@ -79,10 +90,10 @@ public class WifiScannerTest { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mLooper = new TestLooper(); - mHandler = mock(Handler.class); - BidirectionalAsyncChannelServer server = new BidirectionalAsyncChannelServer( + mHandler = spy(new Handler(mLooper.getLooper())); + mBidirectionalAsyncChannelServer = new BidirectionalAsyncChannelServer( mContext, mLooper.getLooper(), mHandler); - when(mService.getMessenger()).thenReturn(server.getMessenger()); + when(mService.getMessenger()).thenReturn(mBidirectionalAsyncChannelServer.getMessenger()); mWifiScanner = new WifiScanner(mContext, mService, mLooper.getLooper()); mLooper.dispatchAll(); } @@ -230,4 +241,208 @@ public class WifiScannerTest { parcel.setDataPosition(0); // Rewind data position back to the beginning for read. return ScanData.CREATOR.createFromParcel(parcel); } + + + /** + * Test behavior of {@link WifiScanner#startScan(ScanSettings, WifiScanner.ScanListener)} + * @throws Exception + */ + @Test + public void testStartScan() throws Exception { + ScanSettings scanSettings = new ScanSettings(); + WifiScanner.ScanListener scanListener = mock(WifiScanner.ScanListener.class); + + mWifiScanner.startScan(scanSettings, scanListener); + mLooper.dispatchAll(); + + ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); + verify(mHandler).handleMessage(messageArgumentCaptor.capture()); + Message message = messageArgumentCaptor.getValue(); + assertNotNull(message); + + assertEquals(WifiScanner.CMD_START_SINGLE_SCAN, message.what); + assertTrue(message.obj instanceof Bundle); + Bundle messageBundle = (Bundle) message.obj; + assertEquals(scanSettings, + messageBundle.getParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY)); + assertNull(messageBundle.getParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY)); + assertEquals(mContext.getOpPackageName(), + messageBundle.getParcelable(WifiScanner.REQUEST_PACKAGE_NAME_KEY)); + + } + + /** + * Test behavior of {@link WifiScanner#stopScan(WifiScanner.ScanListener)} + * @throws Exception + */ + @Test + public void testStopScan() throws Exception { + ScanSettings scanSettings = new ScanSettings(); + WifiScanner.ScanListener scanListener = mock(WifiScanner.ScanListener.class); + + mWifiScanner.startScan(scanSettings, scanListener); + mLooper.dispatchAll(); + + mWifiScanner.stopScan(scanListener); + mLooper.dispatchAll(); + + ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); + verify(mHandler, times(2)).handleMessage(messageArgumentCaptor.capture()); + Message message = messageArgumentCaptor.getValue(); + assertNotNull(message); + + assertEquals(WifiScanner.CMD_STOP_SINGLE_SCAN, message.what); + assertTrue(message.obj instanceof Bundle); + Bundle messageBundle = (Bundle) message.obj; + assertEquals(mContext.getOpPackageName(), + messageBundle.getParcelable(WifiScanner.REQUEST_PACKAGE_NAME_KEY)); + + } + + /** + * Test behavior of {@link WifiScanner#startScan(ScanSettings, WifiScanner.ScanListener)} + * @throws Exception + */ + @Test + public void testStartScanListenerOnSuccess() throws Exception { + ScanSettings scanSettings = new ScanSettings(); + WifiScanner.ScanListener scanListener = mock(WifiScanner.ScanListener.class); + + mWifiScanner.startScan(scanSettings, scanListener); + mLooper.dispatchAll(); + + ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); + verify(mHandler).handleMessage(messageArgumentCaptor.capture()); + Message sentMessage = messageArgumentCaptor.getValue(); + assertNotNull(sentMessage); + + assertEquals(1, mBidirectionalAsyncChannelServer.getClientMessengers().size()); + Messenger scannerMessenger = + mBidirectionalAsyncChannelServer.getClientMessengers().iterator().next(); + + Message responseMessage = Message.obtain(); + responseMessage.what = WifiScanner.CMD_OP_SUCCEEDED; + responseMessage.arg2 = sentMessage.arg2; + scannerMessenger.send(responseMessage); + mLooper.dispatchAll(); + + verify(scanListener).onSuccess(); + } + + /** + * Test behavior of {@link WifiScanner#startScan(ScanSettings, WifiScanner.ScanListener)} + * @throws Exception + */ + @Test + public void testStartScanListenerOnResults() throws Exception { + ScanSettings scanSettings = new ScanSettings(); + WifiScanner.ScanListener scanListener = mock(WifiScanner.ScanListener.class); + + mWifiScanner.startScan(scanSettings, scanListener); + mLooper.dispatchAll(); + + ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); + verify(mHandler).handleMessage(messageArgumentCaptor.capture()); + Message sentMessage = messageArgumentCaptor.getValue(); + assertNotNull(sentMessage); + + assertEquals(1, mBidirectionalAsyncChannelServer.getClientMessengers().size()); + Messenger scannerMessenger = + mBidirectionalAsyncChannelServer.getClientMessengers().iterator().next(); + + ScanResult scanResult = new ScanResult(); + ScanData scanDatas[] = new ScanData[]{new ScanData(0, 0 , new ScanResult[] {scanResult})}; + Message responseMessage = Message.obtain(); + responseMessage.what = WifiScanner.CMD_SCAN_RESULT; + responseMessage.arg2 = sentMessage.arg2; + responseMessage.obj = new WifiScanner.ParcelableScanData(scanDatas); + scannerMessenger.send(responseMessage); + mLooper.dispatchAll(); + + verify(scanListener).onResults(scanDatas); + } + + /** + * Test behavior of {@link WifiScanner#startDisconnectedPnoScan(ScanSettings, PnoSettings, + * WifiScanner.PnoScanListener)} + * @throws Exception + */ + @Test + public void testStartDisconnectedPnoScan() throws Exception { + ScanSettings scanSettings = new ScanSettings(); + PnoSettings pnoSettings = new PnoSettings(); + WifiScanner.PnoScanListener pnoScanListener = mock(WifiScanner.PnoScanListener.class); + + mWifiScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, pnoScanListener); + mLooper.dispatchAll(); + + ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); + verify(mHandler).handleMessage(messageArgumentCaptor.capture()); + Message message = messageArgumentCaptor.getValue(); + assertNotNull(message); + + assertEquals(WifiScanner.CMD_START_PNO_SCAN, message.what); + assertTrue(message.obj instanceof Bundle); + Bundle messageBundle = (Bundle) message.obj; + assertEquals(scanSettings, + messageBundle.getParcelable(WifiScanner.PNO_PARAMS_SCAN_SETTINGS_KEY)); + assertTrue(scanSettings.isPnoScan); + assertFalse(pnoSettings.isConnected); + assertEquals(pnoSettings, + messageBundle.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY)); + } + + /** + * Test behavior of {@link WifiScanner#startConnectedPnoScan(ScanSettings, PnoSettings, + * WifiScanner.PnoScanListener)} + * @throws Exception + */ + @Test + public void testStartConnectedPnoScan() throws Exception { + ScanSettings scanSettings = new ScanSettings(); + PnoSettings pnoSettings = new PnoSettings(); + WifiScanner.PnoScanListener pnoScanListener = mock(WifiScanner.PnoScanListener.class); + + mWifiScanner.startConnectedPnoScan(scanSettings, pnoSettings, pnoScanListener); + mLooper.dispatchAll(); + + ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); + verify(mHandler).handleMessage(messageArgumentCaptor.capture()); + Message message = messageArgumentCaptor.getValue(); + assertNotNull(message); + + assertEquals(WifiScanner.CMD_START_PNO_SCAN, message.what); + assertTrue(message.obj instanceof Bundle); + Bundle messageBundle = (Bundle) message.obj; + assertEquals(scanSettings, + messageBundle.getParcelable(WifiScanner.PNO_PARAMS_SCAN_SETTINGS_KEY)); + assertTrue(scanSettings.isPnoScan); + assertTrue(pnoSettings.isConnected); + assertEquals(pnoSettings, + messageBundle.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY)); + } + + /** + * Test behavior of {@link WifiScanner#stopPnoScan(WifiScanner.ScanListener)} + * WifiScanner.PnoScanListener)} + * @throws Exception + */ + @Test + public void testStopPnoScan() throws Exception { + ScanSettings scanSettings = new ScanSettings(); + PnoSettings pnoSettings = new PnoSettings(); + WifiScanner.PnoScanListener pnoScanListener = mock(WifiScanner.PnoScanListener.class); + + mWifiScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, pnoScanListener); + mLooper.dispatchAll(); + mWifiScanner.stopPnoScan(pnoScanListener); + mLooper.dispatchAll(); + + ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); + verify(mHandler, times(2)).handleMessage(messageArgumentCaptor.capture()); + Message message = messageArgumentCaptor.getValue(); + assertNotNull(message); + + assertEquals(WifiScanner.CMD_STOP_PNO_SCAN, message.what); + } } |