summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp2
-rw-r--r--Android.mk46
-rw-r--r--api/current.txt28
-rw-r--r--api/system-current.txt12
-rw-r--r--api/test-current.txt7
-rw-r--r--cmds/incidentd/src/IncidentService.cpp42
-rw-r--r--cmds/incidentd/src/IncidentService.h1
-rw-r--r--cmds/incidentd/src/main.cpp3
-rw-r--r--cmds/statsd/src/guardrail/StatsdStats.cpp1
-rw-r--r--config/hiddenapi-light-greylist.txt6
-rw-r--r--core/java/android/app/ActivityView.java52
-rw-r--r--core/java/android/app/Notification.java12
-rw-r--r--core/java/android/app/WallpaperManager.java28
-rw-r--r--core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java9
-rw-r--r--core/java/android/bluetooth/BluetoothManager.java3
-rw-r--r--core/java/android/content/ContentResolver.java3
-rw-r--r--core/java/android/content/pm/PackageManager.java25
-rw-r--r--core/java/android/content/pm/PackageManagerInternal.java20
-rw-r--r--core/java/android/content/pm/PackageParser.java2
-rw-r--r--core/java/android/hardware/biometrics/BiometricAuthenticator.java16
-rw-r--r--core/java/android/hardware/biometrics/BiometricPrompt.java46
-rw-r--r--core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl24
-rw-r--r--core/java/android/hardware/biometrics/IBiometricService.aidl8
-rw-r--r--core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl16
-rw-r--r--core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl41
-rw-r--r--core/java/android/hardware/face/IFaceService.aidl23
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl23
-rw-r--r--core/java/android/os/Binder.java4
-rw-r--r--core/java/android/os/UserManager.java21
-rw-r--r--core/java/android/os/storage/StorageManager.java7
-rw-r--r--core/java/android/provider/Settings.java19
-rw-r--r--core/java/android/provider/TEST_MAPPING12
-rw-r--r--core/java/android/service/oemlock/IOemLockService.aidl2
-rw-r--r--core/java/android/service/oemlock/OemLockManager.java17
-rw-r--r--core/java/android/view/IWindowManager.aidl14
-rw-r--r--core/java/android/view/SurfaceControl.java28
-rw-r--r--core/java/android/view/SurfaceView.java7
-rw-r--r--core/java/android/view/View.java56
-rw-r--r--core/java/android/view/ViewGroup.java5
-rw-r--r--core/java/android/view/WindowManagerPolicyConstants.java1
-rw-r--r--core/java/android/widget/GridView.java5
-rw-r--r--core/java/com/android/internal/os/BinderCallsStats.java49
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java1
-rw-r--r--core/java/com/android/internal/policy/ScreenDecorationsUtils.java50
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl6
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl6
-rw-r--r--core/java/com/android/internal/util/BitUtils.java9
-rw-r--r--core/java/com/android/internal/util/CollectionUtils.java8
-rw-r--r--core/java/com/android/server/SystemConfig.java2
-rw-r--r--core/jni/android_text_AndroidCharacter.cpp16
-rw-r--r--core/jni/android_view_SurfaceControl.cpp10
-rw-r--r--core/proto/android/providers/settings/secure.proto10
-rw-r--r--core/res/AndroidManifest.xml47
-rw-r--r--core/res/res/layout/notification_template_header.xml1
-rw-r--r--core/res/res/values/dimens.xml2
-rw-r--r--core/res/res/values/strings.xml19
-rw-r--r--core/res/res/values/symbols.xml6
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java4
-rw-r--r--core/tests/coretests/src/android/text/AndroidCharacterTest.java71
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java56
-rw-r--r--graphics/java/android/graphics/Typeface.java35
-rw-r--r--libs/hwui/Android.bp1
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp2
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h2
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp2
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.h2
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp44
-rw-r--r--libs/hwui/renderthread/CanvasContext.h3
-rw-r--r--libs/hwui/renderthread/EglManager.cpp4
-rw-r--r--libs/hwui/renderthread/IRenderPipeline.h6
-rw-r--r--libs/hwui/renderthread/ReliableSurface.cpp283
-rw-r--r--libs/hwui/renderthread/ReliableSurface.h85
-rw-r--r--media/java/android/media/MediaPlayer2.java1248
-rw-r--r--media/java/android/media/RingtoneManager.java3
-rw-r--r--native/webview/plat_support/draw_gl.h12
-rw-r--r--packages/PackageInstaller/Android.bp14
-rw-r--r--packages/PackageInstaller/Android.mk32
-rw-r--r--packages/PackageInstaller/AndroidManifest.xml2
-rw-r--r--packages/SettingsLib/ActionButtonsPreference/Android.bp13
-rw-r--r--packages/SettingsLib/ActionButtonsPreference/AndroidManifest.xml23
-rw-r--r--packages/SettingsLib/ActionButtonsPreference/res/layout/settings_action_buttons.xml52
-rw-r--r--packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml26
-rw-r--r--packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java393
-rw-r--r--packages/SettingsLib/Android.bp1
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java4
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java281
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java9
-rw-r--r--packages/SystemUI/res/layout/biometric_dialog.xml9
-rw-r--r--packages/SystemUI/res/values/strings.xml4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java57
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java128
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java5
-rw-r--r--services/core/java/com/android/server/AppOpsService.java2
-rw-r--r--services/core/java/com/android/server/BinderCallsStatsService.java4
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java3
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java6
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java5
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java5
-rw-r--r--services/core/java/com/android/server/biometrics/AuthenticationClient.java343
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java628
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricServiceBase.java183
-rw-r--r--services/core/java/com/android/server/biometrics/ClientMonitor.java12
-rw-r--r--services/core/java/com/android/server/biometrics/EnrollClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/EnumerateClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/RemovalClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/face/FaceService.java95
-rw-r--r--services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java96
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java174
-rw-r--r--services/core/java/com/android/server/oemlock/OemLock.java3
-rw-r--r--services/core/java/com/android/server/oemlock/OemLockService.java13
-rw-r--r--services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java6
-rw-r--r--services/core/java/com/android/server/oemlock/VendorLock.java43
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java57
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java6
-rw-r--r--services/core/java/com/android/server/role/RoleManagerService.java57
-rw-r--r--services/core/java/com/android/server/role/RoleUserState.java41
-rw-r--r--services/core/java/com/android/server/stats/StatsCompanionService.java13
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java17
-rw-r--r--services/core/java/com/android/server/tv/TvInputManagerService.java22
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java40
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java12
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java14
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java42
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java12
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java8
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl7
-rwxr-xr-xtools/hiddenapi/generate_hiddenapi_lists.py403
-rwxr-xr-xtools/hiddenapi/generate_hiddenapi_lists_test.py168
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl4
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java78
-rw-r--r--wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java76
-rw-r--r--wifi/java/android/net/wifi/WifiNetworkSuggestion.java5
-rw-r--r--wifi/java/android/net/wifi/WifiScanner.java10
-rw-r--r--wifi/java/com/android/server/wifi/AbstractWifiService.java4
-rw-r--r--wifi/tests/src/android/net/wifi/WifiManagerTest.java21
-rw-r--r--wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java76
-rw-r--r--wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java34
154 files changed, 4573 insertions, 2338 deletions
diff --git a/Android.bp b/Android.bp
index 25e738ccb3cf..41d5f28a2680 100644
--- a/Android.bp
+++ b/Android.bp
@@ -158,9 +158,9 @@ java_defaults {
"core/java/android/hardware/IConsumerIrService.aidl",
"core/java/android/hardware/ISerialManager.aidl",
"core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl",
- "core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl",
"core/java/android/hardware/biometrics/IBiometricService.aidl",
"core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl",
+ "core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl",
"core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl",
"core/java/android/hardware/display/IDisplayManager.aidl",
"core/java/android/hardware/display/IDisplayManagerCallback.aidl",
diff --git a/Android.mk b/Android.mk
index 770ec20f151e..b7dda9a45ed8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -73,55 +73,37 @@ $(OUT_DOCS)/offline-sdk-timestamp: $(OUT_DOCS)/offline-sdk-docs-docs.zip
( unzip -qo $< -d $(OUT_DOCS)/offline-sdk && touch -f $@ ) || exit 1
# ==== hiddenapi lists =======================================
-.KATI_RESTAT: \
- $(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST): \
- .KATI_IMPLICIT_OUTPUTS := \
- $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST): \
+.KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS)
+$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \
frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
frameworks/base/config/hiddenapi-light-greylist.txt \
frameworks/base/config/hiddenapi-vendor-list.txt \
+ frameworks/base/config/hiddenapi-greylist-max-o.txt \
frameworks/base/config/hiddenapi-max-sdk-p-blacklist.txt \
frameworks/base/config/hiddenapi-force-blacklist.txt \
$(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
$(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE)
frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
- --input-public $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
- --input-private $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
- --input-whitelists $(PRIVATE_WHITELIST_INPUTS) \
- --input-greylists \
+ --public $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
+ --private $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
+ --csv $(PRIVATE_FLAGS_INPUTS) \
+ --greylist \
frameworks/base/config/hiddenapi-light-greylist.txt \
frameworks/base/config/hiddenapi-vendor-list.txt \
- frameworks/base/config/hiddenapi-max-sdk-p-blacklist.txt \
- <(comm -12 <(sort $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE)) \
- $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST)) \
- $(PRIVATE_GREYLIST_INPUTS) \
- --input-blacklists frameworks/base/config/hiddenapi-force-blacklist.txt \
- --output-whitelist $(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST).tmp \
- --output-light-greylist $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST).tmp \
- --output-dark-greylist $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST).tmp \
- --output-blacklist $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST).tmp
- $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST))
- $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST))
- $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST))
- $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST))
+ --greylist-ignore-conflicts $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \
+ --greylist-max-o-ignore-conflicts \
+ frameworks/base/config/hiddenapi-greylist-max-o.txt \
+ --blacklist frameworks/base/config/hiddenapi-force-blacklist.txt \
+ --output $@.tmp
+ $(call commit-change-for-toc,$@)
$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA): \
frameworks/base/tools/hiddenapi/merge_csv.py \
$(PRIVATE_METADATA_INPUTS)
frameworks/base/tools/hiddenapi/merge_csv.py $(PRIVATE_METADATA_INPUTS) > $@
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST))
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST))
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST))
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST))
+$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS))
$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA))
# Include subdirectory makefiles
diff --git a/api/current.txt b/api/current.txt
index 0dce8d490f7c..079539e3c5f4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -150,6 +150,7 @@ package android {
field public static final java.lang.String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS";
field public static final java.lang.String USE_BIOMETRIC = "android.permission.USE_BIOMETRIC";
field public static final deprecated java.lang.String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT";
+ field public static final java.lang.String USE_FULL_SCREEN_INTENT = "android.permission.USE_FULL_SCREEN_INTENT";
field public static final java.lang.String USE_SIP = "android.permission.USE_SIP";
field public static final java.lang.String VIBRATE = "android.permission.VIBRATE";
field public static final java.lang.String WAKE_LOCK = "android.permission.WAKE_LOCK";
@@ -11339,7 +11340,7 @@ package android.content.pm {
method public abstract int checkSignatures(java.lang.String, java.lang.String);
method public abstract int checkSignatures(int, int);
method public abstract void clearInstantAppCookie();
- method public abstract void clearPackagePreferredActivities(java.lang.String);
+ method public abstract deprecated void clearPackagePreferredActivities(java.lang.String);
method public abstract java.lang.String[] currentToCanonicalPackageNames(java.lang.String[]);
method public abstract void extendVerificationTimeout(int, int, long);
method public abstract android.graphics.drawable.Drawable getActivityBanner(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -11383,8 +11384,8 @@ package android.content.pm {
method public abstract java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
method public abstract android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.pm.PermissionInfo getPermissionInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public abstract int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String);
- method public abstract java.util.List<android.content.pm.PackageInfo> getPreferredPackages(int);
+ method public abstract deprecated int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String);
+ method public abstract deprecated java.util.List<android.content.pm.PackageInfo> getPreferredPackages(int);
method public abstract android.content.pm.ProviderInfo getProviderInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.pm.ActivityInfo getReceiverInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.res.Resources getResourcesForActivity(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -29054,7 +29055,7 @@ package android.net.wifi {
public class WifiManager {
method public deprecated int addNetwork(android.net.wifi.WifiConfiguration);
- method public boolean addNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>);
+ method public int addNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>);
method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration);
method public static int calculateSignalLevel(int, int);
method public deprecated void cancelWps(android.net.wifi.WifiManager.WpsCallback);
@@ -29068,6 +29069,7 @@ package android.net.wifi {
method public deprecated java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks();
method public android.net.wifi.WifiInfo getConnectionInfo();
method public android.net.DhcpInfo getDhcpInfo();
+ method public int getMaxNumberOfNetworkSuggestionsPerApp();
method public java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations();
method public java.util.List<android.net.wifi.ScanResult> getScanResults();
method public int getWifiState();
@@ -29086,7 +29088,7 @@ package android.net.wifi {
method public deprecated boolean reassociate();
method public deprecated boolean reconnect();
method public deprecated boolean removeNetwork(int);
- method public boolean removeNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>);
+ method public int removeNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>);
method public void removePasspointConfiguration(java.lang.String);
method public deprecated boolean saveConfiguration();
method public void setTdlsEnabled(java.net.InetAddress, boolean);
@@ -29115,6 +29117,11 @@ package android.net.wifi {
field public static final java.lang.String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE";
field public static final java.lang.String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED";
field public static final java.lang.String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS";
+ field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 2; // 0x2
+ field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 3; // 0x3
+ field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1; // 0x1
+ field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 4; // 0x4
+ field public static final int STATUS_NETWORK_SUGGESTIONS_SUCCESS = 0; // 0x0
field public static final deprecated java.lang.String SUPPLICANT_CONNECTION_CHANGE_ACTION = "android.net.wifi.supplicant.CONNECTION_CHANGE";
field public static final deprecated java.lang.String SUPPLICANT_STATE_CHANGED_ACTION = "android.net.wifi.supplicant.STATE_CHANGE";
field public static final int WIFI_MODE_FULL = 1; // 0x1
@@ -29156,6 +29163,9 @@ package android.net.wifi {
method public void setReferenceCounted(boolean);
}
+ public static abstract class WifiManager.NetworkSuggestionsStatusCode implements java.lang.annotation.Annotation {
+ }
+
public class WifiManager.WifiLock {
method public void acquire();
method public boolean isHeld();
@@ -49070,10 +49080,12 @@ package android.view {
method protected int getTopPaddingOffset();
method public android.view.TouchDelegate getTouchDelegate();
method public java.util.ArrayList<android.view.View> getTouchables();
+ method public float getTransitionAlpha();
method public java.lang.String getTransitionName();
method public float getTranslationX();
method public float getTranslationY();
method public float getTranslationZ();
+ method public long getUniqueDrawingId();
method public int getVerticalFadingEdgeLength();
method public int getVerticalScrollbarPosition();
method public int getVerticalScrollbarWidth();
@@ -49278,6 +49290,7 @@ package android.view {
method public void setActivated(boolean);
method public void setAlpha(float);
method public void setAnimation(android.view.animation.Animation);
+ method public void setAnimationMatrix(android.graphics.Matrix);
method public void setAutofillHints(java.lang.String...);
method public void setAutofillId(android.view.autofill.AutofillId);
method public void setBackground(android.graphics.drawable.Drawable);
@@ -49329,7 +49342,7 @@ package android.view {
method public void setLayoutDirection(int);
method public void setLayoutParams(android.view.ViewGroup.LayoutParams);
method public final void setLeft(int);
- method public void setLeftTopRightBottom(int, int, int, int);
+ method public final void setLeftTopRightBottom(int, int, int, int);
method public void setLongClickable(boolean);
method protected final void setMeasuredDimension(int, int);
method public void setMinimumHeight(int);
@@ -49396,6 +49409,7 @@ package android.view {
method public void setTooltipText(java.lang.CharSequence);
method public final void setTop(int);
method public void setTouchDelegate(android.view.TouchDelegate);
+ method public void setTransitionAlpha(float);
method public final void setTransitionName(java.lang.String);
method public void setTranslationX(float);
method public void setTranslationY(float);
@@ -49869,6 +49883,7 @@ package android.view {
method public deprecated boolean isAnimationCacheEnabled();
method protected boolean isChildrenDrawingOrderEnabled();
method protected deprecated boolean isChildrenDrawnWithCacheEnabled();
+ method public boolean isLayoutSuppressed();
method public boolean isMotionEventSplittingEnabled();
method public boolean isTransitionGroup();
method public final void layout(int, int, int, int);
@@ -49934,6 +49949,7 @@ package android.view {
method public android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
method public void startLayoutAnimation();
method public void startViewTransition(android.view.View);
+ method public void suppressLayout(boolean);
method public void updateViewLayout(android.view.View, android.view.ViewGroup.LayoutParams);
field protected static final int CLIP_TO_PADDING_MASK = 34; // 0x22
field public static final int FOCUS_AFTER_DESCENDANTS = 262144; // 0x40000
diff --git a/api/system-current.txt b/api/system-current.txt
index 2a581a806a8a..62d446f7254e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -217,6 +217,10 @@ package android {
field public static final java.lang.String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS";
}
+ public static final class Manifest.permission_group {
+ field public static final java.lang.String UNDEFINED = "android.permission-group.UNDEFINED";
+ }
+
public static final class R.array {
field public static final int config_keySystemUuidMapping = 17235973; // 0x1070005
}
@@ -1240,7 +1244,7 @@ package android.content.pm {
method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(android.content.Intent, int, android.os.UserHandle);
method public abstract void registerDexModule(java.lang.String, android.content.pm.PackageManager.DexModuleRegisterCallback);
method public abstract void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
- method public void replacePreferredActivity(android.content.IntentFilter, int, java.util.List<android.content.ComponentName>, android.content.ComponentName);
+ method public deprecated void replacePreferredActivity(android.content.IntentFilter, int, java.util.List<android.content.ComponentName>, android.content.ComponentName);
method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public void sendDeviceCustomizationReadyBroadcast();
method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
@@ -3689,7 +3693,6 @@ package android.net.wifi {
method public void disableEphemeralNetwork(java.lang.String);
method public void forget(int, android.net.wifi.WifiManager.ActionListener);
method public java.util.List<android.net.wifi.WifiConfiguration> getAllMatchingWifiConfigs(java.util.List<android.net.wifi.ScanResult>);
- method public java.util.List<android.net.wifi.hotspot2.OsuProvider> getMatchingOsuProviders(java.util.List<android.net.wifi.ScanResult>);
method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
method public android.net.wifi.WifiConfiguration getWifiApConfiguration();
method public int getWifiApState();
@@ -3848,6 +3851,7 @@ package android.net.wifi {
ctor public WifiScanner.ScanSettings();
field public int band;
field public android.net.wifi.WifiScanner.ChannelSpec[] channels;
+ field public boolean ignoreLocationSettings;
field public int maxPeriodInMs;
field public int maxScansToCache;
field public int numBssidsPerScan;
@@ -4305,6 +4309,7 @@ package android.os {
public class UserManager {
method public void clearSeedAccountData();
+ method public android.os.UserHandle getProfileParent(android.os.UserHandle);
method public java.lang.String getSeedAccountName();
method public android.os.PersistableBundle getSeedAccountOptions();
method public java.lang.String getSeedAccountType();
@@ -4636,6 +4641,8 @@ package android.provider {
field public static final java.lang.String HUSH_GESTURE_USED = "hush_gesture_used";
field public static final java.lang.String INSTANT_APPS_ENABLED = "instant_apps_enabled";
field public static final java.lang.String LAST_SETUP_SHOWN = "last_setup_shown";
+ field public static final java.lang.String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis";
+ field public static final java.lang.String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis";
field public static final java.lang.String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications";
field public static final java.lang.String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications";
field public static final java.lang.String MANUAL_RINGER_TOGGLE_COUNT = "manual_ringer_toggle_count";
@@ -5174,6 +5181,7 @@ package android.service.notification {
package android.service.oemlock {
public class OemLockManager {
+ method public java.lang.String getLockName();
method public boolean isOemUnlockAllowedByCarrier();
method public boolean isOemUnlockAllowedByUser();
method public void setOemUnlockAllowedByCarrier(boolean, byte[]);
diff --git a/api/test-current.txt b/api/test-current.txt
index b871c78571ad..46cbb52f6efa 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -943,6 +943,10 @@ package android.os.health {
package android.os.storage {
+ public class StorageManager {
+ method public static boolean hasIsolatedStorage();
+ }
+
public final class StorageVolume implements android.os.Parcelable {
method public java.lang.String getPath();
}
@@ -998,6 +1002,7 @@ package android.provider {
}
public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
+ method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled";
field public static final java.lang.String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service";
field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
@@ -1009,6 +1014,8 @@ package android.provider {
field public static final java.lang.String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
field public static final java.lang.String DISABLED_PRINT_SERVICES = "disabled_print_services";
field public static final deprecated java.lang.String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages";
+ field public static final java.lang.String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis";
+ field public static final java.lang.String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis";
field public static final java.lang.String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
field public static final java.lang.String USER_SETUP_COMPLETE = "user_setup_complete";
field public static final java.lang.String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index 1c3ebd877695..b23c1eda87b3 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -24,6 +24,7 @@
#include "incidentd_util.h"
#include "section_list.h"
+#include <android/os/IncidentReportArgs.h>
#include <binder/IPCThreadState.h>
#include <binder/IResultReceiver.h>
#include <binder/IServiceManager.h>
@@ -41,6 +42,15 @@ enum { WHAT_RUN_REPORT = 1, WHAT_SEND_BACKLOG_TO_DROPBOX = 2 };
#define DEFAULT_BYTES_SIZE_LIMIT (20 * 1024 * 1024) // 20MB
#define DEFAULT_REFACTORY_PERIOD_MS (24 * 60 * 60 * 1000) // 1 Day
+// Skip logs (1100 - 1108) because they are already in the bug report
+// Skip 1200, 1201, 1202, 3018 because they take too long
+// TODO(120079956): Skip 3008, 3015 because of error
+#define SKIPPED_SECTIONS { 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, /* Logs */ \
+ 1200, 1201, 1202, /* Native, hal, java traces */ \
+ 3008, /* "package --proto" */ \
+ 3015, /* "activity --proto processes" */ \
+ 3018 /* "meminfo -a --proto" */ }
+
namespace android {
namespace os {
namespace incidentd {
@@ -391,6 +401,38 @@ status_t IncidentService::cmd_privacy(FILE* in, FILE* out, FILE* err, Vector<Str
return NO_ERROR;
}
+status_t IncidentService::dump(int fd, const Vector<String16>& args) {
+ if (std::find(args.begin(), args.end(), String16("--proto")) == args.end()) {
+ ALOGD("Skip dumping incident. Only proto format is supported.");
+ dprintf(fd, "Incident dump only supports proto version.\n");
+ return NO_ERROR;
+ }
+
+ ALOGD("Dump incident proto");
+ IncidentReportArgs incidentArgs;
+ incidentArgs.setDest(DEST_EXPLICIT);
+ int skipped[] = SKIPPED_SECTIONS;
+ for (const Section** section = SECTION_LIST; *section; section++) {
+ const int id = (*section)->id;
+ if (std::find(std::begin(skipped), std::end(skipped), id) == std::end(skipped)) {
+ incidentArgs.addSection(id);
+ }
+ }
+
+ if (!checkIncidentPermissions(incidentArgs).isOk()) {
+ return PERMISSION_DENIED;
+ }
+
+ int fd1 = dup(fd);
+ if (fd1 < 0) {
+ return -errno;
+ }
+
+ mHandler->scheduleRunReport(new ReportRequest(incidentArgs, NULL, fd1));
+
+ return NO_ERROR;
+}
+
} // namespace incidentd
} // namespace os
} // namespace android
diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h
index e176bfd95c5f..6252ad295224 100644
--- a/cmds/incidentd/src/IncidentService.h
+++ b/cmds/incidentd/src/IncidentService.h
@@ -112,6 +112,7 @@ public:
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags) override;
virtual status_t command(FILE* in, FILE* out, FILE* err, Vector<String8>& args);
+ virtual status_t dump(int fd, const Vector<String16>& args);
private:
sp<ReportRequestQueue> mQueue;
diff --git a/cmds/incidentd/src/main.cpp b/cmds/incidentd/src/main.cpp
index 494882336611..098d74ecd786 100644
--- a/cmds/incidentd/src/main.cpp
+++ b/cmds/incidentd/src/main.cpp
@@ -45,7 +45,8 @@ int main(int /*argc*/, char** /*argv*/) {
// Create the service
sp<IncidentService> service = new IncidentService(looper);
- if (defaultServiceManager()->addService(String16("incident"), service) != 0) {
+ if (defaultServiceManager()->addService(String16("incident"), service, false,
+ IServiceManager::DUMP_FLAG_PRIORITY_NORMAL | IServiceManager::DUMP_FLAG_PROTO) != 0) {
ALOGE("Failed to add service");
return -1;
}
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 661768914c69..3e5e82f7f4df 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -101,6 +101,7 @@ const int FIELD_ID_UID_MAP_DELETED_APPS = 4;
const std::map<int, std::pair<size_t, size_t>> StatsdStats::kAtomDimensionKeySizeLimitMap = {
{android::util::BINDER_CALLS, {6000, 10000}},
+ {android::util::LOOPER_STATS, {1500, 2500}},
{android::util::CPU_TIME_PER_UID_FREQ, {6000, 10000}},
};
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 25bd0330ad5c..e10f72913d22 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -2695,7 +2695,6 @@ Lcom/android/internal/telephony/dataconnection/DcTracker;->onRecordsLoadedOrSubI
Lcom/android/internal/telephony/dataconnection/DcTracker;->onSetUserDataEnabled(Z)V
Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Lcom/android/internal/telephony/dataconnection/ApnContext;)Z
Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Ljava/lang/String;)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->registerForAllDataDisconnected(Landroid/os/Handler;ILjava/lang/Object;)V
Lcom/android/internal/telephony/dataconnection/DcTracker;->registerSettingsObserver()V
Lcom/android/internal/telephony/dataconnection/DcTracker;->resetPollStats()V
Lcom/android/internal/telephony/dataconnection/DcTracker;->restartDataStallAlarm()V
@@ -2709,7 +2708,6 @@ Lcom/android/internal/telephony/dataconnection/DcTracker;->startNetStatPoll()V
Lcom/android/internal/telephony/dataconnection/DcTracker;->stopDataStallAlarm()V
Lcom/android/internal/telephony/dataconnection/DcTracker;->stopNetStatPoll()V
Lcom/android/internal/telephony/dataconnection/DcTracker;->unregisterForAllDataDisconnected(Landroid/os/Handler;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->updateRecords()V
Lcom/android/internal/telephony/DctConstants$Activity;->DATAIN:Lcom/android/internal/telephony/DctConstants$Activity;
Lcom/android/internal/telephony/DctConstants$Activity;->DATAINANDOUT:Lcom/android/internal/telephony/DctConstants$Activity;
Lcom/android/internal/telephony/DctConstants$Activity;->DATAOUT:Lcom/android/internal/telephony/DctConstants$Activity;
@@ -3130,7 +3128,6 @@ Lcom/android/internal/telephony/ITelephony;->getCallState()I
Lcom/android/internal/telephony/ITelephony;->getDataActivity()I
Lcom/android/internal/telephony/ITelephony;->getDataState()I
Lcom/android/internal/telephony/ITelephony;->getNetworkType()I
-Lcom/android/internal/telephony/ITelephony;->getVoiceMessageCount()I
Lcom/android/internal/telephony/ITelephony;->handlePinMmi(Ljava/lang/String;)Z
Lcom/android/internal/telephony/ITelephony;->handlePinMmiForSubscriber(ILjava/lang/String;)Z
Lcom/android/internal/telephony/ITelephony;->hasIccCard()Z
@@ -3201,7 +3198,6 @@ Lcom/android/internal/telephony/Phone;->isVolteEnabled()Z
Lcom/android/internal/telephony/Phone;->isWifiCallingEnabled()Z
Lcom/android/internal/telephony/Phone;->mCi:Lcom/android/internal/telephony/CommandsInterface;
Lcom/android/internal/telephony/Phone;->mContext:Landroid/content/Context;
-Lcom/android/internal/telephony/Phone;->mDcTracker:Lcom/android/internal/telephony/dataconnection/DcTracker;
Lcom/android/internal/telephony/Phone;->mIccRecords:Ljava/util/concurrent/atomic/AtomicReference;
Lcom/android/internal/telephony/Phone;->mImsPhone:Lcom/android/internal/telephony/Phone;
Lcom/android/internal/telephony/Phone;->mMmiRegistrants:Landroid/os/RegistrantList;
@@ -3294,7 +3290,6 @@ Lcom/android/internal/telephony/ProxyController;->mOldRadioAccessFamily:[I
Lcom/android/internal/telephony/ProxyController;->mRadioCapabilitySessionId:I
Lcom/android/internal/telephony/ProxyController;->mSetRadioAccessFamilyStatus:[I
Lcom/android/internal/telephony/ProxyController;->mUniqueIdGenerator:Ljava/util/concurrent/atomic/AtomicInteger;
-Lcom/android/internal/telephony/ProxyController;->registerForAllDataDisconnected(ILandroid/os/Handler;ILjava/lang/Object;)V
Lcom/android/internal/telephony/ProxyController;->sendRadioCapabilityRequest(IIIILjava/lang/String;II)V
Lcom/android/internal/telephony/ProxyController;->sProxyController:Lcom/android/internal/telephony/ProxyController;
Lcom/android/internal/telephony/RadioCapability;->getRadioAccessFamily()I
@@ -3394,7 +3389,6 @@ Lcom/android/internal/telephony/ServiceStateTracker;->mVoiceRoamingOnRegistrants
Lcom/android/internal/telephony/ServiceStateTracker;->notifyDataRegStateRilRadioTechnologyChanged()V
Lcom/android/internal/telephony/ServiceStateTracker;->notifySignalStrength()Z
Lcom/android/internal/telephony/ServiceStateTracker;->pollState()V
-Lcom/android/internal/telephony/ServiceStateTracker;->powerOffRadioSafely(Lcom/android/internal/telephony/dataconnection/DcTracker;)V
Lcom/android/internal/telephony/ServiceStateTracker;->reRegisterNetwork(Landroid/os/Message;)V
Lcom/android/internal/telephony/ServiceStateTracker;->resetServiceStateInIwlanMode()V
Lcom/android/internal/telephony/ServiceStateTracker;->setOperatorIdd(Ljava/lang/String;)V
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index c879db8967d3..446d98e97936 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -34,7 +34,9 @@ import android.view.InputDevice;
import android.view.InputEvent;
import android.view.MotionEvent;
import android.view.Surface;
+import android.view.SurfaceControl;
import android.view.SurfaceHolder;
+import android.view.SurfaceSession;
import android.view.SurfaceView;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -59,12 +61,16 @@ public class ActivityView extends ViewGroup {
private VirtualDisplay mVirtualDisplay;
private final SurfaceView mSurfaceView;
- private Surface mSurface;
+
+ /**
+ * This is the root surface for the VirtualDisplay. The VirtualDisplay child surfaces will be
+ * re-parented to this surface. This will also be a child of the SurfaceView's SurfaceControl.
+ */
+ private SurfaceControl mRootSurfaceControl;
private final SurfaceCallback mSurfaceCallback;
private StateCallback mActivityViewCallback;
- private IActivityManager mActivityManager;
private IActivityTaskManager mActivityTaskManager;
private IInputForwarder mInputForwarder;
// Temp container to store view coordinates on screen.
@@ -75,6 +81,9 @@ public class ActivityView extends ViewGroup {
private final CloseGuard mGuard = CloseGuard.get();
private boolean mOpened; // Protected by mGuard.
+ private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
+ private Surface mTmpSurface = new Surface();
+
@UnsupportedAppUsage
public ActivityView(Context context) {
this(context, null /* attrs */);
@@ -87,7 +96,6 @@ public class ActivityView extends ViewGroup {
public ActivityView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mActivityManager = ActivityManager.getService();
mActivityTaskManager = ActivityTaskManager.getService();
mSurfaceView = new SurfaceView(context);
mSurfaceCallback = new SurfaceCallback();
@@ -297,14 +305,19 @@ public class ActivityView extends ViewGroup {
private class SurfaceCallback implements SurfaceHolder.Callback {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
- mSurface = mSurfaceView.getHolder().getSurface();
+ mTmpSurface = new Surface();
if (mVirtualDisplay == null) {
- initVirtualDisplay();
+ initVirtualDisplay(new SurfaceSession(surfaceHolder.getSurface()));
if (mVirtualDisplay != null && mActivityViewCallback != null) {
mActivityViewCallback.onActivityViewReady(ActivityView.this);
}
} else {
- mVirtualDisplay.setSurface(surfaceHolder.getSurface());
+ // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by
+ // whether it has a surface. Setting a fake surface here so DisplayManager will
+ // consider this display on.
+ mVirtualDisplay.setSurface(mTmpSurface);
+ mTmpTransaction.reparent(mRootSurfaceControl,
+ mSurfaceView.getSurfaceControl().getHandle()).apply();
}
updateLocation();
}
@@ -319,8 +332,8 @@ public class ActivityView extends ViewGroup {
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
- mSurface.release();
- mSurface = null;
+ mTmpSurface.release();
+ mTmpSurface = null;
if (mVirtualDisplay != null) {
mVirtualDisplay.setSurface(null);
}
@@ -328,7 +341,7 @@ public class ActivityView extends ViewGroup {
}
}
- private void initVirtualDisplay() {
+ private void initVirtualDisplay(SurfaceSession surfaceSession) {
if (mVirtualDisplay != null) {
throw new IllegalStateException("Trying to initialize for the second time.");
}
@@ -336,9 +349,13 @@ public class ActivityView extends ViewGroup {
final int width = mSurfaceView.getWidth();
final int height = mSurfaceView.getHeight();
final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+
+ // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by
+ // whether it has a surface. Setting a fake surface here so DisplayManager will consider
+ // this display on.
mVirtualDisplay = displayManager.createVirtualDisplay(
DISPLAY_NAME + "@" + System.identityHashCode(this),
- width, height, getBaseDisplayDensity(), mSurface,
+ width, height, getBaseDisplayDensity(), mTmpSurface,
DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
| DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
if (mVirtualDisplay == null) {
@@ -348,11 +365,20 @@ public class ActivityView extends ViewGroup {
final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+
+ mRootSurfaceControl = new SurfaceControl.Builder(surfaceSession)
+ .setContainerLayer(true)
+ .setName(DISPLAY_NAME)
+ .build();
+
try {
+ wm.reparentDisplayContent(displayId, mRootSurfaceControl.getHandle());
wm.dontOverrideDisplayInfo(displayId);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
+
+ mTmpTransaction.show(mRootSurfaceControl).apply();
mInputForwarder = InputManager.getInstance().createInputForwarder(displayId);
mTaskStackListener = new TaskStackListenerImpl();
try {
@@ -392,9 +418,9 @@ public class ActivityView extends ViewGroup {
displayReleased = false;
}
- if (mSurface != null) {
- mSurface.release();
- mSurface = null;
+ if (mTmpSurface != null) {
+ mTmpSurface.release();
+ mTmpSurface = null;
}
if (displayReleased && mActivityViewCallback != null) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 16d35802a9f6..0281e6a0631e 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3867,6 +3867,9 @@ public class Notification implements Parcelable
* The system UI may choose to display a heads-up notification, instead of
* launching this intent, while the user is using the device.
* </p>
+ * <p>Apps targeting {@link Build.VERSION_CODES#Q} and above will have to request
+ * a permission ({@link android.Manifest.permission#USE_FULL_SCREEN_INTENT}) in order to
+ * use full screen intents.</p>
*
* @param intent The pending intent to launch.
* @param highPriority Passing true will cause this notification to be sent
@@ -4468,6 +4471,14 @@ public class Notification implements Parcelable
}
}
+ private void bindAlertedIcon(RemoteViews contentView, StandardTemplateParams p) {
+ contentView.setDrawableTint(
+ R.id.alerted_icon,
+ false /* targetBackground */,
+ getNeutralColor(p),
+ PorterDuff.Mode.SRC_ATOP);
+ }
+
/**
* @hide
*/
@@ -4870,6 +4881,7 @@ public class Notification implements Parcelable
bindHeaderTextSecondary(contentView, p);
bindHeaderChronometerAndTime(contentView, p);
bindProfileBadge(contentView, p);
+ bindAlertedIcon(contentView, p);
}
bindActivePermissions(contentView, p);
bindExpandButton(contentView, p);
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 27471cac10e9..27ae0b0314d0 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1898,23 +1898,33 @@ public class WallpaperManager {
* @hide
*/
public static ComponentName getDefaultWallpaperComponent(Context context) {
+ ComponentName cn = null;
+
String flat = SystemProperties.get(PROP_WALLPAPER_COMPONENT);
if (!TextUtils.isEmpty(flat)) {
- final ComponentName cn = ComponentName.unflattenFromString(flat);
- if (cn != null) {
- return cn;
+ cn = ComponentName.unflattenFromString(flat);
+ }
+
+ if (cn == null) {
+ flat = context.getString(com.android.internal.R.string.default_wallpaper_component);
+ if (!TextUtils.isEmpty(flat)) {
+ cn = ComponentName.unflattenFromString(flat);
}
}
- flat = context.getString(com.android.internal.R.string.default_wallpaper_component);
- if (!TextUtils.isEmpty(flat)) {
- final ComponentName cn = ComponentName.unflattenFromString(flat);
- if (cn != null) {
- return cn;
+ // Check if the package exists
+ if (cn != null) {
+ try {
+ final PackageManager packageManager = context.getPackageManager();
+ packageManager.getPackageInfo(cn.getPackageName(),
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+ } catch (PackageManager.NameNotFoundException e) {
+ cn = null;
}
}
- return null;
+ return cn;
}
/**
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
index 237082e4cb66..2f0b44f76ffb 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
@@ -18,6 +18,7 @@ package android.bluetooth;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.EventLog;
/**
@@ -30,6 +31,8 @@ import android.os.Parcelable;
*/
public final class BluetoothHidDeviceAppSdpSettings implements Parcelable {
+ private static final int MAX_DESCRIPTOR_SIZE = 2048;
+
private final String mName;
private final String mDescription;
private final String mProvider;
@@ -55,6 +58,12 @@ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable {
mDescription = description;
mProvider = provider;
mSubclass = subclass;
+
+ if (descriptors == null || descriptors.length > MAX_DESCRIPTOR_SIZE) {
+ EventLog.writeEvent(0x534e4554, "119819889", -1, "");
+ throw new IllegalArgumentException("descriptors must be not null and shorter than "
+ + MAX_DESCRIPTOR_SIZE);
+ }
mDescriptors = descriptors.clone();
}
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index 11f8ab7551c2..e3672a7e064f 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -52,8 +52,7 @@ import java.util.List;
@RequiresFeature(PackageManager.FEATURE_BLUETOOTH)
public final class BluetoothManager {
private static final String TAG = "BluetoothManager";
- private static final boolean DBG = true;
- private static final boolean VDBG = true;
+ private static final boolean DBG = false;
private final BluetoothAdapter mAdapter;
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 437039dcbccf..7d5202d0dbce 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -97,8 +97,7 @@ public abstract class ContentResolver {
*
* @hide
*/
- public static final boolean DEPRECATE_DATA_COLUMNS = SystemProperties
- .getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false);
+ public static final boolean DEPRECATE_DATA_COLUMNS = StorageManager.hasIsolatedStorage();
/**
* Special filesystem path prefix which indicates that a path should be
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 6421dc5a3f68..b7df2bf5a5f7 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5372,6 +5372,10 @@ public abstract class PackageManager {
public abstract void removePackageFromPreferred(String packageName);
/**
+ * @deprecated This function no longer does anything; it was an old
+ * approach to managing preferred activities, which has been superseded
+ * by (and conflicts with) the modern activity-based preferences.
+ *
* Retrieve the list of all currently configured preferred packages. The
* first package on the list is the most preferred, the last is the least
* preferred.
@@ -5380,6 +5384,7 @@ public abstract class PackageManager {
* @return A List of PackageInfo objects, one for each preferred
* application, in order of preference.
*/
+ @Deprecated
public abstract List<PackageInfo> getPreferredPackages(@PackageInfoFlags int flags);
/**
@@ -5406,11 +5411,16 @@ public abstract class PackageManager {
ComponentName[] set, ComponentName activity);
/**
+ * @deprecated This is a protected API that should not have been available
+ * to third party applications. It is the platform's responsibility for
+ * assigning preferred activities and this cannot be directly modified.
+ *
* Same as {@link #addPreferredActivity(IntentFilter, int,
ComponentName[], ComponentName)}, but with a specific userId to apply the preference
to.
* @hide
*/
+ @Deprecated
@UnsupportedAppUsage
public void addPreferredActivityAsUser(IntentFilter filter, int match,
ComponentName[] set, ComponentName activity, @UserIdInt int userId) {
@@ -5444,6 +5454,10 @@ public abstract class PackageManager {
ComponentName[] set, ComponentName activity);
/**
+ * @deprecated This is a protected API that should not have been available
+ * to third party applications. It is the platform's responsibility for
+ * assigning preferred activities and this cannot be directly modified.
+ *
* Replaces an existing preferred activity mapping to the system, and if that were not present
* adds a new preferred activity. This will be used to automatically select the given activity
* component when {@link Context#startActivity(Intent) Context.startActivity()} finds multiple
@@ -5459,6 +5473,7 @@ public abstract class PackageManager {
*
* @hide
*/
+ @Deprecated
@SystemApi
public void replacePreferredActivity(@NonNull IntentFilter filter, int match,
@NonNull List<ComponentName> set, @NonNull ComponentName activity) {
@@ -5476,6 +5491,10 @@ public abstract class PackageManager {
}
/**
+ * @deprecated This function no longer does anything; it was an old
+ * approach to managing preferred activities, which has been superseded
+ * by (and conflicts with) the modern activity-based preferences.
+ *
* Remove all preferred activity mappings, previously added with
* {@link #addPreferredActivity}, from the
* system whose activities are implemented in the given package name.
@@ -5484,9 +5503,14 @@ public abstract class PackageManager {
* @param packageName The name of the package whose preferred activity
* mappings are to be removed.
*/
+ @Deprecated
public abstract void clearPackagePreferredActivities(String packageName);
/**
+ * @deprecated This function no longer does anything; it was an old
+ * approach to managing preferred activities, which has been superseded
+ * by (and conflicts with) the modern activity-based preferences.
+ *
* Retrieve all preferred activities, previously added with
* {@link #addPreferredActivity}, that are
* currently registered with the system.
@@ -5503,6 +5527,7 @@ public abstract class PackageManager {
* (the number of distinct IntentFilter records, not the number of unique
* activity components) that were found.
*/
+ @Deprecated
public abstract int getPreferredActivities(@NonNull List<IntentFilter> outFilters,
@NonNull List<ComponentName> outActivities, String packageName);
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 6f49cc42f6f6..b49c4476e82d 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -28,6 +28,7 @@ import android.content.pm.PackageManager.PackageInfoFlags;
import android.content.pm.PackageManager.ResolveInfoFlags;
import android.os.Bundle;
import android.os.PersistableBundle;
+import android.util.ArraySet;
import android.util.SparseArray;
import com.android.internal.util.function.TriFunction;
@@ -37,6 +38,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.function.BiFunction;
+import java.util.function.Consumer;
/**
* Package manager local system service interface.
@@ -735,4 +737,22 @@ public abstract class PackageManagerInternal {
/** Returns {@code true} if the given user requires extra badging for icons. */
public abstract boolean userNeedsBadging(int userId);
+
+ /**
+ * Perform the given action for each package.
+ * Note that packages lock will be held while performin the actions.
+ *
+ * @param actionLocked action to be performed
+ */
+ public abstract void forEachPackage(Consumer<PackageParser.Package> actionLocked);
+
+ /** Returns the list of enabled components */
+ public abstract ArraySet<String> getEnabledComponents(String packageName, int userId);
+
+ /** Returns the list of disabled components */
+ public abstract ArraySet<String> getDisabledComponents(String packageName, int userId);
+
+ /** Returns whether the given package is enabled for the given user */
+ public abstract @PackageManager.EnabledState int getApplicationEnabledState(
+ String packageName, int userId);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 49189e53f385..ac18dca74950 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2513,7 +2513,7 @@ public class PackageParser {
// If the storage model feature flag is disabled, we need to fiddle
// around with permission definitions to return us to pre-Q behavior.
// STOPSHIP(b/112545973): remove once feature enabled by default
- if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (!StorageManager.hasIsolatedStorage()) {
if ("android".equals(pkg.packageName)) {
final ArraySet<String> newGroups = new ArraySet<>();
newGroups.add(android.Manifest.permission_group.MEDIA_AURAL);
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index 79e15a7a9a2d..0ec812fe0350 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -30,19 +30,29 @@ import java.util.concurrent.Executor;
public interface BiometricAuthenticator {
/**
+ * No biometric methods or nothing has been enrolled.
+ * Move/expose these in BiometricPrompt if we ever want to allow applications to "blacklist"
+ * modalities when calling authenticate().
* @hide
*/
- int TYPE_FINGERPRINT = 1;
+ int TYPE_NONE = 0;
+ /**
+ * Constant representing fingerprint.
+ * @hide
+ */
+ int TYPE_FINGERPRINT = 1 << 0;
/**
+ * Constant representing iris.
* @hide
*/
- int TYPE_IRIS = 2;
+ int TYPE_IRIS = 1 << 1;
/**
+ * Constant representing face.
* @hide
*/
- int TYPE_FACE = 3;
+ int TYPE_FACE = 1 << 2;
/**
* Container for biometric data
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index bd149fd05f59..b238d778f55a 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -251,27 +251,11 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
private Executor mExecutor;
private AuthenticationCallback mAuthenticationCallback;
- IBiometricPromptReceiver mDialogReceiver = new IBiometricPromptReceiver.Stub() {
- @Override
- public void onDialogDismissed(int reason) {
- // Check the reason and invoke OnClickListener(s) if necessary
- if (reason == DISMISSED_REASON_POSITIVE) {
- mPositiveButtonInfo.executor.execute(() -> {
- mPositiveButtonInfo.listener.onClick(null, DialogInterface.BUTTON_POSITIVE);
- });
- } else if (reason == DISMISSED_REASON_NEGATIVE) {
- mNegativeButtonInfo.executor.execute(() -> {
- mNegativeButtonInfo.listener.onClick(null, DialogInterface.BUTTON_NEGATIVE);
- });
- }
- }
- };
-
- IBiometricServiceReceiver mBiometricServiceReceiver =
+ private final IBiometricServiceReceiver mBiometricServiceReceiver =
new IBiometricServiceReceiver.Stub() {
@Override
- public void onAuthenticationSucceeded(long deviceId) throws RemoteException {
+ public void onAuthenticationSucceeded() throws RemoteException {
mExecutor.execute(() -> {
final AuthenticationResult result = new AuthenticationResult(mCryptoObject);
mAuthenticationCallback.onAuthenticationSucceeded(result);
@@ -279,26 +263,39 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
}
@Override
- public void onAuthenticationFailed(long deviceId) throws RemoteException {
+ public void onAuthenticationFailed() throws RemoteException {
mExecutor.execute(() -> {
mAuthenticationCallback.onAuthenticationFailed();
});
}
@Override
- public void onError(long deviceId, int error, String message)
- throws RemoteException {
+ public void onError(int error, String message) throws RemoteException {
mExecutor.execute(() -> {
mAuthenticationCallback.onAuthenticationError(error, message);
});
}
@Override
- public void onAcquired(long deviceId, int acquireInfo, String message) {
+ public void onAcquired(int acquireInfo, String message) throws RemoteException {
mExecutor.execute(() -> {
mAuthenticationCallback.onAuthenticationHelp(acquireInfo, message);
});
}
+
+ @Override
+ public void onDialogDismissed(int reason) throws RemoteException {
+ // Check the reason and invoke OnClickListener(s) if necessary
+ if (reason == DISMISSED_REASON_POSITIVE) {
+ mPositiveButtonInfo.executor.execute(() -> {
+ mPositiveButtonInfo.listener.onClick(null, DialogInterface.BUTTON_POSITIVE);
+ });
+ } else if (reason == DISMISSED_REASON_NEGATIVE) {
+ mNegativeButtonInfo.executor.execute(() -> {
+ mNegativeButtonInfo.listener.onClick(null, DialogInterface.BUTTON_NEGATIVE);
+ });
+ }
+ }
};
private BiometricPrompt(Context context, Bundle bundle,
@@ -557,9 +554,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
mExecutor = executor;
mAuthenticationCallback = callback;
final long sessionId = crypto != null ? crypto.getOpId() : 0;
- mService.authenticate(mToken, sessionId, userId,
- mBiometricServiceReceiver, 0 /* flags */, mContext.getOpPackageName(),
- mBundle, mDialogReceiver);
+ mService.authenticate(mToken, sessionId, userId, mBiometricServiceReceiver,
+ mContext.getOpPackageName(), mBundle);
} catch (RemoteException e) {
Log.e(TAG, "Remote exception while authenticating", e);
mExecutor.execute(() -> {
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
deleted file mode 100644
index 27d25b86b859..000000000000
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2018 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.hardware.biometrics;
-
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
-}
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index e17feff0160a..53a076135aaa 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -18,7 +18,6 @@ package android.hardware.biometrics;
import android.os.Bundle;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
-import android.hardware.biometrics.IBiometricPromptReceiver;
import android.hardware.biometrics.IBiometricServiceReceiver;
/**
@@ -32,8 +31,7 @@ interface IBiometricService {
// Requests authentication. The service choose the appropriate biometric to use, and show
// the corresponding BiometricDialog.
void authenticate(IBinder token, long sessionId, int userId,
- IBiometricServiceReceiver receiver, int flags, String opPackageName,
- in Bundle bundle, IBiometricPromptReceiver dialogReceiver);
+ IBiometricServiceReceiver receiver, String opPackageName, in Bundle bundle);
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
@@ -46,4 +44,8 @@ interface IBiometricService {
// Explicitly set the active user.
void setActiveUser(int userId);
+
+ // Notify BiometricService when <Biometric>Service is ready to start the prepared client.
+ // Client lifecycle is still managed in <Biometric>Service.
+ void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
index a6e3696eeb10..22ef33e86e17 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
@@ -16,12 +16,18 @@
package android.hardware.biometrics;
/**
- * Communication channel from the BiometricService back to BiometricPrompt.
+ * Communication channel from BiometricService back to BiometricPrompt
* @hide
*/
oneway interface IBiometricServiceReceiver {
- void onAuthenticationSucceeded(long deviceId);
- void onAuthenticationFailed(long deviceId);
- void onError(long deviceId, int error, String message);
- void onAcquired(long deviceId, int acquiredInfo, String message);
+ // Notify BiometricPrompt that authentication was successful
+ void onAuthenticationSucceeded();
+ // Noties that authentication failed.
+ void onAuthenticationFailed();
+ // Notify BiometricPrompt that an error has occurred.
+ void onError(int error, String message);
+ // Notifies that a biometric has been acquired.
+ void onAcquired(int acquiredInfo, String message);
+ // Notifies that the SystemUI dialog has been dismissed.
+ void onDialogDismissed(int reason);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
new file mode 100644
index 000000000000..180daaf97ada
--- /dev/null
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 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.hardware.biometrics;
+
+/**
+ * Communication channel from
+ * 1) BiometricDialogImpl (SysUI) back to BiometricService
+ * 2) <Biometric>Service back to BiometricService
+ * Receives messages from the above and does some handling before forwarding to BiometricPrompt
+ * via IBiometricServiceReceiver.
+ * @hide
+ */
+oneway interface IBiometricServiceReceiverInternal {
+ // Notify BiometricService that authentication was successful. If user confirmation is required,
+ // the auth token must be submitted into KeyStore.
+ void onAuthenticationSucceeded(boolean requireConfirmation, in byte[] token);
+ // Notify BiometricService that an error has occurred.
+ void onAuthenticationFailed(int cookie, boolean requireConfirmation);
+ // Notify BiometricService than an error has occured. Forward to the correct receiver depending
+ // on the cookie.
+ void onError(int cookie, int error, String message);
+ // Notifies that a biometric has been acquired.
+ void onAcquired(int acquiredInfo, String message);
+ // Notifies that the SystemUI dialog has been dismissed.
+ void onDialogDismissed(int reason);
+ // Notifies that the user has pressed the "try again" button on SystemUI
+ void onTryAgainPressed();
+}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 47df8e8f9729..a15dcec3b276 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -15,9 +15,7 @@
*/
package android.hardware.face;
-import android.os.Bundle;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.face.IFaceServiceReceiver;
import android.hardware.face.Face;
@@ -32,19 +30,24 @@ interface IFaceService {
void authenticate(IBinder token, long sessionId,
IFaceServiceReceiver receiver, int flags, String opPackageName);
- // This method invokes the BiometricDialog. The arguments are almost the same as above,
- // but should only be called from (BiometricPromptService).
- void authenticateFromService(boolean requireConfirmation, IBinder token, long sessionId,
- int userId, IBiometricServiceReceiver receiver, int flags, String opPackageName,
- in Bundle bundle, IBiometricPromptReceiver dialogReceiver,
- int callingUid, int callingPid, int callingUserId);
+ // This method prepares the service to start authenticating, but doesn't start authentication.
+ // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
+ // called from BiometricService. The additional uid, pid, userId arguments should be determined
+ // by BiometricService. To start authentication after the clients are ready, use
+ // startPreparedClient().
+ void prepareForAuthentication(boolean requireConfirmation, IBinder token, long sessionId,
+ int userId, IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName,
+ int cookie, int callingUid, int callingPid, int callingUserId);
+
+ // Starts authentication with the previously prepared client.
+ void startPreparedClient(int cookie);
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
// Same as above, with extra arguments.
void cancelAuthenticationFromService(IBinder token, String opPackageName,
- int callingUid, int callingPid, int callingUserId);
+ int callingUid, int callingPid, int callingUserId, boolean fromClient);
// Start face enrollment
void enroll(IBinder token, in byte [] cryptoToken, int userId, IFaceServiceReceiver receiver,
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 2662a11c2dd4..dd6b29d87d67 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -15,9 +15,7 @@
*/
package android.hardware.fingerprint;
-import android.os.Bundle;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.fingerprint.IFingerprintClientActiveCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -35,22 +33,25 @@ interface IFingerprintService {
void authenticate(IBinder token, long sessionId, int userId,
IFingerprintServiceReceiver receiver, int flags, String opPackageName);
- // This method invokes the BiometricDialog. The arguments are almost the same as above, except
- // this is protected by the MANAGE_BIOMETRIC signature permission. This method should only be
- // called from BiometricPromptService. The additional uid, pid, userId arguments should be
- // determined by BiometricPromptService.
- void authenticateFromService(IBinder token, long sessionId, int userId,
- IBiometricServiceReceiver receiver, int flags, String opPackageName,
- in Bundle bundle, IBiometricPromptReceiver dialogReceiver,
+ // This method prepares the service to start authenticating, but doesn't start authentication.
+ // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
+ // called from BiometricService. The additional uid, pid, userId arguments should be determined
+ // by BiometricService. To start authentication after the clients are ready, use
+ // startPreparedClient().
+ void prepareForAuthentication(IBinder token, long sessionId, int userId,
+ IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName, int cookie,
int callingUid, int callingPid, int callingUserId);
+ // Starts authentication with the previously prepared client.
+ void startPreparedClient(int cookie);
+
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
// Same as above, except this is protected by the MANAGE_BIOMETRIC signature permission. Takes
// an additional uid, pid, userid.
void cancelAuthenticationFromService(IBinder token, String opPackageName,
- int callingUid, int callingPid, int callingUserId);
+ int callingUid, int callingPid, int callingUserId, boolean fromClient);
// Start fingerprint enrollment
void enroll(IBinder token, in byte [] cryptoToken, int groupId, IFingerprintServiceReceiver receiver,
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 1468fe5bfca8..64314a7d8060 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -928,7 +928,9 @@ public class Binder implements IBinder {
final long origWorkSource = ThreadLocalWorkSource.setUid(Binder.getCallingUid());
try {
if (tracingEnabled) {
- Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":" + code);
+ final String transactionName = getTransactionName(code);
+ Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":"
+ + (transactionName != null ? transactionName : code));
}
res = onTransact(code, data, reply, flags);
} catch (RemoteException|RuntimeException e) {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 92b316914d82..abfcfaacde21 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2463,6 +2463,27 @@ public class UserManager {
}
/**
+ * Get the parent of a user profile.
+ *
+ * @param user the handle of the user profile
+ *
+ * @return the parent of the user or {@code null} if the user is not profile
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public @Nullable UserHandle getProfileParent(@NonNull UserHandle user) {
+ UserInfo info = getProfileParent(user.getIdentifier());
+
+ if (info == null) {
+ return null;
+ }
+
+ return UserHandle.of(info.id);
+ }
+
+ /**
* Enables or disables quiet mode for a managed profile. If quiet mode is enabled, apps in a
* managed profile don't run, generate notifications, or consume data or battery.
* <p>
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index b42f1c4df4e3..8e11d858128a 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -25,6 +25,7 @@ import android.annotation.SdkConstant;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.annotation.WorkerThread;
import android.app.Activity;
@@ -1533,6 +1534,12 @@ public class StorageManager {
return SystemProperties.getBoolean(PROP_HAS_ADOPTABLE, false);
}
+ /** {@hide} */
+ @TestApi
+ public static boolean hasIsolatedStorage() {
+ return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false);
+ }
+
/**
* @deprecated disabled now that FUSE has been replaced by sdcardfs
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d7729037bfad..c297ef4f2a85 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5075,6 +5075,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @TestApi
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
public static void resetToDefaults(@NonNull ContentResolver resolver,
@Nullable String tag) {
@@ -8232,6 +8233,24 @@ public final class Settings {
public static final String NOTIFICATION_NEW_INTERRUPTION_MODEL = "new_interruption_model";
/**
+ * How often to check for location access.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS =
+ "location_access_check_interval_millis";
+
+ /**
+ * Delay between granting location access and checking it.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS =
+ "location_access_check_delay_millis";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
diff --git a/core/java/android/provider/TEST_MAPPING b/core/java/android/provider/TEST_MAPPING
new file mode 100644
index 000000000000..8e67ce7b2aa9
--- /dev/null
+++ b/core/java/android/provider/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.provider.SettingsBackupTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/java/android/service/oemlock/IOemLockService.aidl b/core/java/android/service/oemlock/IOemLockService.aidl
index d5e10d6629ca..99cffc5c5b8b 100644
--- a/core/java/android/service/oemlock/IOemLockService.aidl
+++ b/core/java/android/service/oemlock/IOemLockService.aidl
@@ -22,6 +22,8 @@ package android.service.oemlock;
* @hide
*/
interface IOemLockService {
+ String getLockName();
+
void setOemUnlockAllowedByCarrier(boolean allowed, in byte[] signature);
boolean isOemUnlockAllowedByCarrier();
diff --git a/core/java/android/service/oemlock/OemLockManager.java b/core/java/android/service/oemlock/OemLockManager.java
index f0d660354167..029d645dda19 100644
--- a/core/java/android/service/oemlock/OemLockManager.java
+++ b/core/java/android/service/oemlock/OemLockManager.java
@@ -44,6 +44,23 @@ public class OemLockManager {
}
/**
+ * Returns a vendor specific name for the OEM lock.
+ *
+ * This value is used to identify the security protocol used by locks.
+ *
+ * @return The name of the OEM lock or {@code null} if failed to get the name.
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE)
+ @Nullable
+ public String getLockName() {
+ try {
+ return mService.getLockName();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Sets whether the carrier has allowed this device to be OEM unlocked.
*
* Depending on the implementation, the validity of the request might need to be proved. This
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index dace388b66f6..c4be0e504c5b 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -304,7 +304,7 @@ interface IWindowManager
/**
* Get the position of the nav bar
*/
- int getNavBarPosition();
+ int getNavBarPosition(int displayId);
/**
* Lock the device immediately with the specified options (can be null).
@@ -551,4 +551,16 @@ interface IWindowManager
* @see KeyguardManager#isDeviceLocked()
*/
void setShouldShowIme(int displayId, boolean shouldShow);
+
+ /**
+ * Reparent the top layers for a display to the requested surfaceControl. The display that
+ * is going to be re-parented (the displayId passed in) needs to have been created by the same
+ * process that is requesting the re-parent. This is to ensure clients can't just re-parent
+ * display content info to any SurfaceControl, as this would be a security issue.
+ *
+ * @param displayId The id of the display.
+ * @param surfaceControlHandle The SurfaceControl handle that the top level layers for the
+ * display should be re-parented to.
+ */
+ void reparentDisplayContent(int displayId, in IBinder surfaceControlHandle);
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 46f396a0b66b..ab010855b896 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -104,6 +104,8 @@ public class SurfaceControl implements Parcelable {
int flags, int mask);
private static native void nativeSetWindowCrop(long transactionObj, long nativeObject,
int l, int t, int r, int b);
+ private static native void nativeSetCornerRadius(long transactionObj, long nativeObject,
+ float cornerRadius);
private static native void nativeSetLayerStack(long transactionObj, long nativeObject,
int layerStack);
@@ -1006,6 +1008,18 @@ public class SurfaceControl implements Parcelable {
}
}
+ /**
+ * Sets the corner radius of a {@link SurfaceControl}.
+ *
+ * @param cornerRadius Corner radius in pixels.
+ */
+ public void setCornerRadius(float cornerRadius) {
+ checkNotReleased();
+ synchronized (SurfaceControl.class) {
+ sGlobalTransaction.setCornerRadius(this, cornerRadius);
+ }
+ }
+
public void setLayerStack(int layerStack) {
checkNotReleased();
synchronized(SurfaceControl.class) {
@@ -1529,6 +1543,20 @@ public class SurfaceControl implements Parcelable {
return this;
}
+ /**
+ * Sets the corner radius of a {@link SurfaceControl}.
+ * @param sc SurfaceControl
+ * @param cornerRadius Corner radius in pixels.
+ * @return Itself.
+ */
+ @UnsupportedAppUsage
+ public Transaction setCornerRadius(SurfaceControl sc, float cornerRadius) {
+ sc.checkNotReleased();
+ nativeSetCornerRadius(mNativeObject, sc.mNativeObject, cornerRadius);
+
+ return this;
+ }
+
@UnsupportedAppUsage
public Transaction setLayerStack(SurfaceControl sc, int layerStack) {
sc.checkNotReleased();
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 3c4ce8f7cfad..797d1c5f47aa 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1126,6 +1126,13 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
}
};
+ /**
+ * @hide
+ */
+ public SurfaceControl getSurfaceControl() {
+ return mSurfaceControl;
+ }
+
class SurfaceControlWithBackground extends SurfaceControl {
SurfaceControl mBackgroundControl;
private boolean mOpaque = true;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 2767505aece3..c5d03741dbba 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4159,7 +4159,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
float mAlpha = 1f;
/**
- * The opacity of the view as manipulated by the Fade transition. This is a hidden
+ * The opacity of the view as manipulated by the Fade transition. This is a
* property only used by transitions, which is composited with the other alpha
* values to calculate the final visual alpha value.
*/
@@ -15729,15 +15729,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * This property is hidden and intended only for use by the Fade transition, which
- * animates it to produce a visual translucency that does not side-effect (or get
- * affected by) the real alpha property. This value is composited with the other
- * alpha value (and the AlphaAnimation value, when that is present) to produce
- * a final visual translucency result, which is what is passed into the DisplayList.
- *
- * @hide
+ * This property is intended only for use by the Fade transition, which animates it
+ * to produce a visual translucency that does not side-effect (or get affected by)
+ * the real alpha property. This value is composited with the other alpha value
+ * (and the AlphaAnimation value, when that is present) to produce a final visual
+ * translucency result, which is what is passed into the DisplayList.
*/
- @UnsupportedAppUsage
public void setTransitionAlpha(float alpha) {
ensureTransformationInfo();
if (mTransformationInfo.mTransitionAlpha != alpha) {
@@ -15760,16 +15757,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * This property is hidden and intended only for use by the Fade transition, which
- * animates it to produce a visual translucency that does not side-effect (or get
- * affected by) the real alpha property. This value is composited with the other
- * alpha value (and the AlphaAnimation value, when that is present) to produce
- * a final visual translucency result, which is what is passed into the DisplayList.
- *
- * @hide
+ * This property is intended only for use by the Fade transition, which animates
+ * it to produce a visual translucency that does not side-effect (or get affected
+ * by) the real alpha property. This value is composited with the other alpha
+ * value (and the AlphaAnimation value, when that is present) to produce a final
+ * visual translucency result, which is what is passed into the DisplayList.
*/
@ViewDebug.ExportedProperty(category = "drawing")
- @UnsupportedAppUsage
public float getTransitionAlpha() {
return mTransformationInfo != null ? mTransformationInfo.mTransitionAlpha : 1;
}
@@ -16288,9 +16282,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
- /** @hide */
- @UnsupportedAppUsage
- public void setAnimationMatrix(Matrix matrix) {
+ /**
+ * Changes the transformation matrix on the view. This is used in animation frameworks,
+ * such as {@link android.transition.Transition}. When the animation finishes, the matrix
+ * should be cleared by calling this method with <code>null</code> as the matrix parameter.
+ * Application developers should use transformation methods like {@link #setRotation(float)},
+ * {@link #setScaleX(float)}, {@link #setScaleX(float)}, {@link #setTranslationX(float)}}
+ * and {@link #setTranslationY(float)} (float)}} instead.
+ *
+ * @param matrix The matrix, null indicates that the matrix should be cleared.
+ */
+ public void setAnimationMatrix(@Nullable Matrix matrix) {
invalidateViewProperty(true, false);
mRenderNode.setAnimationMatrix(matrix);
invalidateViewProperty(false, true);
@@ -21460,7 +21462,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* previous ones
* {@hide}
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
@@ -21537,7 +21539,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @see #setLeft(int), #setRight(int), #setTop(int), #setBottom(int)
*/
- public void setLeftTopRightBottom(int left, int top, int right, int bottom) {
+ public final void setLeftTopRightBottom(int left, int top, int right, int bottom) {
setFrame(left, top, right, bottom);
}
@@ -23545,6 +23547,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Get the identifier used for this view by the drawing system.
+ *
+ * @see RenderNode#getUniqueId()
+ * @return A long that uniquely identifies this view's drawing component
+ */
+ public long getUniqueDrawingId() {
+ return mRenderNode.getUniqueId();
+ }
+
+ /**
* Returns this view's tag.
*
* @return the Object stored in this view as a tag, or {@code null} if not
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 1e91aa87bfb7..741510e67130 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -7042,10 +7042,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* suppression is disabled with a later call to suppressLayout(false).
* When layout suppression is disabled, a requestLayout() call is sent
* if layout() was attempted while layout was being suppressed.
- *
- * @hide
*/
- @UnsupportedAppUsage
public void suppressLayout(boolean suppress) {
mSuppressLayout = suppress;
if (!suppress) {
@@ -7061,8 +7058,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* suppressed, due to an earlier call to {@link #suppressLayout(boolean)}.
*
* @return true if layout calls are currently suppressed, false otherwise.
- *
- * @hide
*/
public boolean isLayoutSuppressed() {
return mSuppressLayout;
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index 260e93890e3e..16bafe2c4f11 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -44,6 +44,7 @@ public interface WindowManagerPolicyConstants {
int PRESENCE_EXTERNAL = 1 << 1;
// Navigation bar position values
+ int NAV_BAR_INVALID = -1;
int NAV_BAR_LEFT = 1 << 0;
int NAV_BAR_RIGHT = 1 << 1;
int NAV_BAR_BOTTOM = 1 << 2;
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 9ccd3211768d..f78180276373 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -23,6 +23,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Rect;
+import android.os.Build;
import android.os.Bundle;
import android.os.Trace;
import android.util.AttributeSet;
@@ -107,7 +108,7 @@ public class GridView extends AbsListView {
*/
public static final int AUTO_FIT = -1;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117521080)
private int mNumColumns = AUTO_FIT;
@UnsupportedAppUsage
@@ -117,7 +118,7 @@ public class GridView extends AbsListView {
@UnsupportedAppUsage
private int mVerticalSpacing = 0;
private int mStretchMode = STRETCH_COLUMN_WIDTH;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117521079)
private int mColumnWidth;
@UnsupportedAppUsage
private int mRequestedColumnWidth;
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 34e8ed406200..875d7c9ee7a6 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -52,17 +52,23 @@ public class BinderCallsStats implements BinderInternal.Observer {
public static final boolean ENABLED_DEFAULT = false;
public static final boolean DETAILED_TRACKING_DEFAULT = true;
public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 100;
+ public static final int MAX_BINDER_CALL_STATS_COUNT_DEFAULT = 5000;
+
+ private static class OverflowBinder extends Binder {}
private static final String TAG = "BinderCallsStats";
private static final int CALL_SESSIONS_POOL_SIZE = 100;
private static final int MAX_EXCEPTION_COUNT_SIZE = 50;
private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow";
+ private static final Class<? extends Binder> OVERFLOW_BINDER = OverflowBinder.class;
+ private static final int OVERFLOW_TRANSACTION_CODE = -1;
// Whether to collect all the data: cpu + exceptions + reply/request sizes.
private boolean mDetailedTracking = DETAILED_TRACKING_DEFAULT;
// Sampling period to control how often to track CPU usage. 1 means all calls, 100 means ~1 out
// of 100 requests.
private int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT;
+ private int mMaxBinderCallStatsCount = MAX_BINDER_CALL_STATS_COUNT_DEFAULT;
@GuardedBy("mLock")
private final SparseArray<UidEntry> mUidEntries = new SparseArray<>();
@GuardedBy("mLock")
@@ -71,6 +77,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
private final Object mLock = new Object();
private final Random mRandom;
private long mStartTime = System.currentTimeMillis();
+ private long mCallStatsCount = 0;
private CachedDeviceState.Readonly mDeviceState;
@@ -158,7 +165,13 @@ public class BinderCallsStats implements BinderInternal.Observer {
final CallStat callStat = uidEntry.getOrCreate(
callingUid, s.binderClass, s.transactionCode,
- mDeviceState.isScreenInteractive());
+ mDeviceState.isScreenInteractive(),
+ mCallStatsCount >= mMaxBinderCallStatsCount);
+ final boolean isNewCallStat = callStat.callCount == 0;
+ if (isNewCallStat) {
+ mCallStatsCount++;
+ }
+
callStat.callCount++;
callStat.recordedCallCount++;
callStat.cpuTimeMicros += duration;
@@ -444,6 +457,24 @@ public class BinderCallsStats implements BinderInternal.Observer {
}
}
+ /**
+ * Sets the maximum number of items to track.
+ */
+ public void setMaxBinderCallStats(int maxKeys) {
+ if (maxKeys <= 0) {
+ Slog.w(TAG, "Ignored invalid max value (value must be positive): "
+ + maxKeys);
+ return;
+ }
+
+ synchronized (mLock) {
+ if (maxKeys != mMaxBinderCallStatsCount) {
+ mMaxBinderCallStatsCount = maxKeys;
+ reset();
+ }
+ }
+ }
+
public void setSamplingInterval(int samplingInterval) {
if (samplingInterval <= 0) {
Slog.w(TAG, "Ignored invalid sampling interval (value must be positive): "
@@ -461,6 +492,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
public void reset() {
synchronized (mLock) {
+ mCallStatsCount = 0;
mUidEntries.clear();
mExceptionCounts.clear();
mStartTime = System.currentTimeMillis();
@@ -595,10 +627,21 @@ public class BinderCallsStats implements BinderInternal.Observer {
}
CallStat getOrCreate(int callingUid, Class<? extends Binder> binderClass,
- int transactionCode, boolean screenInteractive) {
+ int transactionCode, boolean screenInteractive, boolean maxCallStatsReached) {
CallStat mapCallStat = get(callingUid, binderClass, transactionCode, screenInteractive);
- // Only create CallStat if it's a new entry, otherwise update existing instance
+ // Only create CallStat if it's a new entry, otherwise update existing instance.
if (mapCallStat == null) {
+ if (maxCallStatsReached) {
+ mapCallStat = get(callingUid, OVERFLOW_BINDER, OVERFLOW_TRANSACTION_CODE,
+ screenInteractive);
+ if (mapCallStat != null) {
+ return mapCallStat;
+ }
+
+ binderClass = OVERFLOW_BINDER;
+ transactionCode = OVERFLOW_TRANSACTION_CODE;
+ }
+
mapCallStat = new CallStat(callingUid, binderClass, transactionCode,
screenInteractive);
CallStatKey key = new CallStatKey();
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 031377ebe27b..488b9912ee49 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -41,6 +41,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Color;
diff --git a/core/java/com/android/internal/policy/ScreenDecorationsUtils.java b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
new file mode 100644
index 000000000000..100c6ee6763b
--- /dev/null
+++ b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 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.internal.policy;
+
+import com.android.internal.R;
+
+import android.content.res.Resources;
+
+/**
+ * Utility functions for screen decorations used by both window manager and System UI.
+ */
+public class ScreenDecorationsUtils {
+
+ /**
+ * Corner radius that should be used on windows in order to cover the display.
+ * These values are expressed in pixels because they should not respect display or font
+ * scaling, this means that we don't have to reload them on config changes.
+ */
+ public static float getWindowCornerRadius(Resources resources) {
+ // Radius that should be used in case top or bottom aren't defined.
+ float defaultRadius = resources.getDimension(R.dimen.rounded_corner_radius);
+
+ float topRadius = resources.getDimension(R.dimen.rounded_corner_radius_top);
+ if (topRadius == 0) {
+ topRadius = defaultRadius;
+ }
+ float bottomRadius = resources.getDimension(R.dimen.rounded_corner_radius_bottom);
+ if (bottomRadius == 0) {
+ bottomRadius = defaultRadius;
+ }
+
+ // Always use the smallest radius to make sure the rounded corners will
+ // completely cover the display.
+ return Math.min(topRadius, bottomRadius);
+ }
+}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 604537ffee03..600b1b324257 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -18,7 +18,7 @@ package com.android.internal.statusbar;
import android.content.ComponentName;
import android.graphics.Rect;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
@@ -141,7 +141,7 @@ oneway interface IStatusBar
void showShutdownUi(boolean isReboot, String reason);
// Used to show the dialog when BiometricService starts authentication
- void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver, int type,
+ void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
boolean requireConfirmation, int userId);
// Used to hide the dialog when a biometric is authenticated
void onBiometricAuthenticated();
@@ -151,4 +151,6 @@ oneway interface IStatusBar
void onBiometricError(String error);
// Used to hide the biometric dialog when the AuthenticationClient is stopped
void hideBiometricDialog();
+ // Used to request the "try again" button for authentications which requireConfirmation=true
+ void showBiometricTryAgain();
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index b7ffb5768a31..bf82dc610ad4 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -21,7 +21,7 @@ import android.content.ComponentName;
import android.graphics.Rect;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.StatusBarIcon;
@@ -92,7 +92,7 @@ interface IStatusBarService
void showPinningEscapeToast();
// Used to show the dialog when BiometricService starts authentication
- void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver, int type,
+ void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
boolean requireConfirmation, int userId);
// Used to hide the dialog when a biometric is authenticated
void onBiometricAuthenticated();
@@ -102,4 +102,6 @@ interface IStatusBarService
void onBiometricError(String error);
// Used to hide the biometric dialog when the AuthenticationClient is stopped
void hideBiometricDialog();
+ // Used to request the "try again" button for authentications which requireConfirmation=true
+ void showBiometricTryAgain();
}
diff --git a/core/java/com/android/internal/util/BitUtils.java b/core/java/com/android/internal/util/BitUtils.java
index 17d5a2e36974..61581458f98a 100644
--- a/core/java/com/android/internal/util/BitUtils.java
+++ b/core/java/com/android/internal/util/BitUtils.java
@@ -28,7 +28,7 @@ import java.util.function.IntFunction;
/**
* A utility class for handling unsigned integers and unsigned arithmetics, as well as syntactic
- * sugar methods for ByteBuffer. Useful for networking and packet manipulations.
+ * sugar methods for {@link ByteBuffer}. Useful for networking and packet manipulations.
* {@hide}
*/
public final class BitUtils {
@@ -151,4 +151,11 @@ public final class BitUtils {
TextUtils.wrap(builder, "[", "]");
return builder.toString();
}
+
+ /**
+ * Converts long to byte array
+ */
+ public static byte[] toBytes(long l) {
+ return ByteBuffer.allocate(8).putLong(l).array();
+ }
}
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 083c0c9736f9..151901be7b5b 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -190,6 +191,13 @@ public class CollectionUtils {
}
/**
+ * Returns the size of the given map, or 0 if null
+ */
+ public static int size(@Nullable Map<?, ?> cur) {
+ return cur != null ? cur.size() : 0;
+ }
+
+ /**
* Returns whether the given collection {@link Collection#isEmpty is empty} or {@code null}
*/
public static boolean isEmpty(@Nullable Collection<?> cur) {
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 849585004bc3..b97a9fa8d1cc 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -934,7 +934,7 @@ public class SystemConfig {
// If the storage model feature flag is disabled, we need to fiddle
// around with permission definitions to return us to pre-Q behavior.
// STOPSHIP(b/112545973): remove once feature enabled by default
- if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (!StorageManager.hasIsolatedStorage()) {
if (newPermissions.contains(android.Manifest.permission.READ_MEDIA_AUDIO) ||
newPermissions.contains(android.Manifest.permission.READ_MEDIA_VIDEO) ||
newPermissions.contains(android.Manifest.permission.READ_MEDIA_IMAGES)) {
diff --git a/core/jni/android_text_AndroidCharacter.cpp b/core/jni/android_text_AndroidCharacter.cpp
index 8885aac49064..c6ea4e10f63e 100644
--- a/core/jni/android_text_AndroidCharacter.cpp
+++ b/core/jni/android_text_AndroidCharacter.cpp
@@ -25,9 +25,10 @@
#include "unicode/uchar.h"
#define PROPERTY_UNDEFINED (-1)
+#define JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY 18
// ICU => JDK mapping
-static int directionality_map[U_CHAR_DIRECTION_COUNT] = {
+static int directionality_map[JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY + 1] = {
0, // U_LEFT_TO_RIGHT (0) => DIRECTIONALITY_LEFT_TO_RIGHT (0)
1, // U_RIGHT_TO_LEFT (1) => DIRECTIONALITY_RIGHT_TO_LEFT (1)
3, // U_EUROPEAN_NUMBER (2) => DIRECTIONALITY_EUROPEAN_NUMBER (3)
@@ -75,7 +76,8 @@ static void getDirectionalities(JNIEnv* env, jobject obj, jcharArray srcArray,
int c = 0x00010000 + ((src[i] - 0xD800) << 10) +
(src[i + 1] & 0x3FF);
int dir = u_charDirection(c);
- if (dir < 0 || dir >= U_CHAR_DIRECTION_COUNT)
+ if (dir < 0 || dir > JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY
+ || u_charType(c) == U_UNASSIGNED)
dir = PROPERTY_UNDEFINED;
else
dir = directionality_map[dir];
@@ -85,7 +87,8 @@ static void getDirectionalities(JNIEnv* env, jobject obj, jcharArray srcArray,
} else {
int c = src[i];
int dir = u_charDirection(c);
- if (dir < 0 || dir >= U_CHAR_DIRECTION_COUNT)
+ if (dir < 0 || dir > JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY
+ || u_charType(c) == U_UNASSIGNED)
dest[i] = PROPERTY_UNDEFINED;
else
dest[i] = directionality_map[dir];
@@ -96,7 +99,7 @@ static void getDirectionalities(JNIEnv* env, jobject obj, jcharArray srcArray,
static jint getEastAsianWidth(JNIEnv* env, jobject obj, jchar input)
{
int width = u_getIntPropertyValue(input, UCHAR_EAST_ASIAN_WIDTH);
- if (width < 0 || width >= U_EA_COUNT)
+ if (width < 0 || width > u_getIntPropertyMaxValue(UCHAR_EAST_ASIAN_WIDTH))
width = PROPERTY_UNDEFINED;
return width;
@@ -121,6 +124,7 @@ static void getEastAsianWidths(JNIEnv* env, jobject obj, jcharArray srcArray,
return;
}
+ int maxWidth = u_getIntPropertyMaxValue(UCHAR_EAST_ASIAN_WIDTH);
for (int i = 0; i < count; i++) {
const int srci = start + i;
if (src[srci] >= 0xD800 && src[srci] <= 0xDBFF &&
@@ -129,7 +133,7 @@ static void getEastAsianWidths(JNIEnv* env, jobject obj, jcharArray srcArray,
int c = 0x00010000 + ((src[srci] - 0xD800) << 10) +
(src[srci + 1] & 0x3FF);
int width = u_getIntPropertyValue(c, UCHAR_EAST_ASIAN_WIDTH);
- if (width < 0 || width >= U_EA_COUNT)
+ if (width < 0 || width > maxWidth)
width = PROPERTY_UNDEFINED;
dest[i++] = width;
@@ -137,7 +141,7 @@ static void getEastAsianWidths(JNIEnv* env, jobject obj, jcharArray srcArray,
} else {
int c = src[srci];
int width = u_getIntPropertyValue(c, UCHAR_EAST_ASIAN_WIDTH);
- if (width < 0 || width >= U_EA_COUNT)
+ if (width < 0 || width > maxWidth)
width = PROPERTY_UNDEFINED;
dest[i] = width;
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index ec9c8606e8df..ea6e0178bd9c 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -377,6 +377,14 @@ static void nativeSetWindowCrop(JNIEnv* env, jclass clazz, jlong transactionObj,
transaction->setCrop_legacy(ctrl, crop);
}
+static void nativeSetCornerRadius(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jlong nativeObject, jfloat cornerRadius) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+ transaction->setCornerRadius(ctrl, cornerRadius);
+}
+
static void nativeSetLayerStack(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jint layerStack) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -883,6 +891,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetFlags },
{"nativeSetWindowCrop", "(JJIIII)V",
(void*)nativeSetWindowCrop },
+ {"nativeSetCornerRadius", "(JJF)V",
+ (void*)nativeSetCornerRadius },
{"nativeSetLayerStack", "(JJI)V",
(void*)nativeSetLayerStack },
{"nativeGetBuiltInDisplay", "(I)Landroid/os/IBinder;",
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 6e661e1ce5b2..b465fb4f7eba 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -234,6 +234,14 @@ message SecureSettingsProto {
}
optional Location location = 31;
+ message LocationAccessCheck {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ optional SettingProto interval_millis = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto delay_millis = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional LocationAccessCheck location_access_check = 73;
+
message LockScreen {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -515,5 +523,5 @@ message SecureSettingsProto {
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 73;
+ // Next tag = 74;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7ca8f24bc8c7..b4d5f67e945e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -627,6 +627,13 @@
<!-- ====================================================================== -->
<eat-comment />
+ <!-- Grouping for platform runtime permissions is not accessible to apps
+ @hide
+ @SystemApi
+ -->
+ <permission-group android:name="android.permission-group.UNDEFINED"
+ android:priority="100" />
+
<!-- ====================================================================== -->
<!-- Permissions for accessing user's contacts including personal profile -->
<!-- ====================================================================== -->
@@ -645,6 +652,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_CONTACTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readContacts"
android:description="@string/permdesc_readContacts"
android:protectionLevel="dangerous"
@@ -654,6 +662,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_CONTACTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_writeContacts"
android:description="@string/permdesc_writeContacts"
android:protectionLevel="dangerous" />
@@ -675,6 +684,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_CALENDAR"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readCalendar"
android:description="@string/permdesc_readCalendar"
android:protectionLevel="dangerous"
@@ -684,6 +694,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_CALENDAR"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_writeCalendar"
android:description="@string/permdesc_writeCalendar"
android:protectionLevel="dangerous" />
@@ -705,6 +716,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.SEND_SMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_sendSms"
android:description="@string/permdesc_sendSms"
android:permissionFlags="costsMoney"
@@ -714,6 +726,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECEIVE_SMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_receiveSms"
android:description="@string/permdesc_receiveSms"
android:protectionLevel="dangerous"
@@ -723,6 +736,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_SMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readSms"
android:description="@string/permdesc_readSms"
android:protectionLevel="dangerous"
@@ -732,6 +746,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECEIVE_WAP_PUSH"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_receiveWapPush"
android:description="@string/permdesc_receiveWapPush"
android:protectionLevel="dangerous"
@@ -741,6 +756,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECEIVE_MMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_receiveMms"
android:description="@string/permdesc_receiveMms"
android:protectionLevel="dangerous"
@@ -759,6 +775,7 @@
<p>Protection level: dangerous
@hide Pending API council approval -->
<permission android:name="android.permission.READ_CELL_BROADCASTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readCellBroadcasts"
android:description="@string/permdesc_readCellBroadcasts"
android:protectionLevel="dangerous"
@@ -801,6 +818,7 @@
@deprecated replaced by new strongly-typed permission groups in Q.
-->
<permission android:name="android.permission.READ_EXTERNAL_STORAGE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_sdcardRead"
android:description="@string/permdesc_sdcardRead"
android:protectionLevel="dangerous"
@@ -822,6 +840,7 @@
@deprecated replaced by new strongly-typed permission groups in Q.
-->
<permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_sdcardWrite"
android:description="@string/permdesc_sdcardWrite"
android:protectionLevel="dangerous"
@@ -838,6 +857,7 @@
<!-- Allows an application to read the user's shared audio collection. -->
<permission android:name="android.permission.READ_MEDIA_AUDIO"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_audioRead"
android:description="@string/permdesc_audioRead"
android:protectionLevel="dangerous"
@@ -854,6 +874,7 @@
<!-- Allows an application to read the user's shared images collection. -->
<permission android:name="android.permission.READ_MEDIA_IMAGES"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_imagesRead"
android:description="@string/permdesc_imagesRead"
android:protectionLevel="dangerous"
@@ -861,6 +882,7 @@
<!-- Allows an application to read the user's shared video collection. -->
<permission android:name="android.permission.READ_MEDIA_VIDEO"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_videoRead"
android:description="@string/permdesc_videoRead"
android:protectionLevel="dangerous"
@@ -869,6 +891,7 @@
<!-- Allows an application to access any geographic locations persisted in the
user's shared collection. -->
<permission android:name="android.permission.ACCESS_MEDIA_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_mediaLocation"
android:description="@string/permdesc_mediaLocation"
android:protectionLevel="dangerous"
@@ -900,6 +923,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_FINE_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_accessFineLocation"
android:description="@string/permdesc_accessFineLocation"
android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
@@ -911,6 +935,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_COARSE_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_accessCoarseLocation"
android:description="@string/permdesc_accessCoarseLocation"
android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
@@ -924,6 +949,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_accessBackgroundLocation"
android:description="@string/permdesc_accessBackgroundLocation"
android:protectionLevel="dangerous|instant"
@@ -965,6 +991,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_CALL_LOG"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readCallLog"
android:description="@string/permdesc_readCallLog"
android:protectionLevel="dangerous"
@@ -984,6 +1011,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_CALL_LOG"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_writeCallLog"
android:description="@string/permdesc_writeCallLog"
android:protectionLevel="dangerous" />
@@ -994,6 +1022,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.PROCESS_OUTGOING_CALLS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_processOutgoingCalls"
android:description="@string/permdesc_processOutgoingCalls"
android:protectionLevel="dangerous"
@@ -1026,6 +1055,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_PHONE_STATE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readPhoneState"
android:description="@string/permdesc_readPhoneState"
android:protectionLevel="dangerous"
@@ -1035,6 +1065,7 @@
granted by {@link #READ_PHONE_STATE} but is exposed to instant applications.
<p>Protection level: dangerous-->
<permission android:name="android.permission.READ_PHONE_NUMBERS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readPhoneNumbers"
android:description="@string/permdesc_readPhoneNumbers"
android:protectionLevel="dangerous|instant"
@@ -1045,6 +1076,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.CALL_PHONE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:permissionFlags="costsMoney"
android:label="@string/permlab_callPhone"
android:description="@string/permdesc_callPhone"
@@ -1054,6 +1086,7 @@
<p>Protection level: dangerous
-->
<permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_addVoicemail"
android:description="@string/permdesc_addVoicemail"
android:protectionLevel="dangerous" />
@@ -1062,6 +1095,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.USE_SIP"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:description="@string/permdesc_use_sip"
android:label="@string/permlab_use_sip"
android:protectionLevel="dangerous"/>
@@ -1070,6 +1104,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ANSWER_PHONE_CALLS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_answerPhoneCalls"
android:description="@string/permdesc_answerPhoneCalls"
android:protectionLevel="dangerous|runtime" />
@@ -1097,6 +1132,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCEPT_HANDOVER"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android.label="@string/permlab_acceptHandover"
android:description="@string/permdesc_acceptHandovers"
android:protectionLevel="dangerous" />
@@ -1120,6 +1156,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECORD_AUDIO"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_recordAudio"
android:description="@string/permdesc_recordAudio"
android:protectionLevel="dangerous|instant"
@@ -1142,6 +1179,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACTIVITY_RECOGNITION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_activityRecognition"
android:description="@string/permdesc_activityRecognition"
android:protectionLevel="dangerous|instant"
@@ -1191,6 +1229,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.CAMERA"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_camera"
android:description="@string/permdesc_camera"
android:protectionLevel="dangerous|instant"
@@ -1215,6 +1254,7 @@
measure what is happening inside his/her body, such as heart rate.
<p>Protection level: dangerous -->
<permission android:name="android.permission.BODY_SENSORS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_bodySensors"
android:description="@string/permdesc_bodySensors"
android:protectionLevel="dangerous"
@@ -1702,6 +1742,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.GET_ACCOUNTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:protectionLevel="dangerous"
android:description="@string/permdesc_getAccounts"
android:label="@string/permlab_getAccounts"
@@ -4229,6 +4270,12 @@
<permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS"
android:protectionLevel="signature|appop" />
+ <!-- Required for apps targeting {@link android.os.Build.VERSION_CODES#P} that want to use
+ {@link android.app.Notification.Builder#setFullScreenIntent notification full screen
+ intents}. -->
+ <permission android:name="android.permission.USE_FULL_SCREEN_INTENT"
+ android:protectionLevel="normal" />
+
<!-- @SystemApi Allows requesting the framework broadcast the
{@link Intent#ACTION_DEVICE_CUSTOMIZATION_READY} intent.
@hide -->
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 433ae399c88c..4bf1ad6651ee 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -126,7 +126,6 @@
android:visibility="gone"
android:contentDescription="@string/notification_alerted_content_description"
android:src="@drawable/ic_notifications_alerted"
- android:tint="@color/notification_secondary_text_color_light"
/>
<ImageView
android:id="@+id/profile_badge"
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index f7b9961c39e8..05a156b69371 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -430,7 +430,7 @@
<dimen name="notification_badge_size">12dp</dimen>
<!-- Size of the alerted icon for notifications -->
- <dimen name="notification_alerted_size">18dp</dimen>
+ <dimen name="notification_alerted_size">12dp</dimen>
<!-- Keyguard dimensions -->
<!-- TEMP -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index bd6d97622a4d..a33f6b2fbf9c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1444,9 +1444,14 @@
<!-- Title shown when the system-provided biometric dialog is shown, asking the user to authenticate. [CHAR LIMIT=40] -->
<string name="biometric_dialog_default_title">Application <xliff:g id="app" example="Gmail">%s</xliff:g> wants to authenticate.</string>
-
<!-- Message shown when biometric hardware is not available [CHAR LIMIT=50] -->
<string name="biometric_error_hw_unavailable">Biometric hardware unavailable</string>
+ <!-- Message shown when biometric authentication was canceled by the user [CHAR LIMIT=50] -->
+ <string name="biometric_error_user_canceled">Authentication canceled</string>
+ <!-- Message shown by the biometric dialog when biometric is not recognized -->
+ <string name="biometric_not_recognized">Not recognized</string>
+ <!-- Message shown when biometric authentication has been canceled [CHAR LIMIT=50] -->
+ <string name="biometric_error_canceled">Authentication canceled</string>
<!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized -->
<string name="fingerprint_acquired_partial">Partial fingerprint detected. Please try again.</string>
@@ -1462,8 +1467,6 @@
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <!-- Message shown by the biometric dialog when biometric is not recognized -->
- <string name="biometric_not_recognized">Not recognized</string>
<!-- Accessibility message announced when a fingerprint has been authenticated [CHAR LIMIT=NONE] -->
<string name="fingerprint_authenticated">Fingerprint authenticated</string>
<!-- Accessibility message announced when a face has been authenticated [CHAR LIMIT=NONE] -->
@@ -1585,14 +1588,14 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_readSyncStats">Allows an app to read the sync stats for an account, including the history of sync events and how much data is synced. </string>
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
<string name="permlab_sdcardRead">read the contents of your shared storage</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
<string name="permdesc_sdcardRead">Allows the app to read the contents of your shared storage.</string>
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] -->
<string name="permlab_sdcardWrite">modify or delete the contents of your shared storage</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] -->
<string name="permdesc_sdcardWrite">Allows the app to write the contents of your shared storage.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -2983,7 +2986,7 @@
<!-- Label for item in the text selection menu to translate selected text with a translation app. Should be a verb. [CHAR LIMIT=30] -->
<string name="translate">Translate</string>
-
+
<!-- Accessibility description for an item in the text selection menu to translate selected text with a translation app. [CHAR LIMIT=NONE] -->
<string name="translate_desc">Translate selected text</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e251e27a5496..783f1f355e87 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2406,7 +2406,9 @@
<!-- Biometric messages -->
<java-symbol type="string" name="biometric_dialog_default_title" />
<java-symbol type="string" name="biometric_error_hw_unavailable" />
+ <java-symbol type="string" name="biometric_error_user_canceled" />
<java-symbol type="string" name="biometric_not_recognized" />
+ <java-symbol type="string" name="biometric_error_canceled" />
<!-- Fingerprint messages -->
<java-symbol type="string" name="fingerprint_error_unable_to_process" />
@@ -3499,4 +3501,8 @@
<java-symbol type="string" name="config_defaultAssistantComponentName" />
<java-symbol type="id" name="transition_overlay_view_tag" />
+
+ <java-symbol type="dimen" name="rounded_corner_radius" />
+ <java-symbol type="dimen" name="rounded_corner_radius_top" />
+ <java-symbol type="dimen" name="rounded_corner_radius_bottom" />
</resources>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 305d2af2122d..a8f9e8a5891b 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -664,7 +664,9 @@ public class SettingsBackupTest {
Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE,
Settings.Secure.FLASHLIGHT_AVAILABLE,
Settings.Secure.FLASHLIGHT_ENABLED,
- Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED);
+ Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED,
+ Settings.Secure.LOCATION_ACCESS_CHECK_INTERVAL_MILLIS,
+ Settings.Secure.LOCATION_ACCESS_CHECK_DELAY_MILLIS);
@Test
public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/core/tests/coretests/src/android/text/AndroidCharacterTest.java b/core/tests/coretests/src/android/text/AndroidCharacterTest.java
new file mode 100644
index 000000000000..0c7e730e78e4
--- /dev/null
+++ b/core/tests/coretests/src/android/text/AndroidCharacterTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 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,d
+ * 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.text;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@Presubmit
+@SmallTest
+public class AndroidCharacterTest {
+
+ @Test
+ public void testGetDirectionalities_nonSupplementaryCharacters() {
+ int size = Character.MAX_VALUE + 1
+ - (Character.MAX_SURROGATE - Character.MIN_SURROGATE + 1);
+ char[] chars = new char[size];
+ byte[] java_lang_results = new byte[size];
+ int index = 0;
+ for (int cp = 0; cp <= Character.MAX_VALUE; cp++) {
+ if (cp < Character.MIN_SURROGATE || cp > Character.MAX_SURROGATE) {
+ chars[index] = (char) cp;
+ java_lang_results[index] = Character.getDirectionality(cp);
+ index++;
+ }
+ }
+
+ byte[] android_text_results = new byte[size];
+ AndroidCharacter.getDirectionalities(chars, android_text_results, index);
+ assertArrayEquals(java_lang_results, android_text_results);
+ }
+
+ @Test
+ public void testGetDirectionalities_supplementaryCharacters() {
+ int maxNumberOfChars = Character.MAX_CODE_POINT - Character.MIN_SUPPLEMENTARY_CODE_POINT
+ + 1;
+ int size = maxNumberOfChars * 2;
+ char[] chars = new char[size];
+ byte[] java_lang_results = new byte[size];
+ int index = 0;
+ for (int cp = Character.MIN_SUPPLEMENTARY_CODE_POINT; cp <= Character.MAX_CODE_POINT;
+ cp++) {
+ chars[index] = Character.highSurrogate(cp);
+ chars[index + 1] = Character.lowSurrogate(cp);
+ java_lang_results[index] = java_lang_results[index + 1] = Character
+ .getDirectionality(cp);
+ index += 2;
+ }
+
+ byte[] android_text_results = new byte[size];
+ AndroidCharacter.getDirectionalities(chars, android_text_results, index);
+ assertArrayEquals(java_lang_results, android_text_results);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 30309cf64e96..8691e73f82fb 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -557,6 +557,62 @@ public class BinderCallsStatsTest {
assertEquals(0, bcs.getExceptionCounts().size());
}
+ @Test
+ public void testOverflow_sameEntry() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ bcs.setSamplingInterval(1);
+ bcs.setMaxBinderCallStats(2);
+
+ Binder binder = new Binder();
+ CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.time += 10;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ callSession = bcs.callStarted(binder, 1);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ callSession = bcs.callStarted(binder, 1);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ BinderCallsStats.UidEntry uidEntry = bcs.getUidEntries().get(WORKSOURCE_UID);
+ List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
+ assertEquals(1, callStatsList.size());
+ BinderCallsStats.CallStat callStats = callStatsList.get(0);
+ assertEquals(3, callStats.callCount);
+ }
+
+ @Test
+ public void testOverflow_overflowEntry() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ bcs.setSamplingInterval(1);
+ bcs.setMaxBinderCallStats(1);
+
+ Binder binder = new Binder();
+ CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.time += 10;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ callSession = bcs.callStarted(binder, 2);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ List<BinderCallsStats.ExportedCallStat> callStatsList = bcs.getExportedCallStats();
+ assertEquals(2, callStatsList.size());
+ BinderCallsStats.ExportedCallStat callStats = callStatsList.get(0);
+ assertEquals(1, callStats.callCount);
+ assertEquals("1", callStats.methodName);
+ assertEquals("android.os.Binder", callStats.className);
+ assertEquals(CALLING_UID, callStats.callingUid);
+
+ callStats = callStatsList.get(1);
+ assertEquals(1, callStats.callCount);
+ assertEquals("-1", callStats.methodName);
+ assertEquals("com.android.internal.os.BinderCallsStats$OverflowBinder",
+ callStats.className);
+ assertEquals(CALLING_UID, callStats.callingUid);
+ }
+
class TestBinderCallsStats extends BinderCallsStats {
public int callingUid = CALLING_UID;
public int workSourceUid = WORKSOURCE_UID;
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 3933f508fe84..dbe6e8f4eebb 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -163,9 +163,6 @@ public class Typeface {
private int[] mSupportedAxes;
private static final int[] EMPTY_AXES = {};
- // The underlying font families.
- private final FontFamily[] mFamilies;
-
@UnsupportedAppUsage
private static void setDefault(Typeface t) {
sDefaultTypeface = t;
@@ -732,21 +729,17 @@ public class Typeface {
public Typeface build() {
final int userFallbackSize = mFamilies.size();
final FontFamily[] fallback = SystemFonts.getSystemFallback(mFallbackName);
- final FontFamily[] fullFamilies = new FontFamily[fallback.length + userFallbackSize];
final long[] ptrArray = new long[fallback.length + userFallbackSize];
for (int i = 0; i < userFallbackSize; ++i) {
ptrArray[i] = mFamilies.get(i).getNativePtr();
- fullFamilies[i] = mFamilies.get(i);
}
for (int i = 0; i < fallback.length; ++i) {
ptrArray[i + userFallbackSize] = fallback[i].getNativePtr();
- fullFamilies[i + userFallbackSize] = fallback[i];
}
final int weight = mStyle == null ? 400 : mStyle.getWeight();
final int italic =
(mStyle == null || mStyle.getSlant() == FontStyle.FONT_SLANT_UPRIGHT) ? 0 : 1;
-
- return new Typeface(nativeCreateFromArray(ptrArray, weight, italic), fullFamilies);
+ return new Typeface(nativeCreateFromArray(ptrArray, weight, italic));
}
}
@@ -811,7 +804,7 @@ public class Typeface {
}
}
- typeface = new Typeface(nativeCreateFromTypeface(ni, style), family.mFamilies);
+ typeface = new Typeface(nativeCreateFromTypeface(ni, style));
styles.put(style, typeface);
}
return typeface;
@@ -879,8 +872,7 @@ public class Typeface {
}
typeface = new Typeface(
- nativeCreateFromTypefaceWithExactStyle(
- base.native_instance, weight, italic), base.mFamilies);
+ nativeCreateFromTypefaceWithExactStyle(base.native_instance, weight, italic));
innerCache.put(key, typeface);
}
return typeface;
@@ -890,8 +882,7 @@ public class Typeface {
public static Typeface createFromTypefaceWithVariation(@Nullable Typeface family,
@NonNull List<FontVariationAxis> axes) {
final Typeface base = family == null ? Typeface.DEFAULT : family;
- return new Typeface(nativeCreateFromTypefaceWithVariation(base.native_instance, axes),
- base.mFamilies);
+ return new Typeface(nativeCreateFromTypefaceWithVariation(base.native_instance, axes));
}
/**
@@ -997,7 +988,7 @@ public class Typeface {
ptrArray[i] = families[i].getNativePtr();
}
return new Typeface(nativeCreateFromArray(ptrArray,
- RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE), families);
+ RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
}
/**
@@ -1045,19 +1036,6 @@ public class Typeface {
}
native_instance = ni;
- mFamilies = new FontFamily[0];
- sRegistry.registerNativeAllocation(this, native_instance);
- mStyle = nativeGetStyle(ni);
- mWeight = nativeGetWeight(ni);
- }
-
- private Typeface(long ni, @NonNull FontFamily[] families) {
- if (ni == 0) {
- throw new IllegalStateException("native typeface cannot be made");
- }
-
- native_instance = ni;
- mFamilies = families;
sRegistry.registerNativeAllocation(this, native_instance);
mStyle = nativeGetStyle(ni);
mWeight = nativeGetWeight(ni);
@@ -1084,8 +1062,7 @@ public class Typeface {
final Typeface base = systemFontMap.get(alias.getToName());
final int weight = alias.getWeight();
final Typeface newFace = weight == 400 ? base :
- new Typeface(nativeCreateWeightAlias(base.native_instance, weight),
- base.mFamilies);
+ new Typeface(nativeCreateWeightAlias(base.native_instance, weight));
systemFontMap.put(alias.getName(), newFace);
}
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 5f27faed08f3..4a5b61a2d6cd 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -179,7 +179,6 @@ cc_defaults {
"renderthread/CanvasContext.cpp",
"renderthread/DrawFrameTask.cpp",
"renderthread/EglManager.cpp",
- "renderthread/ReliableSurface.cpp",
"renderthread/VulkanManager.cpp",
"renderthread/RenderProxy.cpp",
"renderthread/RenderTask.cpp",
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 07979a22c988..142bca95e598 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -155,7 +155,7 @@ void SkiaOpenGLPipeline::onStop() {
}
}
-bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior,
+bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior,
ColorMode colorMode) {
if (mEglSurface != EGL_NO_SURFACE) {
mEglManager.destroySurface(mEglSurface);
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index 479910697871..4ab3541d447b 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -42,7 +42,7 @@ public:
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) override;
DeferredLayerUpdater* createTextureLayer() override;
- bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior,
+ bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
renderthread::ColorMode colorMode) override;
void onStop() override;
bool isSurfaceReady() override;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index e50ad1cd8c44..a494e490aea1 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -115,7 +115,7 @@ DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() {
void SkiaVulkanPipeline::onStop() {}
-bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior,
+bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior,
ColorMode colorMode) {
if (mVkSurface) {
mVkManager.destroySurface(mVkSurface);
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 02874c7d2c69..14c0d69dba33 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -38,7 +38,7 @@ public:
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) override;
DeferredLayerUpdater* createTextureLayer() override;
- bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior,
+ bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
renderthread::ColorMode colorMode) override;
void onStop() override;
bool isSurfaceReady() override;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 182233fb3715..f1a522ecd588 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -142,12 +142,7 @@ void CanvasContext::destroy() {
void CanvasContext::setSurface(sp<Surface>&& surface) {
ATRACE_CALL();
- if (surface) {
- mNativeSurface = new ReliableSurface{std::move(surface)};
- mNativeSurface->setDequeueTimeout(500_ms);
- } else {
- mNativeSurface = nullptr;
- }
+ mNativeSurface = std::move(surface);
ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::SRGB;
bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode);
@@ -290,7 +285,6 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
info.damageAccumulator = &mDamageAccumulator;
info.layerUpdateQueue = &mLayerUpdateQueue;
- info.out.canDrawThisFrame = true;
mAnimationContext->startFrame(info.mode);
mRenderPipeline->onPrepareTree();
@@ -310,7 +304,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
mIsDirty = true;
- if (CC_UNLIKELY(!hasSurface())) {
+ if (CC_UNLIKELY(!mNativeSurface.get())) {
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
info.out.canDrawThisFrame = false;
return;
@@ -329,6 +323,27 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
// the deadline for RT animations
info.out.canDrawThisFrame = false;
}
+ /* This logic exists to try and recover from a display latch miss, which essentially
+ * results in the bufferqueue being double-buffered instead of triple-buffered.
+ * SurfaceFlinger itself now tries to handle & recover from this situation, so this
+ * logic should no longer be necessary. As it's occasionally triggering when
+ * undesired disable it.
+ * TODO: Remove this entirely if the results are solid.
+ else if (vsyncDelta >= mRenderThread.timeLord().frameIntervalNanos() * 3 ||
+ (latestVsync - mLastDropVsync) < 500_ms) {
+ // It's been several frame intervals, assume the buffer queue is fine
+ // or the last drop was too recent
+ info.out.canDrawThisFrame = true;
+ } else {
+ info.out.canDrawThisFrame = !isSwapChainStuffed();
+ if (!info.out.canDrawThisFrame) {
+ // dropping frame
+ mLastDropVsync = mRenderThread.timeLord().latestVsync();
+ }
+ }
+ */
+ } else {
+ info.out.canDrawThisFrame = true;
}
// TODO: Do we need to abort out if the backdrop is added but not ready? Should that even
@@ -339,19 +354,6 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
if (!info.out.canDrawThisFrame) {
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
- return;
- }
-
- int err = mNativeSurface->reserveNext();
- if (err != OK) {
- mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
- info.out.canDrawThisFrame = false;
- ALOGW("reserveNext failed, error = %d", err);
- if (err != TIMED_OUT) {
- // A timed out surface can still recover, but assume others are permanently dead.
- setSurface(nullptr);
- }
- return;
}
bool postedFrameCallback = false;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 9e7abf447cd6..70be4a6d7730 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -25,7 +25,6 @@
#include "IRenderPipeline.h"
#include "LayerUpdateQueue.h"
#include "RenderNode.h"
-#include "ReliableSurface.h"
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
#include "thread/Task.h"
@@ -220,7 +219,7 @@ private:
EGLint mLastFrameHeight = 0;
RenderThread& mRenderThread;
- sp<ReliableSurface> mNativeSurface;
+ sp<Surface> mNativeSurface;
// stopped indicates the CanvasContext will reject actual redraw operations,
// and defer repaint until it is un-stopped
bool mStopped = false;
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 8230dfd44f9a..65ced6ad9316 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -31,8 +31,6 @@
#include <string>
#include <vector>
-#include <system/window.h>
-#include <gui/Surface.h>
#define GLES_VERSION 2
@@ -108,7 +106,7 @@ void EglManager::initialize() {
LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
"Failed to initialize display %p! err=%s", mEglDisplay, eglErrorString());
- ALOGV("Initialized EGL, version %d.%d", (int)major, (int)minor);
+ ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
initExtensions();
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 42e17b273bee..4972554c65cc 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -28,10 +28,10 @@
class GrContext;
-struct ANativeWindow;
-
namespace android {
+class Surface;
+
namespace uirenderer {
class DeferredLayerUpdater;
@@ -67,7 +67,7 @@ public:
virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) = 0;
virtual DeferredLayerUpdater* createTextureLayer() = 0;
- virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior, ColorMode colorMode) = 0;
+ virtual bool setSurface(Surface* window, SwapBehavior swapBehavior, ColorMode colorMode) = 0;
virtual void onStop() = 0;
virtual bool isSurfaceReady() = 0;
virtual bool isContextReady() = 0;
diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp
deleted file mode 100644
index 0ab4cd29f1cd..000000000000
--- a/libs/hwui/renderthread/ReliableSurface.cpp
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#include "ReliableSurface.h"
-
-#include <private/android/AHardwareBufferHelpers.h>
-
-namespace android::uirenderer::renderthread {
-
-// TODO: Make surface less protected
-// This exists because perform is a varargs, and ANativeWindow has no va_list perform.
-// So wrapping/chaining that is hard. Telling the compiler to ignore protected is easy, so we do
-// that instead
-struct SurfaceExposer : Surface {
- // Make warnings happy
- SurfaceExposer() = delete;
-
- using Surface::setBufferCount;
- using Surface::setSwapInterval;
- using Surface::dequeueBuffer;
- using Surface::queueBuffer;
- using Surface::cancelBuffer;
- using Surface::lockBuffer_DEPRECATED;
- using Surface::perform;
-};
-
-#define callProtected(surface, func, ...) ((*surface).*&SurfaceExposer::func)(__VA_ARGS__)
-
-ReliableSurface::ReliableSurface(sp<Surface>&& surface) : mSurface(std::move(surface)) {
- LOG_ALWAYS_FATAL_IF(!mSurface, "Error, unable to wrap a nullptr");
-
- ANativeWindow::setSwapInterval = hook_setSwapInterval;
- ANativeWindow::dequeueBuffer = hook_dequeueBuffer;
- ANativeWindow::cancelBuffer = hook_cancelBuffer;
- ANativeWindow::queueBuffer = hook_queueBuffer;
- ANativeWindow::query = hook_query;
- ANativeWindow::perform = hook_perform;
-
- ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;
- ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED;
- ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED;
- ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED;
-}
-
-void ReliableSurface::perform(int operation, va_list args) {
- std::lock_guard _lock{mMutex};
-
- switch (operation) {
- case NATIVE_WINDOW_SET_USAGE:
- mUsage = va_arg(args, uint32_t);
- break;
- case NATIVE_WINDOW_SET_USAGE64:
- mUsage = va_arg(args, uint64_t);
- break;
- case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
- /* width */ va_arg(args, uint32_t);
- /* height */ va_arg(args, uint32_t);
- mFormat = va_arg(args, PixelFormat);
- break;
- case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
- mFormat = va_arg(args, PixelFormat);
- break;
- }
-}
-
-int ReliableSurface::reserveNext() {
- {
- std::lock_guard _lock{mMutex};
- if (mReservedBuffer) {
- ALOGW("reserveNext called but there was already a buffer reserved?");
- return OK;
- }
- if (mInErrorState) {
- return UNKNOWN_ERROR;
- }
- }
-
- int fenceFd = -1;
- ANativeWindowBuffer* buffer = nullptr;
- int result = callProtected(mSurface, dequeueBuffer, &buffer, &fenceFd);
-
- {
- std::lock_guard _lock{mMutex};
- LOG_ALWAYS_FATAL_IF(mReservedBuffer, "race condition in reserveNext");
- mReservedBuffer = buffer;
- mReservedFenceFd.reset(fenceFd);
- if (result != OK) {
- ALOGW("reserveNext failed, error %d", result);
- }
- }
-
- return result;
-}
-
-void ReliableSurface::clearReservedBuffer() {
- std::lock_guard _lock{mMutex};
- if (mReservedBuffer) {
- ALOGW("Reserved buffer %p was never used", mReservedBuffer);
- }
- mReservedBuffer = nullptr;
- mReservedFenceFd.reset();
-}
-
-int ReliableSurface::cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd) {
- clearReservedBuffer();
- if (isFallbackBuffer(buffer)) {
- if (fenceFd > 0) {
- close(fenceFd);
- }
- return OK;
- }
- int result = callProtected(mSurface, cancelBuffer, buffer, fenceFd);
- return result;
-}
-
-int ReliableSurface::dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd) {
- {
- std::lock_guard _lock{mMutex};
- if (mReservedBuffer) {
- *buffer = mReservedBuffer;
- *fenceFd = mReservedFenceFd.release();
- mReservedBuffer = nullptr;
- return OK;
- }
- }
-
- int result = callProtected(mSurface, dequeueBuffer, buffer, fenceFd);
- if (result != OK) {
- ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result);
- *buffer = acquireFallbackBuffer();
- *fenceFd = -1;
- return *buffer ? OK : INVALID_OPERATION;
- }
- return OK;
-}
-
-int ReliableSurface::queueBuffer(ANativeWindowBuffer* buffer, int fenceFd) {
- clearReservedBuffer();
-
- if (isFallbackBuffer(buffer)) {
- if (fenceFd > 0) {
- close(fenceFd);
- }
- return OK;
- }
-
- int result = callProtected(mSurface, queueBuffer, buffer, fenceFd);
- return result;
-}
-
-bool ReliableSurface::isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const {
- if (!mScratchBuffer || !windowBuffer) {
- return false;
- }
- ANativeWindowBuffer* scratchBuffer =
- AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get());
- return windowBuffer == scratchBuffer;
-}
-
-ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer() {
- std::lock_guard _lock{mMutex};
- mInErrorState = true;
-
- if (mScratchBuffer) {
- return AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get());
- }
-
- AHardwareBuffer_Desc desc;
- desc.usage = mUsage;
- desc.format = mFormat;
- desc.width = 1;
- desc.height = 1;
- desc.layers = 1;
- desc.rfu0 = 0;
- desc.rfu1 = 0;
- AHardwareBuffer* newBuffer = nullptr;
- int err = AHardwareBuffer_allocate(&desc, &newBuffer);
- if (err) {
- // Allocate failed, that sucks
- ALOGW("Failed to allocate scratch buffer, error=%d", err);
- return nullptr;
- }
- mScratchBuffer.reset(newBuffer);
- return AHardwareBuffer_to_ANativeWindowBuffer(newBuffer);
-}
-
-Surface* ReliableSurface::getWrapped(const ANativeWindow* window) {
- return getSelf(window)->mSurface.get();
-}
-
-int ReliableSurface::hook_setSwapInterval(ANativeWindow* window, int interval) {
- return callProtected(getWrapped(window), setSwapInterval, interval);
-}
-
-int ReliableSurface::hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
- int* fenceFd) {
- return getSelf(window)->dequeueBuffer(buffer, fenceFd);
-}
-
-int ReliableSurface::hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
- int fenceFd) {
- return getSelf(window)->cancelBuffer(buffer, fenceFd);
-}
-
-int ReliableSurface::hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
- int fenceFd) {
- return getSelf(window)->queueBuffer(buffer, fenceFd);
-}
-
-int ReliableSurface::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
- ANativeWindowBuffer** buffer) {
- ANativeWindowBuffer* buf;
- int fenceFd = -1;
- int result = window->dequeueBuffer(window, &buf, &fenceFd);
- if (result != OK) {
- return result;
- }
- sp<Fence> fence(new Fence(fenceFd));
- int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED");
- if (waitResult != OK) {
- ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d", waitResult);
- window->cancelBuffer(window, buf, -1);
- return waitResult;
- }
- *buffer = buf;
- return result;
-}
-
-int ReliableSurface::hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
- ANativeWindowBuffer* buffer) {
- return window->cancelBuffer(window, buffer, -1);
-}
-
-int ReliableSurface::hook_lockBuffer_DEPRECATED(ANativeWindow* window,
- ANativeWindowBuffer* buffer) {
- // This method is a no-op in Surface as well
- return OK;
-}
-
-int ReliableSurface::hook_queueBuffer_DEPRECATED(ANativeWindow* window,
- ANativeWindowBuffer* buffer) {
- return window->queueBuffer(window, buffer, -1);
-}
-
-int ReliableSurface::hook_query(const ANativeWindow* window, int what, int* value) {
- return getWrapped(window)->query(what, value);
-}
-
-int ReliableSurface::hook_perform(ANativeWindow* window, int operation, ...) {
- va_list args;
- va_start(args, operation);
- int result = callProtected(getWrapped(window), perform, operation, args);
- va_end(args);
-
- switch (operation) {
- case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
- case NATIVE_WINDOW_SET_USAGE:
- case NATIVE_WINDOW_SET_USAGE64:
- va_start(args, operation);
- getSelf(window)->perform(operation, args);
- va_end(args);
- break;
- default:
- break;
- }
-
- return result;
-}
-
-}; // namespace android::uirenderer::renderthread \ No newline at end of file
diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h
deleted file mode 100644
index 9ae53a9798d3..000000000000
--- a/libs/hwui/renderthread/ReliableSurface.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#pragma once
-
-#include <gui/Surface.h>
-#include <utils/Macros.h>
-#include <utils/StrongPointer.h>
-
-#include <memory>
-
-namespace android::uirenderer::renderthread {
-
-class ReliableSurface : public ANativeObjectBase<ANativeWindow, ReliableSurface, RefBase> {
- PREVENT_COPY_AND_ASSIGN(ReliableSurface);
-
-public:
- ReliableSurface(sp<Surface>&& surface);
-
- void setDequeueTimeout(nsecs_t timeout) { mSurface->setDequeueTimeout(timeout); }
-
- int reserveNext();
-
- void allocateBuffers() { mSurface->allocateBuffers(); }
-
- int query(int what, int* value) const { return mSurface->query(what, value); }
-
- nsecs_t getLastDequeueStartTime() const { return mSurface->getLastDequeueStartTime(); }
-
- uint64_t getNextFrameNumber() const { return mSurface->getNextFrameNumber(); }
-
-private:
- const sp<Surface> mSurface;
-
- mutable std::mutex mMutex;
-
- uint64_t mUsage = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER;
- PixelFormat mFormat = PIXEL_FORMAT_RGBA_8888;
- std::unique_ptr<AHardwareBuffer, void (*)(AHardwareBuffer*)> mScratchBuffer{
- nullptr, AHardwareBuffer_release};
- bool mInErrorState = false;
- ANativeWindowBuffer* mReservedBuffer = nullptr;
- base::unique_fd mReservedFenceFd;
-
- bool isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const;
- ANativeWindowBuffer* acquireFallbackBuffer();
- void clearReservedBuffer();
-
- void perform(int operation, va_list args);
- int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd);
- int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
- int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd);
-
- static Surface* getWrapped(const ANativeWindow*);
-
- // ANativeWindow hooks
- static int hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
- static int hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
- int* fenceFd);
- static int hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
-
- static int hook_perform(ANativeWindow* window, int operation, ...);
- static int hook_query(const ANativeWindow* window, int what, int* value);
- static int hook_setSwapInterval(ANativeWindow* window, int interval);
-
- static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
- static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer** buffer);
- static int hook_lockBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
- static int hook_queueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
-};
-
-}; // namespace android::uirenderer::renderthread \ No newline at end of file
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index b047f8de515d..111dd0f0b63e 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -29,6 +29,7 @@ import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
+import android.media.MediaPlayer2.DrmInfo;
import android.media.MediaPlayer2Proto.PlayerMessage;
import android.media.MediaPlayer2Proto.Value;
import android.net.Uri;
@@ -72,7 +73,11 @@ import java.util.Map;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@@ -300,21 +305,7 @@ public class MediaPlayer2 implements AutoCloseable
private volatile float mVolume = 1.0f;
private VideoSize mVideoSize = new VideoSize(0, 0);
- // TODO: create per-source drm fields in SourceInfo
- // Modular DRM
- private final Object mDrmLock = new Object();
- //--- guarded by |mDrmLock| start
- private UUID mDrmUUID;
- private DrmInfo mDrmInfo;
- private MediaDrm mDrmObj;
- private byte[] mDrmSessionId;
- private boolean mDrmInfoResolved;
- private boolean mActiveDrmScheme;
- private boolean mDrmConfigAllowed;
- private boolean mDrmProvisioningInProgress;
- private boolean mPrepareDrmInProgress;
- private ProvisioningThread mDrmProvisioningThread;
- //--- guarded by |mDrmLock| end
+ private ExecutorService mDrmThreadPool = Executors.newCachedThreadPool();
// Creating a dummy audio track, used for keeping session id alive
private final Object mSessionIdLock = new Object();
@@ -328,6 +319,7 @@ public class MediaPlayer2 implements AutoCloseable
private final List<Task> mPendingTasks = new LinkedList<>();
@GuardedBy("mTaskLock")
private Task mCurrentTask;
+ private final AtomicLong mTaskIdGenerator = new AtomicLong(0);
@GuardedBy("mTaskLock")
boolean mIsPreviousCommandSeekTo = false;
@@ -413,15 +405,13 @@ public class MediaPlayer2 implements AutoCloseable
mHandlerThread = null;
}
- setCurrentSourceInfo(null);
- clearNextSourceInfos();
+ clearSourceInfos();
// Modular DRM clean up
mOnDrmConfigHelper = null;
synchronized (mDrmEventCbLock) {
mDrmEventCallbackRecords.clear();
}
- resetDrmState();
native_release();
@@ -461,13 +451,8 @@ public class MediaPlayer2 implements AutoCloseable
synchronized (mDrmEventCbLock) {
mDrmEventCallbackRecords.clear();
}
- setCurrentSourceInfo(null);
- clearNextSourceInfos();
- synchronized (mTaskLock) {
- mPendingTasks.clear();
- mIsPreviousCommandSeekTo = false;
- }
+ clearSourceInfos();
stayAwake(false);
native_reset();
@@ -481,7 +466,6 @@ public class MediaPlayer2 implements AutoCloseable
mTaskHandler.removeCallbacksAndMessages(null);
}
- resetDrmState();
}
private native void native_reset();
@@ -706,13 +690,14 @@ public class MediaPlayer2 implements AutoCloseable
}
synchronized (mSrcLock) {
- setCurrentSourceInfo(new SourceInfo(dsd));
+ setCurrentSourceInfo_l(new SourceInfo(dsd));
handleDataSource(true /* isCurrent */, dsd, mCurrentSourceInfo.mId);
}
} finally {
dsd.close();
}
}
+
});
}
@@ -732,7 +717,7 @@ public class MediaPlayer2 implements AutoCloseable
void process() {
Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
synchronized (mSrcLock) {
- clearNextSourceInfos();
+ clearNextSourceInfos_l();
mNextSourceInfos.add(new SourceInfo(dsd));
}
prepareNextDataSource();
@@ -758,7 +743,7 @@ public class MediaPlayer2 implements AutoCloseable
}
synchronized (mSrcLock) {
- clearNextSourceInfos();
+ clearNextSourceInfos_l();
for (DataSourceDesc dsd : dsds) {
if (dsd != null) {
mNextSourceInfos.add(new SourceInfo(dsd));
@@ -781,7 +766,9 @@ public class MediaPlayer2 implements AutoCloseable
return addTask(new Task(CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES, false) {
@Override
void process() {
- clearNextSourceInfos();
+ synchronized (mSrcLock) {
+ clearNextSourceInfos_l();
+ }
}
});
}
@@ -1073,7 +1060,7 @@ public class MediaPlayer2 implements AutoCloseable
SourceInfo nextSourceInfo = mNextSourceInfos.peek();
if (nextSourceInfo.mStateAsNextSource == NEXT_SOURCE_STATE_PREPARED) {
// Switch to next source only when it has been prepared.
- setCurrentSourceInfo(mNextSourceInfos.poll());
+ setCurrentSourceInfo_l(mNextSourceInfos.poll());
long srcId = mCurrentSourceInfo.mId;
try {
@@ -2173,7 +2160,7 @@ public class MediaPlayer2 implements AutoCloseable
final int what = msg.arg1;
final int extra = msg.arg2;
- final SourceInfo sourceInfo = getSourceInfoById(srcId);
+ final SourceInfo sourceInfo = getSourceInfo(srcId);
if (sourceInfo == null) {
return;
}
@@ -2227,11 +2214,11 @@ public class MediaPlayer2 implements AutoCloseable
Log.w(TAG, "MEDIA_DRM_INFO msg.obj=NULL");
} else if (msg.obj instanceof byte[]) {
// The PlayerMessage was parsed already in postEventFromNative
- final DrmInfo drmInfo;
- synchronized (mDrmLock) {
- if (mDrmInfo != null) {
- drmInfo = mDrmInfo.makeCopy();
+ final DrmInfo drmInfo;
+ synchronized (sourceInfo) {
+ if (sourceInfo.mDrmInfo != null) {
+ drmInfo = sourceInfo.mDrmInfo.makeCopy();
} else {
drmInfo = null;
}
@@ -2303,7 +2290,7 @@ public class MediaPlayer2 implements AutoCloseable
}
});
- SourceInfo src = getSourceInfoById(srcId);
+ SourceInfo src = getSourceInfo(srcId);
if (src != null) {
src.mBufferedPercentage.set(percent);
}
@@ -2504,6 +2491,7 @@ public class MediaPlayer2 implements AutoCloseable
return;
}
+ final SourceInfo sourceInfo = mp.getSourceInfo(srcId);
switch (what) {
case MEDIA_DRM_INFO:
// We need to derive mDrmInfo before prepare() returns so processing it here
@@ -2511,7 +2499,7 @@ public class MediaPlayer2 implements AutoCloseable
// notification looper so its handleMessage might process the event after prepare()
// has returned.
Log.v(TAG, "postEventFromNative MEDIA_DRM_INFO");
- if (obj != null) {
+ if (obj != null && sourceInfo != null) {
PlayerMessage playerMsg;
try {
playerMsg = PlayerMessage.parseFrom(obj);
@@ -2520,11 +2508,12 @@ public class MediaPlayer2 implements AutoCloseable
break;
}
DrmInfo drmInfo = new DrmInfo(playerMsg);
- synchronized (mp.mDrmLock) {
- mp.mDrmInfo = drmInfo;
+ synchronized (sourceInfo) {
+ sourceInfo.mDrmInfo = drmInfo;
}
} else {
- Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + obj);
+ Log.w(TAG, "MEDIA_DRM_INFO sourceInfo " + sourceInfo
+ + " msg.obj of unexpected type " + obj);
}
break;
@@ -2533,8 +2522,10 @@ public class MediaPlayer2 implements AutoCloseable
// mainly for prepare() use case. For prepare(), this still can run to a race
// condition b/c MediaPlayerNative releases the prepare() lock before calling notify
// so we also set mDrmInfoResolved in prepare().
- synchronized (mp.mDrmLock) {
- mp.mDrmInfoResolved = true;
+ if (sourceInfo != null) {
+ synchronized (sourceInfo) {
+ sourceInfo.mDrmInfoResolved = true;
+ }
}
break;
}
@@ -3211,9 +3202,7 @@ public class MediaPlayer2 implements AutoCloseable
*/
// This is a synchronous call.
public void setOnDrmConfigHelper(OnDrmConfigHelper listener) {
- synchronized (mDrmLock) {
- mOnDrmConfigHelper = listener;
- }
+ mOnDrmConfigHelper = listener;
}
private OnDrmConfigHelper mOnDrmConfigHelper;
@@ -3358,24 +3347,27 @@ public class MediaPlayer2 implements AutoCloseable
* @throws IllegalStateException if called before being prepared
*/
public DrmInfo getDrmInfo(@NonNull DataSourceDesc dsd) {
- // TODO: this implementation only works when dsd is the only data source
- DrmInfo drmInfo = null;
-
- // there is not much point if the app calls getDrmInfo within an OnDrmInfoListenet;
- // regardless below returns drmInfo anyway instead of raising an exception
- synchronized (mDrmLock) {
- if (!mDrmInfoResolved && mDrmInfo == null) {
- final String msg = "The Player has not been prepared yet";
- Log.v(TAG, msg);
- throw new IllegalStateException(msg);
- }
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ DrmInfo drmInfo = null;
+
+ // there is not much point if the app calls getDrmInfo within an OnDrmInfoListener;
+ // regardless below returns drmInfo anyway instead of raising an exception
+ synchronized (sourceInfo) {
+ if (!sourceInfo.mDrmInfoResolved && sourceInfo.mDrmInfo == null) {
+ final String msg = "The Player has not been prepared yet";
+ Log.v(TAG, msg);
+ throw new IllegalStateException(msg);
+ }
- if (mDrmInfo != null) {
- drmInfo = mDrmInfo.makeCopy();
- }
- } // synchronized
+ if (sourceInfo.mDrmInfo != null) {
+ drmInfo = sourceInfo.mDrmInfo.makeCopy();
+ }
+ } // synchronized
- return drmInfo;
+ return drmInfo;
+ }
+ return null;
}
/**
@@ -3411,15 +3403,28 @@ public class MediaPlayer2 implements AutoCloseable
*/
// This is an asynchronous call.
public Object prepareDrm(@NonNull DataSourceDesc dsd, @NonNull UUID uuid) {
- // TODO: this implementation only works when dsd is the only data source
return addTask(new Task(CALL_COMPLETED_PREPARE_DRM, true) {
@Override
void process() {
- int status = PREPARE_DRM_STATUS_SUCCESS;
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ int status = PREPARE_DRM_STATUS_PREPARATION_ERROR;
boolean sendEvent = true;
+ if (sourceInfo == null) {
+ Log.e(TAG, "prepareDrm(): DataSource not found.");
+ } else if (sourceInfo.mDrmInfo == null) {
+ // only allowing if tied to a protected source;
+ // might relax for releasing offline keys
+ Log.e(TAG, "prepareDrm(): Wrong usage: The player must be prepared and "
+ + "DRM info be retrieved before this call.");
+ } else {
+ status = PREPARE_DRM_STATUS_SUCCESS;
+ }
+
try {
- doPrepareDrm(dsd, uuid);
+ if (status == PREPARE_DRM_STATUS_SUCCESS) {
+ sourceInfo.mDrmHandle.prepare(uuid);
+ }
} catch (ResourceBusyException e) {
status = PREPARE_DRM_STATUS_RESOURCE_BUSY;
} catch (UnsupportedSchemeException e) {
@@ -3428,14 +3433,14 @@ public class MediaPlayer2 implements AutoCloseable
Log.w(TAG, "prepareDrm: NotProvisionedException");
// handle provisioning internally; it'll reset mPrepareDrmInProgress
- status = handleProvisioninig(dsd, uuid);
+ status = sourceInfo.mDrmHandle.handleProvisioninig(uuid, mTaskId);
if (status == PREPARE_DRM_STATUS_SUCCESS) {
// DrmEventCallback will be fired in provisioning
sendEvent = false;
} else {
- synchronized (mDrmLock) {
- cleanDrmObj();
+ synchronized (sourceInfo.mDrmHandle) {
+ sourceInfo.mDrmHandle.cleanDrmObj();
}
switch (status) {
@@ -3478,95 +3483,6 @@ public class MediaPlayer2 implements AutoCloseable
});
}
- private void doPrepareDrm(@NonNull DataSourceDesc dsd, @NonNull UUID uuid)
- throws UnsupportedSchemeException, ResourceBusyException,
- NotProvisionedException {
- Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + mOnDrmConfigHelper);
-
- synchronized (mDrmLock) {
- // only allowing if tied to a protected source; might relax for releasing offline keys
- if (mDrmInfo == null) {
- final String msg = "prepareDrm(): Wrong usage: The player must be prepared and "
- + "DRM info be retrieved before this call.";
- Log.e(TAG, msg);
- throw new IllegalStateException(msg);
- }
-
- if (mActiveDrmScheme) {
- final String msg = "prepareDrm(): Wrong usage: There is already "
- + "an active DRM scheme with " + mDrmUUID;
- Log.e(TAG, msg);
- throw new IllegalStateException(msg);
- }
-
- if (mPrepareDrmInProgress) {
- final String msg = "prepareDrm(): Wrong usage: There is already "
- + "a pending prepareDrm call.";
- Log.e(TAG, msg);
- throw new IllegalStateException(msg);
- }
-
- if (mDrmProvisioningInProgress) {
- final String msg = "prepareDrm(): Unexpectd: Provisioning is already in progress.";
- Log.e(TAG, msg);
- throw new IllegalStateException(msg);
- }
-
- // shouldn't need this; just for safeguard
- cleanDrmObj();
-
- mPrepareDrmInProgress = true;
-
- try {
- // only creating the DRM object to allow pre-openSession configuration
- prepareDrm_createDrmStep(uuid);
- } catch (Exception e) {
- Log.w(TAG, "prepareDrm(): Exception ", e);
- mPrepareDrmInProgress = false;
- throw e;
- }
-
- mDrmConfigAllowed = true;
- } // synchronized
-
- // call the callback outside the lock
- if (mOnDrmConfigHelper != null) {
- mOnDrmConfigHelper.onDrmConfig(this, dsd);
- }
-
- synchronized (mDrmLock) {
- mDrmConfigAllowed = false;
- boolean earlyExit = false;
-
- try {
- prepareDrm_openSessionStep(uuid);
-
- mDrmUUID = uuid;
- mActiveDrmScheme = true;
- mPrepareDrmInProgress = false;
- } catch (IllegalStateException e) {
- final String msg = "prepareDrm(): Wrong usage: The player must be "
- + "in the prepared state to call prepareDrm().";
- Log.e(TAG, msg);
- earlyExit = true;
- mPrepareDrmInProgress = false;
- throw new IllegalStateException(msg);
- } catch (NotProvisionedException e) {
- Log.w(TAG, "prepareDrm: NotProvisionedException", e);
- throw e;
- } catch (Exception e) {
- Log.e(TAG, "prepareDrm: Exception " + e);
- earlyExit = true;
- mPrepareDrmInProgress = false;
- throw e;
- } finally {
- if (earlyExit) { // clean up object if didn't succeed
- cleanDrmObj();
- }
- } // finally
- } // synchronized
- }
-
/**
* Releases the DRM session for the given data source
* <p>
@@ -3581,35 +3497,10 @@ public class MediaPlayer2 implements AutoCloseable
// This is a synchronous call.
public void releaseDrm(@NonNull DataSourceDesc dsd)
throws NoDrmSchemeException {
- // TODO: this implementation only works when dsd is the only data source
- synchronized (mDrmLock) {
- Log.v(TAG, "releaseDrm:");
-
- if (!mActiveDrmScheme) {
- Log.e(TAG, "releaseDrm(): No active DRM scheme to release.");
- throw new NoDrmSchemeException(
- "releaseDrm: No active DRM scheme to release.");
- }
-
- try {
- // we don't have the player's state in this layer. The below call raises
- // exception if we're in a non-stopped/prepared state.
-
- // for cleaning native/mediaserver crypto object
- native_releaseDrm();
-
- // for cleaning client-side MediaDrm object; only called if above has succeeded
- cleanDrmObj();
-
- mActiveDrmScheme = false;
- } catch (IllegalStateException e) {
- Log.w(TAG, "releaseDrm: Exception ", e);
- throw new IllegalStateException(
- "releaseDrm: The player is not in a valid state.");
- } catch (Exception e) {
- Log.e(TAG, "releaseDrm: Exception ", e);
- }
- } // synchronized
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ sourceInfo.mDrmHandle.release();
+ }
}
private native void native_releaseDrm();
@@ -3653,51 +3544,22 @@ public class MediaPlayer2 implements AutoCloseable
*
* @throws NoDrmSchemeException if there is no active DRM session
*/
- @NonNull
public MediaDrm.KeyRequest getDrmKeyRequest(
@NonNull DataSourceDesc dsd,
@Nullable byte[] keySetId, @Nullable byte[] initData,
@Nullable String mimeType, @MediaDrmKeyType int keyType,
@Nullable Map<String, String> optionalParameters)
throws NoDrmSchemeException {
- // TODO: this implementation only works when dsd is the only data source
- Log.v(TAG, "getDrmKeyRequest: "
- + " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType
- + " keyType: " + keyType + " optionalParameters: " + optionalParameters);
-
- synchronized (mDrmLock) {
- if (!mActiveDrmScheme) {
- Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "getDrmKeyRequest: Has to set a DRM scheme first.");
- }
-
- try {
- byte[] scope = (keyType != MediaDrm.KEY_TYPE_RELEASE)
- ? mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
- keySetId; // keySetId for KEY_TYPE_RELEASE
-
- HashMap<String, String> hmapOptionalParameters =
- (optionalParameters != null)
- ? new HashMap<String, String>(optionalParameters) :
- null;
-
- MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(scope, initData, mimeType,
- keyType, hmapOptionalParameters);
- Log.v(TAG, "getDrmKeyRequest: --> request: " + request);
-
- return request;
-
- } catch (NotProvisionedException e) {
- Log.w(TAG, "getDrmKeyRequest NotProvisionedException: "
- + "Unexpected. Shouldn't have reached here.");
- throw new IllegalStateException("getDrmKeyRequest: Unexpected provisioning error.");
- } catch (Exception e) {
- Log.w(TAG, "getDrmKeyRequest Exception " + e);
- throw e;
- }
-
- } // synchronized
+ Log.v(TAG, "getDrmKeyRequest: " +
+ " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType +
+ " keyType: " + keyType + " optionalParameters: " + optionalParameters);
+
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ return sourceInfo.mDrmHandle.getDrmKeyRequest(
+ keySetId, initData, mimeType, keyType, optionalParameters);
+ }
+ return null;
}
/**
@@ -3727,40 +3589,13 @@ public class MediaPlayer2 implements AutoCloseable
@NonNull DataSourceDesc dsd,
@Nullable byte[] keySetId, @NonNull byte[] response)
throws NoDrmSchemeException, DeniedByServerException {
- // TODO: this implementation only works when dsd is the only data source
Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId + " response: " + response);
- synchronized (mDrmLock) {
-
- if (!mActiveDrmScheme) {
- Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "getDrmKeyRequest: Has to set a DRM scheme first.");
- }
-
- try {
- byte[] scope = (keySetId == null)
- ? mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
- keySetId; // keySetId for KEY_TYPE_RELEASE
-
- byte[] keySetResult = mDrmObj.provideKeyResponse(scope, response);
-
- Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId + " response: " + response
- + " --> " + keySetResult);
-
-
- return keySetResult;
-
- } catch (NotProvisionedException e) {
- Log.w(TAG, "provideDrmKeyResponse NotProvisionedException: "
- + "Unexpected. Shouldn't have reached here.");
- throw new IllegalStateException("provideDrmKeyResponse: "
- + "Unexpected provisioning error.");
- } catch (Exception e) {
- Log.w(TAG, "provideDrmKeyResponse Exception " + e);
- throw e;
- }
- } // synchronized
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ return sourceInfo.mDrmHandle.provideDrmKeyResponse(keySetId, response);
+ }
+ return null;
}
/**
@@ -3779,23 +3614,12 @@ public class MediaPlayer2 implements AutoCloseable
@NonNull DataSourceDesc dsd,
@NonNull byte[] keySetId)
throws NoDrmSchemeException {
- // TODO: this implementation only works when dsd is the only data source
Log.v(TAG, "restoreDrmKeys: keySetId: " + keySetId);
- synchronized (mDrmLock) {
- if (!mActiveDrmScheme) {
- Log.w(TAG, "restoreDrmKeys NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "restoreDrmKeys: Has to set a DRM scheme first.");
- }
-
- try {
- mDrmObj.restoreKeys(mDrmSessionId, keySetId);
- } catch (Exception e) {
- Log.w(TAG, "restoreKeys Exception " + e);
- throw e;
- }
- } // synchronized
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ sourceInfo.mDrmHandle.restoreDrmKeys(keySetId);
+ }
}
/**
@@ -3812,34 +3636,17 @@ public class MediaPlayer2 implements AutoCloseable
*
* @throws NoDrmSchemeException if there is no active DRM session
*/
- @NonNull
public String getDrmPropertyString(
@NonNull DataSourceDesc dsd,
@NonNull @MediaDrmStringProperty String propertyName)
throws NoDrmSchemeException {
- // TODO: this implementation only works when dsd is the only data source
Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName);
- String value;
- synchronized (mDrmLock) {
-
- if (!mActiveDrmScheme && !mDrmConfigAllowed) {
- Log.w(TAG, "getDrmPropertyString NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "getDrmPropertyString: Has to prepareDrm() first.");
- }
-
- try {
- value = mDrmObj.getPropertyString(propertyName);
- } catch (Exception e) {
- Log.w(TAG, "getDrmPropertyString Exception " + e);
- throw e;
- }
- } // synchronized
-
- Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName + " --> value: " + value);
-
- return value;
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ return sourceInfo.mDrmHandle.getDrmPropertyString(propertyName);
+ }
+ return null;
}
/**
@@ -3863,21 +3670,10 @@ public class MediaPlayer2 implements AutoCloseable
// TODO: this implementation only works when dsd is the only data source
Log.v(TAG, "setDrmPropertyString: propertyName: " + propertyName + " value: " + value);
- synchronized (mDrmLock) {
-
- if (!mActiveDrmScheme && !mDrmConfigAllowed) {
- Log.w(TAG, "setDrmPropertyString NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "setDrmPropertyString: Has to prepareDrm() first.");
- }
-
- try {
- mDrmObj.setPropertyString(propertyName, value);
- } catch (Exception e) {
- Log.w(TAG, "setDrmPropertyString Exception " + e);
- throw e;
- }
- } // synchronized
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ sourceInfo.mDrmHandle.setDrmPropertyString(propertyName, value);
+ }
}
/**
@@ -4029,43 +3825,6 @@ public class MediaPlayer2 implements AutoCloseable
private native void native_prepareDrm(@NonNull byte[] uuid, @NonNull byte[] drmSessionId);
- // Modular DRM helpers
-
- private void prepareDrm_createDrmStep(@NonNull UUID uuid)
- throws UnsupportedSchemeException {
- Log.v(TAG, "prepareDrm_createDrmStep: UUID: " + uuid);
-
- try {
- mDrmObj = new MediaDrm(uuid);
- Log.v(TAG, "prepareDrm_createDrmStep: Created mDrmObj=" + mDrmObj);
- } catch (Exception e) { // UnsupportedSchemeException
- Log.e(TAG, "prepareDrm_createDrmStep: MediaDrm failed with " + e);
- throw e;
- }
- }
-
- private void prepareDrm_openSessionStep(@NonNull UUID uuid)
- throws NotProvisionedException, ResourceBusyException {
- Log.v(TAG, "prepareDrm_openSessionStep: uuid: " + uuid);
-
- // TODO: don't need an open session for a future specialKeyReleaseDrm mode but we should do
- // it anyway so it raises provisioning error if needed. We'd rather handle provisioning
- // at prepareDrm/openSession rather than getDrmKeyRequest/provideDrmKeyResponse
- try {
- mDrmSessionId = mDrmObj.openSession();
- Log.v(TAG, "prepareDrm_openSessionStep: mDrmSessionId=" + mDrmSessionId);
-
- // Sending it down to native/mediaserver to create the crypto object
- // This call could simply fail due to bad player state, e.g., after play().
- native_prepareDrm(getByteArrayFromUUID(uuid), mDrmSessionId);
- Log.v(TAG, "prepareDrm_openSessionStep: native_prepareDrm/Crypto succeeded");
-
- } catch (Exception e) { //ResourceBusyException, NotProvisionedException
- Log.e(TAG, "prepareDrm_openSessionStep: open/crypto failed with " + e);
- throw e;
- }
- }
-
// Instantiated from the native side
@SuppressWarnings("unused")
private static class StreamEventCallback extends AudioTrack.StreamEventCallback {
@@ -4097,227 +3856,28 @@ public class MediaPlayer2 implements AutoCloseable
}
}
- private class ProvisioningThread extends Thread {
- public static final int TIMEOUT_MS = 60000;
-
- private final DataSourceDesc mDSD;
- private UUID mUuid;
- private String mUrlStr;
- private Object mDrmLock;
- private MediaPlayer2 mMediaPlayer;
- private int mStatus;
- public int status() {
- return mStatus;
- }
-
- public ProvisioningThread(MediaDrm.ProvisionRequest request,
- DataSourceDesc dsd,
- UUID uuid, MediaPlayer2 mediaPlayer) {
- // lock is held by the caller
- mDSD = dsd;
- mDrmLock = mediaPlayer.mDrmLock;
- mMediaPlayer = mediaPlayer;
-
- mUrlStr = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData());
- mUuid = uuid;
-
- mStatus = PREPARE_DRM_STATUS_PREPARATION_ERROR;
-
- Log.v(TAG, "handleProvisioninig: Thread is initialised url: " + mUrlStr);
- }
-
- public void run() {
-
- byte[] response = null;
- boolean provisioningSucceeded = false;
- try {
- URL url = new URL(mUrlStr);
- final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
- try {
- connection.setRequestMethod("POST");
- connection.setDoOutput(false);
- connection.setDoInput(true);
- connection.setConnectTimeout(TIMEOUT_MS);
- connection.setReadTimeout(TIMEOUT_MS);
-
- connection.connect();
- response = readInputStreamFully(connection.getInputStream());
-
- Log.v(TAG, "handleProvisioninig: Thread run: response "
- + response.length + " " + response);
- } catch (Exception e) {
- mStatus = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
- Log.w(TAG, "handleProvisioninig: Thread run: connect " + e + " url: " + url);
- } finally {
- connection.disconnect();
- }
- } catch (Exception e) {
- mStatus = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
- Log.w(TAG, "handleProvisioninig: Thread run: openConnection " + e);
- }
-
- if (response != null) {
- try {
- mDrmObj.provideProvisionResponse(response);
- Log.v(TAG, "handleProvisioninig: Thread run: "
- + "provideProvisionResponse SUCCEEDED!");
-
- provisioningSucceeded = true;
- } catch (Exception e) {
- mStatus = PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR;
- Log.w(TAG, "handleProvisioninig: Thread run: "
- + "provideProvisionResponse " + e);
- }
- }
-
- boolean succeeded = false;
-
- synchronized (mDrmLock) {
- // continuing with prepareDrm
- if (provisioningSucceeded) {
- succeeded = mMediaPlayer.resumePrepareDrm(mUuid);
- mStatus = (succeeded)
- ? PREPARE_DRM_STATUS_SUCCESS :
- PREPARE_DRM_STATUS_PREPARATION_ERROR;
- }
- mMediaPlayer.mDrmProvisioningInProgress = false;
- mMediaPlayer.mPrepareDrmInProgress = false;
- if (!succeeded) {
- cleanDrmObj(); // cleaning up if it hasn't gone through while in the lock
- }
- } // synchronized
-
- // calling the callback outside the lock
- sendDrmEvent(new DrmEventNotifier() {
- @Override
- public void notify(DrmEventCallback callback) {
- callback.onDrmPrepared(
- mMediaPlayer, mDSD, mStatus);
- }
- });
-
- synchronized (mTaskLock) {
- if (mCurrentTask != null
- && mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE_DRM
- && mCurrentTask.mNeedToWaitForEventToComplete) {
- mCurrentTask = null;
- processPendingTask_l();
- }
- }
- }
-
- /**
- * Returns a byte[] containing the remainder of 'in', closing it when done.
- */
- private byte[] readInputStreamFully(InputStream in) throws IOException {
- try {
- return readInputStreamFullyNoClose(in);
- } finally {
- in.close();
- }
- }
-
- /**
- * Returns a byte[] containing the remainder of 'in'.
- */
- private byte[] readInputStreamFullyNoClose(InputStream in) throws IOException {
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- byte[] buffer = new byte[1024];
- int count;
- while ((count = in.read(buffer)) != -1) {
- bytes.write(buffer, 0, count);
- }
- return bytes.toByteArray();
- }
- } // ProvisioningThread
-
- private int handleProvisioninig(DataSourceDesc dsd, UUID uuid) {
- synchronized (mDrmLock) {
- if (mDrmProvisioningInProgress) {
- Log.e(TAG, "handleProvisioninig: Unexpected mDrmProvisioningInProgress");
- return PREPARE_DRM_STATUS_PREPARATION_ERROR;
- }
-
- MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
- if (provReq == null) {
- Log.e(TAG, "handleProvisioninig: getProvisionRequest returned null.");
- return PREPARE_DRM_STATUS_PREPARATION_ERROR;
- }
-
- Log.v(TAG, "handleProvisioninig provReq "
- + " data: " + provReq.getData() + " url: " + provReq.getDefaultUrl());
-
- // networking in a background thread
- mDrmProvisioningInProgress = true;
-
- mDrmProvisioningThread = new ProvisioningThread(provReq, dsd, uuid, this);
- mDrmProvisioningThread.start();
-
- return PREPARE_DRM_STATUS_SUCCESS;
- }
- }
-
- private boolean resumePrepareDrm(UUID uuid) {
- Log.v(TAG, "resumePrepareDrm: uuid: " + uuid);
-
- // mDrmLock is guaranteed to be held
- boolean success = false;
+ /**
+ * Returns a byte[] containing the remainder of 'in', closing it when done.
+ */
+ private static byte[] readInputStreamFully(InputStream in) throws IOException {
try {
- // resuming
- prepareDrm_openSessionStep(uuid);
-
- mDrmUUID = uuid;
- mActiveDrmScheme = true;
-
- success = true;
- } catch (Exception e) {
- Log.w(TAG, "handleProvisioninig: Thread run native_prepareDrm resume failed with " + e);
- // mDrmObj clean up is done by the caller
+ return readInputStreamFullyNoClose(in);
+ } finally {
+ in.close();
}
-
- return success;
- }
-
- private void resetDrmState() {
- synchronized (mDrmLock) {
- Log.v(TAG, "resetDrmState:"
- + " mDrmInfo=" + mDrmInfo
- + " mDrmProvisioningThread=" + mDrmProvisioningThread
- + " mPrepareDrmInProgress=" + mPrepareDrmInProgress
- + " mActiveDrmScheme=" + mActiveDrmScheme);
-
- mDrmInfoResolved = false;
- mDrmInfo = null;
-
- if (mDrmProvisioningThread != null) {
- // timeout; relying on HttpUrlConnection
- try {
- mDrmProvisioningThread.join();
- } catch (InterruptedException e) {
- Log.w(TAG, "resetDrmState: ProvThread.join Exception " + e);
- }
- mDrmProvisioningThread = null;
- }
-
- mPrepareDrmInProgress = false;
- mActiveDrmScheme = false;
-
- cleanDrmObj();
- } // synchronized
}
- private void cleanDrmObj() {
- // the caller holds mDrmLock
- Log.v(TAG, "cleanDrmObj: mDrmObj=" + mDrmObj + " mDrmSessionId=" + mDrmSessionId);
-
- if (mDrmSessionId != null) {
- mDrmObj.closeSession(mDrmSessionId);
- mDrmSessionId = null;
- }
- if (mDrmObj != null) {
- mDrmObj.release();
- mDrmObj = null;
+ /**
+ * Returns a byte[] containing the remainder of 'in'.
+ */
+ private static byte[] readInputStreamFullyNoClose(InputStream in) throws IOException {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int count;
+ while ((count = in.read(buffer)) != -1) {
+ bytes.write(buffer, 0, count);
}
+ return bytes.toByteArray();
}
private static byte[] getByteArrayFromUUID(@NonNull UUID uuid) {
@@ -4333,8 +3893,6 @@ public class MediaPlayer2 implements AutoCloseable
return uuidBytes;
}
- // Modular DRM end
-
private static class TimedTextUtil {
// These keys must be in sync with the keys in TextDescription2.h
private static final int KEY_START_TIME = 7; // int
@@ -4410,6 +3968,7 @@ public class MediaPlayer2 implements AutoCloseable
}
private abstract class Task implements Runnable {
+ final long mTaskId = mTaskIdGenerator.getAndIncrement();
private final int mMediaCallType;
private final boolean mNeedToWaitForEventToComplete;
private DataSourceDesc mDSD;
@@ -4501,7 +4060,503 @@ public class MediaPlayer2 implements AutoCloseable
}
};
- private final class SourceInfo {
+ // Modular DRM
+ final class DrmHandle {
+
+ static final int PROVISION_TIMEOUT_MS = 60000;
+
+ final DataSourceDesc mDSD;
+
+ //--- guarded by |this| start
+ MediaDrm mDrmObj;
+ byte[] mDrmSessionId;
+ UUID mActiveDrmUUID;
+ boolean mDrmConfigAllowed;
+ boolean mDrmProvisioningInProgress;
+ boolean mPrepareDrmInProgress;
+ Future<?> mProvisionResult;
+ //--- guarded by |this| end
+
+ DrmHandle(DataSourceDesc dsd) {
+ mDSD = dsd;
+ }
+
+ void prepare(UUID uuid) throws UnsupportedSchemeException,
+ ResourceBusyException, NotProvisionedException {
+ final OnDrmConfigHelper onDrmConfigHelper = mOnDrmConfigHelper;
+ Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + onDrmConfigHelper);
+
+ synchronized (this) {
+ if (mActiveDrmUUID != null) {
+ final String msg = "prepareDrm(): Wrong usage: There is already "
+ + "an active DRM scheme with " + uuid;
+ Log.e(TAG, msg);
+ throw new IllegalStateException(msg);
+ }
+
+ if (mPrepareDrmInProgress) {
+ final String msg = "prepareDrm(): Wrong usage: There is already "
+ + "a pending prepareDrm call.";
+ Log.e(TAG, msg);
+ throw new IllegalStateException(msg);
+ }
+
+ if (mDrmProvisioningInProgress) {
+ final String msg = "prepareDrm(): Unexpectd: Provisioning already in progress";
+ Log.e(TAG, msg);
+ throw new IllegalStateException(msg);
+ }
+
+ // shouldn't need this; just for safeguard
+ cleanDrmObj();
+
+ mPrepareDrmInProgress = true;
+
+ try {
+ // only creating the DRM object to allow pre-openSession configuration
+ prepareDrm_createDrmStep(uuid);
+ } catch (Exception e) {
+ Log.w(TAG, "prepareDrm(): Exception ", e);
+ mPrepareDrmInProgress = false;
+ throw e;
+ }
+
+ mDrmConfigAllowed = true;
+ } // synchronized
+
+ // call the callback outside the lock
+ if (onDrmConfigHelper != null) {
+ onDrmConfigHelper.onDrmConfig(MediaPlayer2.this, mDSD);
+ }
+
+ synchronized (this) {
+ mDrmConfigAllowed = false;
+ boolean earlyExit = false;
+
+ try {
+ prepareDrm_openSessionStep(uuid);
+
+ this.mActiveDrmUUID = uuid;
+ mPrepareDrmInProgress = false;
+ } catch (IllegalStateException e) {
+ final String msg = "prepareDrm(): Wrong usage: The player must be "
+ + "in the prepared state to call prepareDrm().";
+ Log.e(TAG, msg);
+ earlyExit = true;
+ mPrepareDrmInProgress = false;
+ throw new IllegalStateException(msg);
+ } catch (NotProvisionedException e) {
+ Log.w(TAG, "prepareDrm: NotProvisionedException", e);
+ throw e;
+ } catch (Exception e) {
+ Log.e(TAG, "prepareDrm: Exception " + e);
+ earlyExit = true;
+ mPrepareDrmInProgress = false;
+ throw e;
+ } finally {
+ if (earlyExit) { // clean up object if didn't succeed
+ cleanDrmObj();
+ }
+ } // finally
+ } // synchronized
+ }
+
+ void prepareDrm_createDrmStep(UUID uuid)
+ throws UnsupportedSchemeException {
+ Log.v(TAG, "prepareDrm_createDrmStep: UUID: " + uuid);
+
+ try {
+ mDrmObj = new MediaDrm(uuid);
+ Log.v(TAG, "prepareDrm_createDrmStep: Created mDrmObj=" + mDrmObj);
+ } catch (Exception e) { // UnsupportedSchemeException
+ Log.e(TAG, "prepareDrm_createDrmStep: MediaDrm failed with " + e);
+ throw e;
+ }
+ }
+
+ void prepareDrm_openSessionStep(UUID uuid)
+ throws NotProvisionedException, ResourceBusyException {
+ Log.v(TAG, "prepareDrm_openSessionStep: uuid: " + uuid);
+
+ // TODO:
+ // don't need an open session for a future specialKeyReleaseDrm mode but we should do
+ // it anyway so it raises provisioning error if needed. We'd rather handle provisioning
+ // at prepareDrm/openSession rather than getDrmKeyRequest/provideDrmKeyResponse
+ try {
+ mDrmSessionId = mDrmObj.openSession();
+ Log.v(TAG, "prepareDrm_openSessionStep: mDrmSessionId=" + mDrmSessionId);
+
+ // Sending it down to native/mediaserver to create the crypto object
+ // This call could simply fail due to bad player state, e.g., after play().
+ MediaPlayer2.this.native_prepareDrm(getByteArrayFromUUID(uuid), mDrmSessionId);
+ Log.v(TAG, "prepareDrm_openSessionStep: native_prepareDrm/Crypto succeeded");
+
+ } catch (Exception e) { //ResourceBusyException, NotProvisionedException
+ Log.e(TAG, "prepareDrm_openSessionStep: open/crypto failed with " + e);
+ throw e;
+ }
+
+ }
+
+ int handleProvisioninig(UUID uuid, long taskId) {
+ synchronized (this) {
+ if (mDrmProvisioningInProgress) {
+ Log.e(TAG, "handleProvisioninig: Unexpected mDrmProvisioningInProgress");
+ return PREPARE_DRM_STATUS_PREPARATION_ERROR;
+ }
+
+ MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
+ if (provReq == null) {
+ Log.e(TAG, "handleProvisioninig: getProvisionRequest returned null.");
+ return PREPARE_DRM_STATUS_PREPARATION_ERROR;
+ }
+
+ Log.v(TAG, "handleProvisioninig provReq "
+ + " data: " + provReq.getData() + " url: " + provReq.getDefaultUrl());
+
+ // networking in a background thread
+ mDrmProvisioningInProgress = true;
+
+ mProvisionResult = mDrmThreadPool.submit(newProvisioningTask(uuid, taskId));
+
+ return PREPARE_DRM_STATUS_SUCCESS;
+ }
+ }
+
+ void provision(UUID uuid, long taskId) {
+
+ MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
+ String urlStr = provReq.getDefaultUrl();
+ urlStr += "&signedRequest=" + new String(provReq.getData());
+ Log.v(TAG, "handleProvisioninig: Thread is initialised url: " + urlStr);
+
+ byte[] response = null;
+ boolean provisioningSucceeded = false;
+ int status = PREPARE_DRM_STATUS_PREPARATION_ERROR;
+ try {
+ URL url = new URL(urlStr);
+ final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ try {
+ connection.setRequestMethod("POST");
+ connection.setDoOutput(false);
+ connection.setDoInput(true);
+ connection.setConnectTimeout(PROVISION_TIMEOUT_MS);
+ connection.setReadTimeout(PROVISION_TIMEOUT_MS);
+
+ connection.connect();
+ response = readInputStreamFully(connection.getInputStream());
+
+ Log.v(TAG, "handleProvisioninig: Thread run: response " +
+ response.length + " " + response);
+ } catch (Exception e) {
+ status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
+ Log.w(TAG, "handleProvisioninig: Thread run: connect " + e + " url: " + url);
+ } finally {
+ connection.disconnect();
+ }
+ } catch (Exception e) {
+ status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
+ Log.w(TAG, "handleProvisioninig: Thread run: openConnection " + e);
+ }
+
+ if (response != null) {
+ try {
+ mDrmObj.provideProvisionResponse(response);
+ Log.v(TAG, "handleProvisioninig: Thread run: " +
+ "provideProvisionResponse SUCCEEDED!");
+
+ provisioningSucceeded = true;
+ } catch (Exception e) {
+ status = PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR;
+ Log.w(TAG, "handleProvisioninig: Thread run: " +
+ "provideProvisionResponse " + e);
+ }
+ }
+
+ boolean succeeded = false;
+
+ synchronized (this) {
+ // continuing with prepareDrm
+ if (provisioningSucceeded) {
+ succeeded = resumePrepare(uuid);
+ status = (succeeded) ?
+ PREPARE_DRM_STATUS_SUCCESS :
+ PREPARE_DRM_STATUS_PREPARATION_ERROR;
+ }
+ mDrmProvisioningInProgress = false;
+ mPrepareDrmInProgress = false;
+ if (!succeeded) {
+ cleanDrmObj(); // cleaning up if it hasn't gone through while in the lock
+ }
+ } // synchronized
+
+ // calling the callback outside the lock
+ final int finalStatus = status;
+ sendDrmEvent(new DrmEventNotifier() {
+ @Override
+ public void notify(DrmEventCallback callback) {
+ callback.onDrmPrepared(
+ MediaPlayer2.this, mDSD, finalStatus);
+ }
+ });
+
+ synchronized (mTaskLock) {
+ if (mCurrentTask != null
+ && mCurrentTask.mTaskId == taskId
+ && mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE_DRM
+ && mCurrentTask.mNeedToWaitForEventToComplete) {
+ mCurrentTask = null;
+ processPendingTask_l();
+ }
+ }
+ }
+
+ Runnable newProvisioningTask(UUID uuid, long taskId) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ provision(uuid, taskId);
+ }
+ };
+ }
+
+ boolean resumePrepare(UUID uuid) {
+ Log.v(TAG, "resumePrepareDrm: uuid: " + uuid);
+
+ // mDrmLock is guaranteed to be held
+ boolean success = false;
+ try {
+ // resuming
+ prepareDrm_openSessionStep(uuid);
+
+ this.mActiveDrmUUID = uuid;
+
+ success = true;
+ } catch (Exception e) {
+ Log.w(TAG, "handleProvisioninig: Thread run native_prepareDrm resume failed:" + e);
+ // mDrmObj clean up is done by the caller
+ }
+
+ return success;
+ }
+
+ void cleanDrmObj() {
+ // the caller holds mDrmLock
+ Log.v(TAG, "cleanDrmObj: mDrmObj=" + mDrmObj + " mDrmSessionId=" + mDrmSessionId);
+
+ if (mDrmSessionId != null) {
+ mDrmObj.closeSession(mDrmSessionId);
+ mDrmSessionId = null;
+ }
+ if (mDrmObj != null) {
+ mDrmObj.close();
+ mDrmObj = null;
+ }
+ }
+
+ void release() throws NoDrmSchemeException {
+ synchronized (this) {
+ Log.v(TAG, "releaseDrm:");
+
+ if (mActiveDrmUUID == null) {
+ Log.e(TAG, "releaseDrm(): No active DRM scheme to release.");
+ throw new NoDrmSchemeException(
+ "releaseDrm: No active DRM scheme to release.");
+ }
+
+ try {
+ // we don't have the player's state in this layer. The below call raises
+ // exception if we're in a non-stopped/prepared state.
+
+ // for cleaning native/mediaserver crypto object
+ native_releaseDrm();
+
+ // for cleaning client-side MediaDrm object; only called if above has succeeded
+ cleanDrmObj();
+
+ this.mActiveDrmUUID = null;
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "releaseDrm: Exception ", e);
+ throw new IllegalStateException(
+ "releaseDrm: The player is not in a valid state.");
+ } catch (Exception e) {
+ Log.e(TAG, "releaseDrm: Exception ", e);
+ }
+ } // synchronized
+ }
+
+ void cleanup() {
+ synchronized (this) {
+ Log.v(TAG, "cleanupDrm: " +
+ " mProvisioningTask=" + mProvisionResult +
+ " mPrepareDrmInProgress=" + mPrepareDrmInProgress +
+ " mActiveDrmScheme=" + mActiveDrmUUID);
+
+ if (mProvisionResult != null) {
+ // timeout; relying on HttpUrlConnection
+ try {
+ mProvisionResult.get();
+ }
+ catch (InterruptedException | ExecutionException e) {
+ Log.w(TAG, "resetDrmState: ProvThread.join Exception " + e);
+ }
+ }
+
+ // set to false to avoid duplicate release calls
+ this.mActiveDrmUUID = null;
+
+ cleanDrmObj();
+ } // synchronized
+ }
+
+ Runnable newCleanupTask() {
+ return new Runnable() {
+ @Override
+ public void run() {
+ cleanup();
+ }
+ };
+ }
+
+ MediaDrm.KeyRequest getDrmKeyRequest(
+ byte[] keySetId, byte[] initData,
+ String mimeType, int keyType,
+ Map<String, String> optionalParameters)
+ throws NoDrmSchemeException {
+ synchronized (this) {
+ if (mActiveDrmUUID == null) {
+ Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
+ throw new NoDrmSchemeException(
+ "getDrmKeyRequest: Has to set a DRM scheme first.");
+ }
+
+ try {
+ byte[] scope = (keyType != MediaDrm.KEY_TYPE_RELEASE) ?
+ mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
+ keySetId; // keySetId for KEY_TYPE_RELEASE
+
+ HashMap<String, String> hmapOptionalParameters =
+ (optionalParameters != null)
+ ? new HashMap<String, String>(optionalParameters)
+ : null;
+
+ MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(
+ scope, initData, mimeType, keyType, hmapOptionalParameters);
+ Log.v(TAG, "getDrmKeyRequest: --> request: " + request);
+
+ return request;
+
+ } catch (NotProvisionedException e) {
+ Log.w(TAG, "getDrmKeyRequest NotProvisionedException: " +
+ "Unexpected. Shouldn't have reached here.");
+ throw new IllegalStateException("getDrmKeyRequest: provisioning error.");
+ } catch (Exception e) {
+ Log.w(TAG, "getDrmKeyRequest Exception " + e);
+ throw e;
+ }
+
+ }
+ }
+
+ byte[] provideDrmKeyResponse(byte[] keySetId, byte[] response)
+ throws NoDrmSchemeException, DeniedByServerException {
+ synchronized (this) {
+
+ if (mActiveDrmUUID == null) {
+ Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
+ throw new NoDrmSchemeException(
+ "getDrmKeyRequest: Has to set a DRM scheme first.");
+ }
+
+ try {
+ byte[] scope = (keySetId == null) ?
+ mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
+ keySetId; // keySetId for KEY_TYPE_RELEASE
+
+ byte[] keySetResult = mDrmObj.provideKeyResponse(scope, response);
+
+ Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId
+ + " response: " + response + " --> " + keySetResult);
+
+
+ return keySetResult;
+
+ } catch (NotProvisionedException e) {
+ Log.w(TAG, "provideDrmKeyResponse NotProvisionedException: " +
+ "Unexpected. Shouldn't have reached here.");
+ throw new IllegalStateException("provideDrmKeyResponse: " +
+ "Unexpected provisioning error.");
+ } catch (Exception e) {
+ Log.w(TAG, "provideDrmKeyResponse Exception " + e);
+ throw e;
+ }
+ }
+ }
+
+ void restoreDrmKeys(byte[] keySetId)
+ throws NoDrmSchemeException {
+ synchronized (this) {
+ if (mActiveDrmUUID == null) {
+ Log.w(TAG, "restoreDrmKeys NoDrmSchemeException");
+ throw new NoDrmSchemeException(
+ "restoreDrmKeys: Has to set a DRM scheme first.");
+ }
+
+ try {
+ mDrmObj.restoreKeys(mDrmSessionId, keySetId);
+ } catch (Exception e) {
+ Log.w(TAG, "restoreKeys Exception " + e);
+ throw e;
+ }
+ }
+ }
+
+ String getDrmPropertyString(String propertyName)
+ throws NoDrmSchemeException {
+ String v;
+ synchronized (this) {
+
+ if (mActiveDrmUUID == null && !mDrmConfigAllowed) {
+ Log.w(TAG, "getDrmPropertyString NoDrmSchemeException");
+ throw new NoDrmSchemeException(
+ "getDrmPropertyString: Has to prepareDrm() first.");
+ }
+
+ try {
+ v = mDrmObj.getPropertyString(propertyName);
+ } catch (Exception e) {
+ Log.w(TAG, "getDrmPropertyString Exception " + e);
+ throw e;
+ }
+ } // synchronized
+
+ Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName + " --> value: " + v);
+
+ return v;
+ }
+
+ void setDrmPropertyString(String propertyName, String value)
+ throws NoDrmSchemeException {
+ synchronized (this) {
+
+ if ( mActiveDrmUUID == null && !mDrmConfigAllowed ) {
+ Log.w(TAG, "setDrmPropertyString NoDrmSchemeException");
+ throw new NoDrmSchemeException(
+ "setDrmPropertyString: Has to prepareDrm() first.");
+ }
+
+ try {
+ mDrmObj.setPropertyString(propertyName, value);
+ } catch ( Exception e ) {
+ Log.w(TAG, "setDrmPropertyString Exception " + e);
+ throw e;
+ }
+ }
+ }
+
+ }
+
+ final class SourceInfo {
final DataSourceDesc mDSD;
final long mId = mSrcIdGenerator.getAndIncrement();
AtomicInteger mBufferedPercentage = new AtomicInteger(0);
@@ -4513,8 +4568,14 @@ public class MediaPlayer2 implements AutoCloseable
int mStateAsNextSource = NEXT_SOURCE_STATE_INIT;
boolean mPlayPendingAsNextSource = false;
+ // Modular DRM
+ final DrmHandle mDrmHandle;
+ DrmInfo mDrmInfo;
+ boolean mDrmInfoResolved;
+
SourceInfo(DataSourceDesc dsd) {
this.mDSD = dsd;
+ mDrmHandle = new DrmHandle(dsd);
}
void close() {
@@ -4535,7 +4596,7 @@ public class MediaPlayer2 implements AutoCloseable
}
- private SourceInfo getSourceInfoById(long srcId) {
+ private SourceInfo getSourceInfo(long srcId) {
synchronized (mSrcLock) {
if (isCurrentSource(srcId)) {
return mCurrentSourceInfo;
@@ -4547,34 +4608,65 @@ public class MediaPlayer2 implements AutoCloseable
return null;
}
+ private SourceInfo getSourceInfo(DataSourceDesc dsd) {
+ synchronized (mSrcLock) {
+ if (isCurrentSource(dsd)) {
+ return mCurrentSourceInfo;
+ }
+ if (isNextSource(dsd)) {
+ return mNextSourceInfos.peek();
+ }
+ }
+ return null;
+ }
+
private boolean isCurrentSource(long srcId) {
synchronized (mSrcLock) {
return mCurrentSourceInfo != null && mCurrentSourceInfo.mId == srcId;
}
}
+ private boolean isCurrentSource(DataSourceDesc dsd) {
+ synchronized (mSrcLock) {
+ return mCurrentSourceInfo != null && mCurrentSourceInfo.mDSD == dsd;
+ }
+ }
+
private boolean isNextSource(long srcId) {
SourceInfo nextSourceInfo = mNextSourceInfos.peek();
return nextSourceInfo != null && nextSourceInfo.mId == srcId;
}
- private void setCurrentSourceInfo(SourceInfo newSourceInfo) {
- synchronized (mSrcLock) {
- if (mCurrentSourceInfo != null) {
- mCurrentSourceInfo.close();
- }
- mCurrentSourceInfo = newSourceInfo;
+ private boolean isNextSource(DataSourceDesc dsd) {
+ SourceInfo nextSourceInfo = mNextSourceInfos.peek();
+ return nextSourceInfo != null && nextSourceInfo.mDSD == dsd;
+ }
+
+ @GuardedBy("mSrcLock")
+ private void setCurrentSourceInfo_l(SourceInfo sourceInfo) {
+ cleanupSourceInfo(mCurrentSourceInfo);
+ mCurrentSourceInfo = sourceInfo;
+ }
+
+ @GuardedBy("mSrcLock")
+ private void clearNextSourceInfos_l() {
+ while (!mNextSourceInfos.isEmpty()) {
+ cleanupSourceInfo(mNextSourceInfos.poll());
+ }
+ }
+
+ private void cleanupSourceInfo(SourceInfo sourceInfo) {
+ if (sourceInfo != null) {
+ sourceInfo.close();
+ Runnable task = sourceInfo.mDrmHandle.newCleanupTask();
+ mDrmThreadPool.submit(task);
}
}
- private void clearNextSourceInfos() {
+ private void clearSourceInfos() {
synchronized (mSrcLock) {
- for (SourceInfo sourceInfo : mNextSourceInfos) {
- if (sourceInfo != null) {
- sourceInfo.close();
- }
- }
- mNextSourceInfos.clear();
+ setCurrentSourceInfo_l(null);
+ clearNextSourceInfos_l();
}
}
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 436897fbf8b0..874f21ea8c28 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -18,6 +18,7 @@ package android.media;
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -1029,7 +1030,7 @@ public class RingtoneManager {
* @throws FileNotFoundException if the provided URI could not be opened.
* @see #getDefaultUri
*/
- public static AssetFileDescriptor openDefaultRingtoneUri(
+ public static @Nullable AssetFileDescriptor openDefaultRingtoneUri(
@NonNull Context context, @NonNull Uri uri) throws FileNotFoundException {
// Try cached ringtone first since the actual provider may not be
// encryption aware, or it may be stored on CE media storage
diff --git a/native/webview/plat_support/draw_gl.h b/native/webview/plat_support/draw_gl.h
index c8434b61eba5..de13ed0dec6f 100644
--- a/native/webview/plat_support/draw_gl.h
+++ b/native/webview/plat_support/draw_gl.h
@@ -43,9 +43,9 @@ struct AwDrawGLInfo {
// Input: tells the draw function what action to perform.
enum Mode {
kModeDraw = 0,
- kModeProcess,
- kModeProcessNoContext,
- kModeSync,
+ kModeProcess = 1,
+ kModeProcessNoContext = 2,
+ kModeSync = 3,
} mode;
// Input: current clip rect in surface coordinates. Reflects the current state
@@ -93,9 +93,9 @@ typedef void (AwDrawGLFunction)(long view_context,
AwDrawGLInfo* draw_info,
void* spare);
enum AwMapMode {
- MAP_READ_ONLY,
- MAP_WRITE_ONLY,
- MAP_READ_WRITE,
+ MAP_READ_ONLY = 0,
+ MAP_WRITE_ONLY = 1,
+ MAP_READ_WRITE = 2,
};
// Called to create a GraphicBuffer
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
deleted file mode 100644
index bc06cab7aef5..000000000000
--- a/packages/PackageInstaller/Android.bp
+++ /dev/null
@@ -1,14 +0,0 @@
-android_app {
- name: "PackageInstaller",
-
- srcs: ["src/**/*.java"],
-
- static_libs: [
- "androidx.leanback_leanback",
- "xz-java",
- ],
-
- certificate: "platform",
- privileged: true,
- platform_apis: true,
-} \ No newline at end of file
diff --git a/packages/PackageInstaller/Android.mk b/packages/PackageInstaller/Android.mk
new file mode 100644
index 000000000000..ab5483c8afb8
--- /dev/null
+++ b/packages/PackageInstaller/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2018 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := PackageInstaller
+
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+LOCAL_STATIC_JAVA_LIBRARIES := xz-java
+LOCAL_STATIC_ANDROID_LIBRARIES := androidx.leanback_leanback
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index 4801f62bae67..eb9ec82c9519 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.packageinstaller">
+ <original-package android:name="com.android.packageinstaller" />
+
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
diff --git a/packages/SettingsLib/ActionButtonsPreference/Android.bp b/packages/SettingsLib/ActionButtonsPreference/Android.bp
new file mode 100644
index 000000000000..e518e0b97c4b
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+ name: "ActionButtonsPreference",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.preference_preference",
+ ],
+
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/ActionButtonsPreference/AndroidManifest.xml b/packages/SettingsLib/ActionButtonsPreference/AndroidManifest.xml
new file mode 100644
index 000000000000..4b9f1ab8d6cc
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.widget">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout/settings_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout/settings_action_buttons.xml
new file mode 100644
index 000000000000..4f47113bbafa
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/layout/settings_action_buttons.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="8dp"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/button1"
+ style="@style/SettingsActionButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <Button
+ android:id="@+id/button2"
+ style="@style/SettingsActionButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <Button
+ android:id="@+id/button3"
+ style="@style/SettingsActionButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <Button
+ android:id="@+id/button4"
+ style="@style/SettingsActionButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml b/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml
new file mode 100644
index 000000000000..efa508dcde62
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 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.
+ -->
+
+<resources>
+ <style name="SettingsActionButton" parent="android:Widget.DeviceDefault.Button.Borderless.Colored">
+ <item name="android:drawablePadding">4dp</item>
+ <item name="android:drawableTint">@*android:color/btn_colored_borderless_text_material</item>
+ <item name="android:layout_marginEnd">8dp</item>
+ <item name="android:paddingTop">20dp</item>
+ <item name="android:paddingBottom">20dp</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
new file mode 100644
index 000000000000..8b46cc608fd1
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2018 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.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.StringRes;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+/**
+ * This preference provides a four buttons layout with Settings style.
+ * It looks like below
+ *
+ * --------------------------------------------------
+ * button1 | button2 | button3 | button4 |
+ * --------------------------------------------------
+ *
+ * User can set title / icon / click listener for each button.
+ *
+ * By default, four buttons are visible.
+ * However, there are two cases which button should be invisible(View.GONE).
+ *
+ * 1. User sets invisible for button. ex: ActionButtonPreference.setButton1Visible(false)
+ * 2. User doesn't set any title or icon for button.
+ */
+public class ActionButtonsPreference extends Preference {
+
+ private static final String TAG = "ActionButtonPreference";
+ private final ButtonInfo mButton1Info = new ButtonInfo();
+ private final ButtonInfo mButton2Info = new ButtonInfo();
+ private final ButtonInfo mButton3Info = new ButtonInfo();
+ private final ButtonInfo mButton4Info = new ButtonInfo();
+
+ public ActionButtonsPreference(Context context, AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
+ }
+
+ public ActionButtonsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ public ActionButtonsPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public ActionButtonsPreference(Context context) {
+ super(context);
+ init();
+ }
+
+ private void init() {
+ setLayoutResource(R.layout.settings_action_buttons);
+ setSelectable(false);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ holder.setDividerAllowedAbove(true);
+ holder.setDividerAllowedBelow(true);
+
+ mButton1Info.mButton = (Button) holder.findViewById(R.id.button1);
+ mButton2Info.mButton = (Button) holder.findViewById(R.id.button2);
+ mButton3Info.mButton = (Button) holder.findViewById(R.id.button3);
+ mButton4Info.mButton = (Button) holder.findViewById(R.id.button4);
+
+ mButton1Info.setUpButton();
+ mButton2Info.setUpButton();
+ mButton3Info.setUpButton();
+ mButton4Info.setUpButton();
+ }
+
+ /**
+ * Set the visibility state of button1.
+ */
+ public ActionButtonsPreference setButton1Visible(boolean isVisible) {
+ if (isVisible != mButton1Info.mIsVisible) {
+ mButton1Info.mIsVisible = isVisible;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the text to be displayed in button1.
+ */
+ public ActionButtonsPreference setButton1Text(@StringRes int textResId) {
+ final String newText = getContext().getString(textResId);
+ if (!TextUtils.equals(newText, mButton1Info.mText)) {
+ mButton1Info.mText = newText;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the drawable to be displayed above of text in button1.
+ */
+ public ActionButtonsPreference setButton1Icon(@DrawableRes int iconResId) {
+ if (iconResId == 0) {
+ return this;
+ }
+
+ final Drawable icon;
+ try {
+ icon = getContext().getDrawable(iconResId);
+ mButton1Info.mIcon = icon;
+ notifyChanged();
+ } catch (Resources.NotFoundException exception) {
+ Log.e(TAG, "Resource does not exist: " + iconResId);
+ }
+ return this;
+ }
+
+ /**
+ * Set the enabled state of button1.
+ */
+ public ActionButtonsPreference setButton1Enabled(boolean isEnabled) {
+ if (isEnabled != mButton1Info.mIsEnabled) {
+ mButton1Info.mIsEnabled = isEnabled;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Register a callback to be invoked when button1 is clicked.
+ */
+ public ActionButtonsPreference setButton1OnClickListener(
+ View.OnClickListener listener) {
+ if (listener != mButton1Info.mListener) {
+ mButton1Info.mListener = listener;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Set the visibility state of button2.
+ */
+ public ActionButtonsPreference setButton2Visible(boolean isVisible) {
+ if (isVisible != mButton2Info.mIsVisible) {
+ mButton2Info.mIsVisible = isVisible;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the text to be displayed in button2.
+ */
+ public ActionButtonsPreference setButton2Text(@StringRes int textResId) {
+ final String newText = getContext().getString(textResId);
+ if (!TextUtils.equals(newText, mButton2Info.mText)) {
+ mButton2Info.mText = newText;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the drawable to be displayed above of text in button2.
+ */
+ public ActionButtonsPreference setButton2Icon(@DrawableRes int iconResId) {
+ if (iconResId == 0) {
+ return this;
+ }
+
+ final Drawable icon;
+ try {
+ icon = getContext().getDrawable(iconResId);
+ mButton2Info.mIcon = icon;
+ notifyChanged();
+ } catch (Resources.NotFoundException exception) {
+ Log.e(TAG, "Resource does not exist: " + iconResId);
+ }
+ return this;
+ }
+
+ /**
+ * Set the enabled state of button2.
+ */
+ public ActionButtonsPreference setButton2Enabled(boolean isEnabled) {
+ if (isEnabled != mButton2Info.mIsEnabled) {
+ mButton2Info.mIsEnabled = isEnabled;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Register a callback to be invoked when button2 is clicked.
+ */
+ public ActionButtonsPreference setButton2OnClickListener(
+ View.OnClickListener listener) {
+ if (listener != mButton2Info.mListener) {
+ mButton2Info.mListener = listener;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Set the visibility state of button3.
+ */
+ public ActionButtonsPreference setButton3Visible(boolean isVisible) {
+ if (isVisible != mButton3Info.mIsVisible) {
+ mButton3Info.mIsVisible = isVisible;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the text to be displayed in button3.
+ */
+ public ActionButtonsPreference setButton3Text(@StringRes int textResId) {
+ final String newText = getContext().getString(textResId);
+ if (!TextUtils.equals(newText, mButton3Info.mText)) {
+ mButton3Info.mText = newText;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the drawable to be displayed above of text in button3.
+ */
+ public ActionButtonsPreference setButton3Icon(@DrawableRes int iconResId) {
+ if (iconResId == 0) {
+ return this;
+ }
+
+ final Drawable icon;
+ try {
+ icon = getContext().getDrawable(iconResId);
+ mButton3Info.mIcon = icon;
+ notifyChanged();
+ } catch (Resources.NotFoundException exception) {
+ Log.e(TAG, "Resource does not exist: " + iconResId);
+ }
+ return this;
+ }
+
+ /**
+ * Set the enabled state of button3.
+ */
+ public ActionButtonsPreference setButton3Enabled(boolean isEnabled) {
+ if (isEnabled != mButton3Info.mIsEnabled) {
+ mButton3Info.mIsEnabled = isEnabled;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Register a callback to be invoked when button3 is clicked.
+ */
+ public ActionButtonsPreference setButton3OnClickListener(
+ View.OnClickListener listener) {
+ if (listener != mButton3Info.mListener) {
+ mButton3Info.mListener = listener;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Set the visibility state of button4.
+ */
+ public ActionButtonsPreference setButton4Visible(boolean isVisible) {
+ if (isVisible != mButton4Info.mIsVisible) {
+ mButton4Info.mIsVisible = isVisible;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the text to be displayed in button4.
+ */
+ public ActionButtonsPreference setButton4Text(@StringRes int textResId) {
+ final String newText = getContext().getString(textResId);
+ if (!TextUtils.equals(newText, mButton4Info.mText)) {
+ mButton4Info.mText = newText;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the drawable to be displayed above of text in button4.
+ */
+ public ActionButtonsPreference setButton4Icon(@DrawableRes int iconResId) {
+ if (iconResId == 0) {
+ return this;
+ }
+
+ final Drawable icon;
+ try {
+ icon = getContext().getDrawable(iconResId);
+ mButton4Info.mIcon = icon;
+ notifyChanged();
+ } catch (Resources.NotFoundException exception) {
+ Log.e(TAG, "Resource does not exist: " + iconResId);
+ }
+ return this;
+ }
+
+ /**
+ * Set the enabled state of button4.
+ */
+ public ActionButtonsPreference setButton4Enabled(boolean isEnabled) {
+ if (isEnabled != mButton4Info.mIsEnabled) {
+ mButton4Info.mIsEnabled = isEnabled;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Register a callback to be invoked when button4 is clicked.
+ */
+ public ActionButtonsPreference setButton4OnClickListener(
+ View.OnClickListener listener) {
+ if (listener != mButton4Info.mListener) {
+ mButton4Info.mListener = listener;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ static class ButtonInfo {
+ private Button mButton;
+ private CharSequence mText;
+ private Drawable mIcon;
+ private View.OnClickListener mListener;
+ private boolean mIsEnabled = true;
+ private boolean mIsVisible = true;
+
+ void setUpButton() {
+ mButton.setText(mText);
+ mButton.setOnClickListener(mListener);
+ mButton.setEnabled(mIsEnabled);
+ mButton.setCompoundDrawablesWithIntrinsicBounds(
+ null /* left */, mIcon /* top */, null /* right */, null /* bottom */);
+
+ if (shouldBeVisible()) {
+ mButton.setVisibility(View.VISIBLE);
+ } else {
+ mButton.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * By default, four buttons are visible.
+ * However, there are two cases which button should be invisible.
+ *
+ * 1. User set invisible for this button. ex: mIsVisible = false.
+ * 2. User didn't set any title or icon.
+ */
+ private boolean shouldBeVisible() {
+ return mIsVisible && (!TextUtils.isEmpty(mText) || mIcon != null);
+ }
+ }
+}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 444e72459510..cc17b25d9a40 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -17,6 +17,7 @@ android_library {
"SettingsLibSearchWidget",
"SettingsLibSettingsSpinner",
"SettingsLayoutPreference",
+ "ActionButtonsPreference",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index a97e05442b6d..9270d13002d7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -208,6 +208,10 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
mHiSyncId = id;
}
+ public boolean isHearingAidDevice() {
+ return mHiSyncId != BluetoothHearingAid.HI_SYNC_ID_INVALID;
+ }
+
void onBondingDockConnect() {
// Attempt to connect if UUIDs are available. Otherwise,
// we will connect when the ACTION_UUID intent arrives.
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java
new file mode 100644
index 000000000000..88fef08bfcb7
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2018 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.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.Button;
+
+import androidx.preference.PreferenceViewHolder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class ActionButtonsPreferenceTest {
+
+ private Context mContext;
+ private View mRootView;
+ private ActionButtonsPreference mPref;
+ private PreferenceViewHolder mHolder;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mRootView = View.inflate(mContext, R.layout.settings_action_buttons, null /* parent */);
+ mHolder = PreferenceViewHolder.createInstanceForTests(mRootView);
+ mPref = new ActionButtonsPreference(mContext);
+ }
+
+ @Test
+ public void onBindViewHolder_setTitle_shouldShowButtonByDefault() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton2Text(R.string.install_other_apps);
+ mPref.setButton3Text(R.string.install_other_apps);
+ mPref.setButton4Text(R.string.install_other_apps);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void onBindViewHolder_setIcon_shouldShowButtonByDefault() {
+ mPref.setButton1Icon(R.drawable.ic_plus);
+ mPref.setButton2Icon(R.drawable.ic_plus);
+ mPref.setButton3Icon(R.drawable.ic_plus);
+ mPref.setButton4Icon(R.drawable.ic_plus);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void onBindViewHolder_notSetTitleOrIcon_shouldNotShowButtonByDefault() {
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onBindViewHolder_setVisibleIsGoneAndSetTitle_shouldNotShowButton() {
+ mPref.setButton1Text(R.string.install_other_apps).setButton1Visible(false);
+ mPref.setButton2Text(R.string.install_other_apps).setButton2Visible(false);
+ mPref.setButton3Text(R.string.install_other_apps).setButton3Visible(false);
+ mPref.setButton4Text(R.string.install_other_apps).setButton4Visible(false);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onBindViewHolder_setVisibleIsGoneAndSetIcon_shouldNotShowButton() {
+ mPref.setButton1Icon(R.drawable.ic_plus).setButton1Visible(false);
+ mPref.setButton2Icon(R.drawable.ic_plus).setButton2Visible(false);
+ mPref.setButton3Icon(R.drawable.ic_plus).setButton3Visible(false);
+ mPref.setButton4Icon(R.drawable.ic_plus).setButton4Visible(false);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onBindViewHolder_setVisibility_shouldUpdateButtonVisibility() {
+ mPref.setButton1Text(R.string.install_other_apps).setButton1Visible(false);
+ mPref.setButton2Text(R.string.install_other_apps).setButton2Visible(false);
+ mPref.setButton3Text(R.string.install_other_apps).setButton3Visible(false);
+ mPref.setButton4Text(R.string.install_other_apps).setButton4Visible(false);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.GONE);
+
+ mPref.setButton1Visible(true);
+ mPref.setButton2Visible(true);
+ mPref.setButton3Visible(true);
+ mPref.setButton4Visible(true);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void onBindViewHolder_setEnabled_shouldEnableButton() {
+ mPref.setButton1Enabled(true);
+ mPref.setButton2Enabled(false);
+ mPref.setButton3Enabled(true);
+ mPref.setButton4Enabled(false);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).isEnabled()).isTrue();
+ assertThat(mRootView.findViewById(R.id.button2).isEnabled()).isFalse();
+ assertThat(mRootView.findViewById(R.id.button3).isEnabled()).isTrue();
+ assertThat(mRootView.findViewById(R.id.button4).isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onBindViewHolder_setText_shouldShowSameText() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton2Text(R.string.install_other_apps);
+ mPref.setButton3Text(R.string.install_other_apps);
+ mPref.setButton4Text(R.string.install_other_apps);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(((Button) mRootView.findViewById(R.id.button1)).getText())
+ .isEqualTo(mContext.getText(R.string.install_other_apps));
+ assertThat(((Button) mRootView.findViewById(R.id.button2)).getText())
+ .isEqualTo(mContext.getText(R.string.install_other_apps));
+ assertThat(((Button) mRootView.findViewById(R.id.button3)).getText())
+ .isEqualTo(mContext.getText(R.string.install_other_apps));
+ assertThat(((Button) mRootView.findViewById(R.id.button4)).getText())
+ .isEqualTo(mContext.getText(R.string.install_other_apps));
+ }
+
+ @Test
+ public void onBindViewHolder_setButtonIcon_iconMustDisplayAboveText() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton1Icon(R.drawable.ic_plus);
+
+ mPref.onBindViewHolder(mHolder);
+ final Drawable[] drawablesAroundText =
+ ((Button) mRootView.findViewById(R.id.button1))
+ .getCompoundDrawables();
+
+ assertThat(drawablesAroundText[1 /* top */]).isNotNull();
+ }
+
+ @Test
+ public void setButtonIcon_iconResourceIdIsZero_shouldNotDisplayIcon() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton1Icon(0);
+
+ mPref.onBindViewHolder(mHolder);
+ final Drawable[] drawablesAroundText =
+ ((Button) mRootView.findViewById(R.id.button1))
+ .getCompoundDrawables();
+
+ assertThat(drawablesAroundText[1 /* top */]).isNull();
+ }
+
+ @Test
+ public void setButtonIcon_iconResourceIdNotExisting_shouldNotDisplayIconAndCrash() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton1Icon(999999999 /* not existing id */);
+ // Should not crash here
+ mPref.onBindViewHolder(mHolder);
+ final Drawable[] drawablesAroundText =
+ ((Button) mRootView.findViewById(R.id.button1))
+ .getCompoundDrawables();
+
+ assertThat(drawablesAroundText[1 /* top */]).isNull();
+ }
+
+ public static ActionButtonsPreference createMock() {
+ final ActionButtonsPreference pref = mock(ActionButtonsPreference.class);
+ when(pref.setButton1Text(anyInt())).thenReturn(pref);
+ when(pref.setButton1Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton1Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton1Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton1OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+ when(pref.setButton2Text(anyInt())).thenReturn(pref);
+ when(pref.setButton2Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton2Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton2Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton2OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+ when(pref.setButton3Text(anyInt())).thenReturn(pref);
+ when(pref.setButton3Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton3Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton3Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton3OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+ when(pref.setButton4Text(anyInt())).thenReturn(pref);
+ when(pref.setButton4Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton4Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton4Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton4OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+ return pref;
+ }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index b2c12b277dc0..56b768feee23 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1914,6 +1914,15 @@ class SettingsProtoDumpUtil {
SecureSettingsProto.Location.CHANGER);
p.end(locationToken);
+ final long locationAccessCheckToken = p.start(SecureSettingsProto.LOCATION_ACCESS_CHECK);
+ dumpSetting(s, p,
+ Settings.Secure.LOCATION_ACCESS_CHECK_INTERVAL_MILLIS,
+ SecureSettingsProto.LocationAccessCheck.INTERVAL_MILLIS);
+ dumpSetting(s, p,
+ Settings.Secure.LOCATION_ACCESS_CHECK_DELAY_MILLIS,
+ SecureSettingsProto.LocationAccessCheck.DELAY_MILLIS);
+ p.end(locationAccessCheckToken);
+
final long lockScreenToken = p.start(SecureSettingsProto.LOCK_SCREEN);
// Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS intentionally excluded since it's deprecated.
// Settings.Secure.LOCK_PATTERN_ENABLED intentionally excluded since it's deprecated.
diff --git a/packages/SystemUI/res/layout/biometric_dialog.xml b/packages/SystemUI/res/layout/biometric_dialog.xml
index 5ca34b033f0d..1e8cd5a7e13b 100644
--- a/packages/SystemUI/res/layout/biometric_dialog.xml
+++ b/packages/SystemUI/res/layout/biometric_dialog.xml
@@ -160,6 +160,15 @@
android:maxLines="2"
android:text="@string/biometric_dialog_confirm"
android:visibility="gone"/>
+ <!-- Try Again Button -->
+ <Button android:id="@+id/button_try_again"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ style="@*android:style/Widget.DeviceDefault.Button.Colored"
+ android:gravity="center"
+ android:maxLines="2"
+ android:text="@string/biometric_dialog_try_again"
+ android:visibility="gone"/>
<Space android:id="@+id/rightSpacer"
android:layout_width="12dip"
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2148e274d506..9917257bba8a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -272,6 +272,8 @@
<string name="accessibility_biometric_dialog_help_area">Help message area</string>
<!-- Message shown when a biometric is authenticated, asking the user to confirm authentication [CHAR LIMIT=30] -->
<string name="biometric_dialog_confirm">Confirm</string>
+ <!-- Button name on BiometricPrompt shown when a biometric is detected but not authenticated. Tapping the button resumes authentication [CHAR_LIMIT=30] -->
+ <string name="biometric_dialog_try_again">Try again</string>
<!-- Message shown when the system-provided fingerprint dialog is shown, asking for authentication -->
<string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string>
@@ -700,6 +702,8 @@
<string name="quick_settings_bluetooth_secondary_label_headset">Headset</string>
<!-- QuickSettings: Bluetooth secondary label for an input/IO device being connected [CHAR LIMIT=20]-->
<string name="quick_settings_bluetooth_secondary_label_input">Input</string>
+ <!-- QuickSettings: Bluetooth secondary label for a Hearing Aids device being connected [CHAR LIMIT=20]-->
+ <string name="quick_settings_bluetooth_secondary_label_hearing_aids">Hearing Aids</string>
<!-- QuickSettings: Bluetooth secondary label shown when bluetooth is being enabled [CHAR LIMIT=NONE] -->
<string name="quick_settings_bluetooth_secondary_label_transient">Turning on&#8230;</string>
<!-- QuickSettings: Brightness [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index ece2bb9a9507..f3bdbae97e42 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -71,4 +71,9 @@ interface ISystemUiProxy {
*/
void onStatusBarMotionEvent(in MotionEvent event) = 9;
+ /**
+ * Get the corner radius of windows in pixels.
+ */
+ float getWindowCornerRadius() = 10;
+
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
index 65c52200a7d8..a9cf857c3e50 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
@@ -83,6 +83,7 @@ public class SyncRtSurfaceTransactionApplier {
t.setWindowCrop(params.surface, params.windowCrop);
t.setAlpha(params.surface, params.alpha);
t.setLayer(params.surface, params.layer);
+ t.setCornerRadius(params.surface, params.cornerRadius);
t.show(params.surface);
}
@@ -98,12 +99,13 @@ public class SyncRtSurfaceTransactionApplier {
* @param windowCrop Crop to apply.
*/
public SurfaceParams(SurfaceControlCompat surface, float alpha, Matrix matrix,
- Rect windowCrop, int layer) {
+ Rect windowCrop, int layer, float cornerRadius) {
this.surface = surface.mSurfaceControl;
this.alpha = alpha;
this.matrix = new Matrix(matrix);
this.windowCrop = new Rect(windowCrop);
this.layer = layer;
+ this.cornerRadius = cornerRadius;
}
final SurfaceControl surface;
@@ -111,5 +113,6 @@ public class SyncRtSurfaceTransactionApplier {
final Matrix matrix;
final Rect windowCrop;
final int layer;
+ final float cornerRadius;
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index ff9e84ca29c1..8a251ae51f38 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -18,6 +18,7 @@ package com.android.systemui.shared.system;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
@@ -61,7 +62,7 @@ public class WindowManagerWrapper {
public static final int TRANSIT_KEYGUARD_OCCLUDE = WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
public static final int TRANSIT_KEYGUARD_UNOCCLUDE = WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
- public static final int NAV_BAR_POS_INVALID = -1;
+ public static final int NAV_BAR_POS_INVALID = NAV_BAR_INVALID;
public static final int NAV_BAR_POS_LEFT = NAV_BAR_LEFT;
public static final int NAV_BAR_POS_RIGHT = NAV_BAR_RIGHT;
public static final int NAV_BAR_POS_BOTTOM = NAV_BAR_BOTTOM;
@@ -178,10 +179,9 @@ public class WindowManagerWrapper {
* @see #NAV_BAR_POS_BOTTOM
* @see #NAV_BAR_POS_INVALID
*/
- public int getNavBarPosition() {
+ public int getNavBarPosition(int displayId) {
try {
- // TODO: Use WindowManagerService.getNavBarPosition(int displayId)
- return WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
+ return WindowManagerGlobal.getWindowManagerService().getNavBarPosition(displayId);
} catch (RemoteException e) {
Log.w(TAG, "Failed to get nav bar position");
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index c0047c015813..a90a7d231dc1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -21,7 +21,7 @@ import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricPrompt;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -52,15 +52,20 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
private static final int MSG_BUTTON_NEGATIVE = 6;
private static final int MSG_USER_CANCELED = 7;
private static final int MSG_BUTTON_POSITIVE = 8;
+ private static final int MSG_BIOMETRIC_SHOW_TRY_AGAIN = 9;
+ private static final int MSG_TRY_AGAIN_PRESSED = 10;
private Map<Integer, BiometricDialogView> mDialogs; // BiometricAuthenticator type, view
private SomeArgs mCurrentDialogArgs;
private BiometricDialogView mCurrentDialog;
private WindowManager mWindowManager;
- private IBiometricPromptReceiver mReceiver;
+ private IBiometricServiceReceiverInternal mReceiver;
private boolean mDialogShowing;
private Callback mCallback = new Callback();
+ private boolean mTryAgainShowing; // No good place to save state before config change :/
+ private boolean mConfirmShowing; // No good place to save state before config change :/
+
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -89,6 +94,15 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
case MSG_BUTTON_POSITIVE:
handleButtonPositive();
break;
+ case MSG_BIOMETRIC_SHOW_TRY_AGAIN:
+ handleShowTryAgain();
+ break;
+ case MSG_TRY_AGAIN_PRESSED:
+ handleTryAgainPressed();
+ break;
+ default:
+ Log.w(TAG, "Unknown message: " + msg.what);
+ break;
}
}
};
@@ -96,7 +110,7 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
private class Callback implements DialogViewCallback {
@Override
public void onUserCanceled() {
- mHandler.obtainMessage(BiometricDialogImpl.MSG_USER_CANCELED).sendToTarget();
+ mHandler.obtainMessage(MSG_USER_CANCELED).sendToTarget();
}
@Override
@@ -107,12 +121,17 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
@Override
public void onNegativePressed() {
- mHandler.obtainMessage(BiometricDialogImpl.MSG_BUTTON_NEGATIVE).sendToTarget();
+ mHandler.obtainMessage(MSG_BUTTON_NEGATIVE).sendToTarget();
}
@Override
public void onPositivePressed() {
- mHandler.obtainMessage(BiometricDialogImpl.MSG_BUTTON_POSITIVE).sendToTarget();
+ mHandler.obtainMessage(MSG_BUTTON_POSITIVE).sendToTarget();
+ }
+
+ @Override
+ public void onTryAgainPressed() {
+ mHandler.obtainMessage(MSG_TRY_AGAIN_PRESSED).sendToTarget();
}
}
@@ -139,13 +158,14 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
- boolean requireConfirmation, int userId) {
+ public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
+ int type, boolean requireConfirmation, int userId) {
if (DEBUG) Log.d(TAG, "showBiometricDialog, type: " + type);
// Remove these messages as they are part of the previous client
mHandler.removeMessages(MSG_BIOMETRIC_ERROR);
mHandler.removeMessages(MSG_BIOMETRIC_HELP);
mHandler.removeMessages(MSG_BIOMETRIC_AUTHENTICATED);
+ mHandler.removeMessages(MSG_HIDE_DIALOG);
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
args.arg2 = receiver;
@@ -179,6 +199,12 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
mHandler.obtainMessage(MSG_HIDE_DIALOG, false /* userCanceled */).sendToTarget();
}
+ @Override
+ public void showBiometricTryAgain() {
+ if (DEBUG) Log.d(TAG, "showBiometricTryAgain");
+ mHandler.obtainMessage(MSG_BIOMETRIC_SHOW_TRY_AGAIN).sendToTarget();
+ }
+
private void handleShowDialog(SomeArgs args, boolean skipAnimation) {
mCurrentDialogArgs = args;
final int type = args.argi1;
@@ -193,11 +219,13 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
Log.w(TAG, "Dialog already showing");
return;
}
- mReceiver = (IBiometricPromptReceiver) args.arg2;
+ mReceiver = (IBiometricServiceReceiverInternal) args.arg2;
mCurrentDialog.setBundle((Bundle)args.arg1);
mCurrentDialog.setRequireConfirmation((boolean) args.arg3);
mCurrentDialog.setUserId(args.argi2);
mCurrentDialog.setSkipIntro(skipAnimation);
+ mCurrentDialog.setPendingTryAgain(mTryAgainShowing);
+ mCurrentDialog.setPendingConfirm(mConfirmShowing);
mWindowManager.addView(mCurrentDialog, mCurrentDialog.getLayoutParams());
mDialogShowing = true;
}
@@ -209,7 +237,8 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
mContext.getResources()
.getText(mCurrentDialog.getAuthenticatedAccessibilityResourceId()));
if (mCurrentDialog.requiresConfirmation()) {
- mCurrentDialog.showConfirmationButton();
+ mConfirmShowing = true;
+ mCurrentDialog.showConfirmationButton(true /* show */);
} else {
handleHideDialog(false /* userCanceled */);
}
@@ -226,6 +255,7 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
if (DEBUG) Log.d(TAG, "Dialog already dismissed");
return;
}
+ mTryAgainShowing = false;
mCurrentDialog.showErrorMessage(error);
}
@@ -246,6 +276,8 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
}
mReceiver = null;
mDialogShowing = false;
+ mConfirmShowing = false;
+ mTryAgainShowing = false;
mCurrentDialog.startDismiss();
}
@@ -259,6 +291,7 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
} catch (RemoteException e) {
Log.e(TAG, "Remote exception when handling negative button", e);
}
+ mTryAgainShowing = false;
handleHideDialog(false /* userCanceled */);
}
@@ -272,13 +305,31 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
} catch (RemoteException e) {
Log.e(TAG, "Remote exception when handling positive button", e);
}
+ mConfirmShowing = false;
handleHideDialog(false /* userCanceled */);
}
private void handleUserCanceled() {
+ mTryAgainShowing = false;
+ mConfirmShowing = false;
handleHideDialog(true /* userCanceled */);
}
+ private void handleShowTryAgain() {
+ mCurrentDialog.showTryAgainButton(true /* show */);
+ mTryAgainShowing = true;
+ }
+
+ private void handleTryAgainPressed() {
+ try {
+ mCurrentDialog.clearTemporaryMessage();
+ mTryAgainShowing = false;
+ mReceiver.onTryAgainPressed();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException when handling try again", e);
+ }
+ }
+
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index 38427adfd42a..e085f2368214 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -87,6 +87,9 @@ public abstract class BiometricDialogView extends LinearLayout {
protected boolean mRequireConfirmation;
private int mUserId; // used to determine if we should show work background
+ private boolean mPendingShowTryAgain;
+ private boolean mPendingShowConfirm;
+
protected abstract void updateIcon(int lastState, int newState);
protected abstract int getHintStringResourceId();
protected abstract int getAuthenticatedAccessibilityResourceId();
@@ -178,6 +181,7 @@ public abstract class BiometricDialogView extends LinearLayout {
final Button negative = mLayout.findViewById(R.id.button2);
final Button positive = mLayout.findViewById(R.id.button1);
final ImageView icon = mLayout.findViewById(R.id.biometric_icon);
+ final Button tryAgain = mLayout.findViewById(R.id.button_try_again);
icon.setContentDescription(getResources().getString(getIconDescriptionResourceId()));
@@ -193,6 +197,11 @@ public abstract class BiometricDialogView extends LinearLayout {
mCallback.onPositivePressed();
});
+ tryAgain.setOnClickListener((View v) -> {
+ showTryAgainButton(false /* show */);
+ mCallback.onTryAgainPressed();
+ });
+
mLayout.setFocusableInTouchMode(true);
mLayout.requestFocus();
}
@@ -207,7 +216,6 @@ public abstract class BiometricDialogView extends LinearLayout {
final TextView subtitle = mLayout.findViewById(R.id.subtitle);
final TextView description = mLayout.findViewById(R.id.description);
final Button negative = mLayout.findViewById(R.id.button2);
- final Button positive = mLayout.findViewById(R.id.button1);
final ImageView backgroundView = mLayout.findViewById(R.id.background);
if (mUserManager.isManagedProfile(mUserId)) {
@@ -233,8 +241,6 @@ public abstract class BiometricDialogView extends LinearLayout {
title.setText(titleText);
title.setSelected(true);
- positive.setVisibility(View.INVISIBLE);
-
final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
if (TextUtils.isEmpty(subtitleText)) {
subtitle.setVisibility(View.GONE);
@@ -243,7 +249,8 @@ public abstract class BiometricDialogView extends LinearLayout {
subtitle.setText(subtitleText);
}
- final CharSequence descriptionText = mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
+ final CharSequence descriptionText =
+ mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
if (TextUtils.isEmpty(descriptionText)) {
description.setVisibility(View.GONE);
} else {
@@ -253,6 +260,9 @@ public abstract class BiometricDialogView extends LinearLayout {
negative.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
+ showTryAgainButton(mPendingShowTryAgain);
+ showConfirmationButton(mPendingShowConfirm);
+
if (mWasForceRemoved || mSkipIntro) {
// Show the dialog immediately
mLayout.animate().cancel();
@@ -281,11 +291,17 @@ public abstract class BiometricDialogView extends LinearLayout {
public void startDismiss() {
mAnimatingAway = true;
+ // This is where final cleanup should occur.
final Runnable endActionRunnable = new Runnable() {
@Override
public void run() {
mWindowManager.removeView(BiometricDialogView.this);
mAnimatingAway = false;
+ // Set the icons / text back to normal state
+ handleClearMessage();
+ showTryAgainButton(false /* show */);
+ mPendingShowTryAgain = false;
+ mPendingShowConfirm = false;
}
};
@@ -345,9 +361,13 @@ public abstract class BiometricDialogView extends LinearLayout {
return mRequireConfirmation;
}
- public void showConfirmationButton() {
+ public void showConfirmationButton(boolean show) {
final Button positive = mLayout.findViewById(R.id.button1);
- positive.setVisibility(View.VISIBLE);
+ if (show) {
+ positive.setVisibility(View.VISIBLE);
+ } else {
+ positive.setVisibility(View.GONE);
+ }
}
public void setUserId(int userId) {
@@ -376,12 +396,18 @@ public abstract class BiometricDialogView extends LinearLayout {
BiometricPrompt.HIDE_DIALOG_DELAY);
}
+ public void clearTemporaryMessage() {
+ mHandler.removeMessages(MSG_CLEAR_MESSAGE);
+ mHandler.obtainMessage(MSG_CLEAR_MESSAGE).sendToTarget();
+ }
+
public void showHelpMessage(String message) {
showTemporaryMessage(message);
}
public void showErrorMessage(String error) {
showTemporaryMessage(error);
+ showTryAgainButton(false /* show */);
mCallback.onErrorShown();
}
@@ -390,6 +416,25 @@ public abstract class BiometricDialogView extends LinearLayout {
mLastState = newState;
}
+ public void showTryAgainButton(boolean show) {
+ final Button tryAgain = mLayout.findViewById(R.id.button_try_again);
+ if (show) {
+ tryAgain.setVisibility(View.VISIBLE);
+ } else {
+ tryAgain.setVisibility(View.GONE);
+ }
+ }
+
+ // Set the state before the window is attached, so we know if the dialog should be started
+ // with or without the button. This is because there's no good onPause signal
+ public void setPendingTryAgain(boolean show) {
+ mPendingShowTryAgain = show;
+ }
+
+ public void setPendingConfirm(boolean show) {
+ mPendingShowConfirm = show;
+ }
+
public WindowManager.LayoutParams getLayoutParams() {
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
index f388d9c67718..24fd22e2ee80 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
@@ -43,4 +43,9 @@ public interface DialogViewCallback {
* should be dismissed.
*/
void onPositivePressed();
+
+ /**
+ * Invoked when the "try again" button is pressed.
+ */
+ void onTryAgainPressed();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 591e9e015897..9d2be39b28fc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -26,6 +26,7 @@ import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
+import android.provider.Settings;
import android.service.quicksettings.TileService;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -69,7 +70,8 @@ public class TileQueryHelper {
mSpecs.clear();
mFinished = false;
// Enqueue jobs to fetch every system tile and then ever package tile.
- addStockTiles(host);
+ addCurrentAndStockTiles(host);
+
addPackageTiles(host);
}
@@ -77,16 +79,28 @@ public class TileQueryHelper {
return mFinished;
}
- private void addStockTiles(QSTileHost host) {
- String possible = mContext.getString(R.string.quick_settings_tiles_stock);
+ private void addCurrentAndStockTiles(QSTileHost host) {
+ String stock = mContext.getString(R.string.quick_settings_tiles_stock);
+ String current = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.QS_TILES);
final ArrayList<String> possibleTiles = new ArrayList<>();
- possibleTiles.addAll(Arrays.asList(possible.split(",")));
- if (Build.IS_DEBUGGABLE) {
+ if (current != null) {
+ // The setting QS_TILES is not populated immediately upon Factory Reset
+ possibleTiles.addAll(Arrays.asList(current.split(",")));
+ }
+ String[] stockSplit = stock.split(",");
+ for (String spec : stockSplit) {
+ if (!current.contains(spec)) {
+ possibleTiles.add(spec);
+ }
+ }
+ if (Build.IS_DEBUGGABLE && !current.contains(GarbageMonitor.MemoryTile.TILE_SPEC)) {
possibleTiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);
}
final ArrayList<QSTile> tilesToAdd = new ArrayList<>();
for (String spec : possibleTiles) {
+ // Only add current and stock tiles that can be created from QSFactoryImpl
final QSTile tile = host.createTile(spec);
if (tile == null) {
continue;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index b2f60436fecd..7d52f0b2763d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -114,7 +114,7 @@ public class QSFactoryImpl implements QSFactory {
}
// Broken tiles.
- Log.w(TAG, "Bad tile spec: " + tileSpec);
+ Log.w(TAG, "No stock tile spec: " + tileSpec);
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index c62a592be8e2..3ab1c21b5066 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -205,7 +205,10 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
} else {
final BluetoothClass bluetoothClass = lastDevice.getBtClass();
if (bluetoothClass != null) {
- if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
+ if (lastDevice.isHearingAidDevice()) {
+ return mContext.getString(
+ R.string.quick_settings_bluetooth_secondary_label_hearing_aids);
+ } else if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
return mContext.getString(
R.string.quick_settings_bluetooth_secondary_label_audio);
} else if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 1b89324209de..12b6f673de1c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -43,6 +43,7 @@ import android.provider.Settings;
import android.util.Log;
import android.view.MotionEvent;
+import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.Prefs;
@@ -97,6 +98,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
private int mCurrentBoundedUserId = -1;
private float mBackButtonAlpha;
private MotionEvent mStatusBarGestureDownEvent;
+ private float mWindowCornerRadius;
private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
@@ -228,6 +230,18 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
}
+ public float getWindowCornerRadius() {
+ if (!verifyCaller("getWindowCornerRadius")) {
+ return 0;
+ }
+ long token = Binder.clearCallingIdentity();
+ try {
+ return mWindowCornerRadius;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
@@ -334,6 +348,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
.setPackage(mRecentsComponentName.getPackageName());
mInteractionFlags = Prefs.getInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS,
getDefaultInteractionFlags());
+ mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources());
// Listen for the package update changes.
if (mDeviceProvisionedController.getCurrentUser() == UserHandle.USER_SYSTEM) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 0c8f48752455..8b9399536969 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -21,7 +21,7 @@ import static com.android.systemui.statusbar.phone.StatusBar.ONLY_CORE_APPS;
import android.app.StatusBarManager;
import android.content.ComponentName;
import android.graphics.Rect;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -96,6 +96,7 @@ public class CommandQueue extends IStatusBar.Stub {
private static final int MSG_SHOW_CHARGING_ANIMATION = 44 << MSG_SHIFT;
private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
private static final int MSG_SHOW_PINNING_TOAST_ESCAPE = 46 << MSG_SHIFT;
+ private static final int MSG_BIOMETRIC_TRY_AGAIN = 47 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -163,12 +164,13 @@ public class CommandQueue extends IStatusBar.Stub {
default void onRotationProposal(int rotation, boolean isValid) { }
- default void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver,
+ default void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
int type, boolean requireConfirmation, int userId) { }
default void onBiometricAuthenticated() { }
default void onBiometricHelp(String message) { }
default void onBiometricError(String error) { }
default void hideBiometricDialog() { }
+ default void showBiometricTryAgain() { }
}
@VisibleForTesting
@@ -523,8 +525,8 @@ public class CommandQueue extends IStatusBar.Stub {
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
- boolean requireConfirmation, int userId) {
+ public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
+ int type, boolean requireConfirmation, int userId) {
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
@@ -565,6 +567,13 @@ public class CommandQueue extends IStatusBar.Stub {
}
}
+ @Override
+ public void showBiometricTryAgain() {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_BIOMETRIC_TRY_AGAIN).sendToTarget();
+ }
+ }
+
private final class H extends Handler {
private H(Looper l) {
super(l);
@@ -774,7 +783,7 @@ public class CommandQueue extends IStatusBar.Stub {
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).showBiometricDialog(
(Bundle) someArgs.arg1,
- (IBiometricPromptReceiver) someArgs.arg2,
+ (IBiometricServiceReceiverInternal) someArgs.arg2,
someArgs.argi1 /* type */,
(boolean) someArgs.arg3 /* requireConfirmation */,
someArgs.argi2 /* userId */);
@@ -816,6 +825,11 @@ public class CommandQueue extends IStatusBar.Stub {
mCallbacks.get(i).showPinningEscapeToast();
}
break;
+ case MSG_BIOMETRIC_TRY_AGAIN:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).showBiometricTryAgain();
+ }
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index f0e5462bc167..f506753379a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -29,6 +29,7 @@ import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
+import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.shared.system.SurfaceControlCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
@@ -55,6 +56,7 @@ public class ActivityLaunchAnimator {
private final NotificationPanelView mNotificationPanel;
private final NotificationListContainer mNotificationContainer;
private final StatusBarWindowView mStatusBarWindow;
+ private final float mWindowCornerRadius;
private Callback mCallback;
private final Runnable mTimeoutRunnable = () -> {
setAnimationPending(false);
@@ -72,6 +74,8 @@ public class ActivityLaunchAnimator {
mNotificationContainer = container;
mStatusBarWindow = statusBarWindow;
mCallback = callback;
+ mWindowCornerRadius = ScreenDecorationsUtils
+ .getWindowCornerRadius(statusBarWindow.getResources());
}
public RemoteAnimationAdapter getLaunchAnimation(
@@ -124,6 +128,8 @@ public class ActivityLaunchAnimator {
private final ExpandableNotificationRow mSourceNotification;
private final ExpandAnimationParameters mParams;
private final Rect mWindowCrop = new Rect();
+ private final float mNotificationCornerRadius;
+ private float mCornerRadius;
private boolean mIsFullScreenLaunch = true;
private final SyncRtSurfaceTransactionApplier mSyncRtTransactionApplier;
@@ -131,6 +137,8 @@ public class ActivityLaunchAnimator {
mSourceNotification = sourceNofitication;
mParams = new ExpandAnimationParameters();
mSyncRtTransactionApplier = new SyncRtSurfaceTransactionApplier(mSourceNotification);
+ mNotificationCornerRadius = Math.max(mSourceNotification.getCurrentTopRoundness(),
+ mSourceNotification.getCurrentBottomRoundness());
}
@Override
@@ -181,8 +189,7 @@ public class ActivityLaunchAnimator {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mParams.linearProgress = animation.getAnimatedFraction();
- float progress
- = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
+ float progress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
mParams.linearProgress);
int newWidth = (int) MathUtils.lerp(notificationWidth,
targetWidth, progress);
@@ -194,6 +201,8 @@ public class ActivityLaunchAnimator {
+ notificationHeight,
primary.position.y + primary.sourceContainerBounds.bottom,
progress);
+ mCornerRadius = MathUtils.lerp(mNotificationCornerRadius,
+ mWindowCornerRadius, progress);
applyParamsToWindow(primary);
applyParamsToNotification(mParams);
applyParamsToNotificationList(mParams);
@@ -259,7 +268,7 @@ public class ActivityLaunchAnimator {
m.postTranslate(0, (float) (mParams.top - app.position.y));
mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());
SurfaceParams params = new SurfaceParams(new SurfaceControlCompat(app.leash),
- 1f /* alpha */, m, mWindowCrop, app.prefixOrderIndex);
+ 1f /* alpha */, m, mWindowCrop, app.prefixOrderIndex, mCornerRadius);
mSyncRtTransactionApplier.scheduleApply(params);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 713bd90b30c3..fef28cf1393e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -375,12 +375,9 @@ public class StackStateAnimator {
// Find the amount to translate up. This is needed in order to understand the
// direction of the remove animation (either downwards or upwards)
- ExpandableViewState viewState =
- ((ExpandableView) event.viewAfterChangingView).getViewState();
- int actualHeight = changingView.getActualHeight();
// upwards by default
float translationDirection = -1.0f;
- if (viewState != null) {
+ if (event.viewAfterChangingView != null) {
float ownPosition = changingView.getTranslationY();
if (changingView instanceof ExpandableNotificationRow
&& event.viewAfterChangingView instanceof ExpandableNotificationRow) {
@@ -396,8 +393,11 @@ public class StackStateAnimator {
ownPosition = changingRow.getTranslationWhenRemoved();
}
}
+ int actualHeight = changingView.getActualHeight();
// there was a view after this one, Approximate the distance the next child
// travelled
+ ExpandableViewState viewState =
+ ((ExpandableView) event.viewAfterChangingView).getViewState();
translationDirection = ((viewState.yTranslation
- (ownPosition + actualHeight / 2.0f)) * 2 /
actualHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 33d022c83c16..cd6e1d794428 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB;
import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
@@ -950,10 +951,10 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
private void updateTaskSwitchHelper() {
if (mGestureHelper == null) return;
boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
- int navBarPos = 0;
+ int navBarPos = NAV_BAR_INVALID;
try {
- // TODO: Use WindowManagerService.getNavBarPosition(int displayId)
- navBarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
+ navBarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition(
+ mDisplay.getDisplayId());
} catch (RemoteException e) {
Slog.e(TAG, "Failed to get nav bar position.", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 497fdfb2deb1..43e86d66b8be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
@@ -421,6 +422,9 @@ public class QuickStepController implements GestureHelper {
mDragHPositive = !isRTL;
mDragVPositive = true;
break;
+ case NAV_BAR_INVALID:
+ Log.e(TAG, "Invalid nav bar position");
+ break;
}
for (NavigationGestureAction action: mGestureActions) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 408ab42a6f06..cfd53be8979b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1211,7 +1211,8 @@ public class StatusBar extends SystemUI implements DemoMode,
}
int dockSide = WindowManagerProxy.getInstance().getDockSide();
if (dockSide == WindowManager.DOCKED_INVALID) {
- final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition();
+ final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(
+ mDisplay.getDisplayId());
if (navbarPos == NAV_BAR_POS_INVALID) {
return false;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index f63d2360d976..26fa20de4e86 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -14,41 +14,76 @@
package com.android.systemui.qs.customize;
-import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import android.text.TextUtils;
+import android.util.ArraySet;
import com.android.systemui.Dependency;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSTileHost;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class TileQueryHelperTest extends SysuiTestCase {
- @Mock private TileQueryHelper.TileStateListener mListener;
- @Mock private QSTileHost mQSTileHost;
+ private static final String CURRENT_TILES = "wifi,dnd,nfc";
+ private static final String ONLY_STOCK_TILES = "wifi,dnd";
+ private static final String WITH_OTHER_TILES = "wifi,dnd,other";
+ // Note no nfc in stock tiles
+ private static final String STOCK_TILES = "wifi,dnd,cell,battery";
+ private static final String ALL_TILES = "wifi,dnd,nfc,cell,battery";
+ private static final Set<String> FACTORY_TILES = new ArraySet<>();
+
+ static {
+ FACTORY_TILES.addAll(Arrays.asList(
+ new String[]{"wifi", "bt", "cell", "dnd", "inversion", "airplane", "work",
+ "rotation", "flashlight", "location", "cast", "hotspot", "user", "battery",
+ "saver", "night", "nfc"}));
+ }
- private TestableLooper mBGLooper;
+ @Mock
+ private TileQueryHelper.TileStateListener mListener;
+ @Mock
+ private QSTileHost mQSTileHost;
+ @Mock
+ private PackageManager mPackageManager;
+ private QSTile.State mState;
+ private TestableLooper mBGLooper;
private TileQueryHelper mTileQueryHelper;
@Before
@@ -56,6 +91,23 @@ public class TileQueryHelperTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mBGLooper = TestableLooper.get(this);
mDependency.injectTestDependency(Dependency.BG_LOOPER, mBGLooper.getLooper());
+ mContext.setMockPackageManager(mPackageManager);
+
+ mState = new QSTile.State();
+ doAnswer(invocation -> {
+ String spec = (String) invocation.getArguments()[0];
+ if (FACTORY_TILES.contains(spec)) {
+ QSTile m = mock(QSTile.class);
+ when(m.isAvailable()).thenReturn(true);
+ when(m.getTileSpec()).thenReturn(spec);
+ when(m.getState()).thenReturn(mState);
+ return m;
+ } else {
+ return null;
+ }
+ }
+ ).when(mQSTileHost).createTile(anyString());
+
mTileQueryHelper = new TileQueryHelper(mContext, mListener);
}
@@ -98,4 +150,72 @@ public class TileQueryHelperTest extends SysuiTestCase {
assertTrue(mTileQueryHelper.isFinished());
}
+
+ @Test
+ public void testQueryTiles_correctTilesAndOrderOnlyStockTiles() {
+ ArgumentCaptor<List<TileQueryHelper.TileInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+ Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES,
+ ONLY_STOCK_TILES);
+ mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
+ STOCK_TILES);
+
+ mTileQueryHelper.queryTiles(mQSTileHost);
+
+ mBGLooper.processAllMessages();
+ waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER));
+
+ verify(mListener, atLeastOnce()).onTilesChanged(captor.capture());
+ List<String> specs = new ArrayList<>();
+ for (TileQueryHelper.TileInfo t : captor.getValue()) {
+ specs.add(t.spec);
+ }
+ String tiles = TextUtils.join(",", specs);
+ assertThat(tiles, is(equalTo(STOCK_TILES)));
+ }
+
+ @Test
+ public void testQueryTiles_correctTilesAndOrderOtherFactoryTiles() {
+ ArgumentCaptor<List<TileQueryHelper.TileInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+ Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES,
+ CURRENT_TILES);
+ mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
+ STOCK_TILES);
+
+ mTileQueryHelper.queryTiles(mQSTileHost);
+
+ mBGLooper.processAllMessages();
+ waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER));
+
+ verify(mListener, atLeastOnce()).onTilesChanged(captor.capture());
+ List<String> specs = new ArrayList<>();
+ for (TileQueryHelper.TileInfo t : captor.getValue()) {
+ specs.add(t.spec);
+ }
+ String tiles = TextUtils.join(",", specs);
+ assertThat(tiles, is(equalTo(ALL_TILES)));
+ }
+
+ @Test
+ public void testQueryTiles_otherTileNotIncluded() {
+ ArgumentCaptor<List<TileQueryHelper.TileInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+ Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES,
+ WITH_OTHER_TILES);
+ mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
+ STOCK_TILES);
+
+ mTileQueryHelper.queryTiles(mQSTileHost);
+
+ mBGLooper.processAllMessages();
+ waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER));
+
+ verify(mListener, atLeastOnce()).onTilesChanged(captor.capture());
+ List<String> specs = new ArrayList<>();
+ for (TileQueryHelper.TileInfo t : captor.getValue()) {
+ specs.add(t.spec);
+ }
+ assertFalse(specs.contains("other"));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
index 435ede4d8ff0..55583931af43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
@@ -62,6 +62,7 @@ public class ActivityLaunchAnimatorTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
+ when(mStatusBarWindowView.getResources()).thenReturn(mContext.getResources());
when(mCallback.areLaunchAnimationsEnabled()).thenReturn(true);
mLaunchAnimator = new ActivityLaunchAnimator(
mStatusBarWindowView,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 21047217e942..59a49378e9e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone;
import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.StatusBarManager;
@@ -63,9 +64,11 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mContext.putComponent(CommandQueue.class, mCommandQueue);
mDependency.injectTestDependency(ShadeController.class, mShadeController);
+ StatusBarWindowView statusBarWindowView = mock(StatusBarWindowView.class);
+ when(statusBarWindowView.getResources()).thenReturn(mContext.getResources());
mStatusBar = new StatusBarNotificationPresenter(mContext,
mock(NotificationPanelView.class), mock(HeadsUpManagerPhone.class),
- mock(StatusBarWindowView.class), mock(NotificationListContainerViewGroup.class),
+ statusBarWindowView, mock(NotificationListContainerViewGroup.class),
mock(DozeScrimController.class), mock(ScrimController.class),
mock(ActivityLaunchAnimator.Callback.class));
}
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 356a4daba66e..8d912fadf6d1 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -692,7 +692,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
});
- if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (!StorageManager.hasIsolatedStorage()) {
StorageManagerInternal storageManagerInternal = LocalServices.getService(
StorageManagerInternal.class);
storageManagerInternal.addExternalStoragePolicy(
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index dd960751ab21..98203213e996 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -56,6 +56,7 @@ public class BinderCallsStatsService extends Binder {
private static final String SETTINGS_DETAILED_TRACKING_KEY = "detailed_tracking";
private static final String SETTINGS_UPLOAD_DATA_KEY = "upload_data";
private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
+ private static final String SETTINGS_MAX_CALL_STATS_KEY = "max_call_stats_count";
private boolean mEnabled;
private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS);
@@ -97,6 +98,9 @@ public class BinderCallsStatsService extends Binder {
mBinderCallsStats.setSamplingInterval(mParser.getInt(
SETTINGS_SAMPLING_INTERVAL_KEY,
BinderCallsStats.PERIODIC_SAMPLING_INTERVAL_DEFAULT));
+ mBinderCallsStats.setSamplingInterval(mParser.getInt(
+ SETTINGS_MAX_CALL_STATS_KEY,
+ BinderCallsStats.MAX_BINDER_CALL_STATS_COUNT_DEFAULT));
final boolean enabled =
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 0e6f8dda44f6..e933bd0bc7ff 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -183,8 +183,7 @@ class StorageManagerService extends IStorageManager.Stub
private static final String ZRAM_ENABLED_PROPERTY =
"persist.sys.zram_enabled";
- private static final boolean ENABLE_ISOLATED_STORAGE = SystemProperties
- .getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false);
+ private static final boolean ENABLE_ISOLATED_STORAGE = StorageManager.hasIsolatedStorage();
public static class Lifecycle extends SystemService {
private StorageManagerService mStorageManagerService;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 80f47d5a000b..0e354d515eef 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -328,6 +328,7 @@ import com.android.server.AlarmManagerInternal;
import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
import com.android.server.DeviceIdleController;
+import com.android.server.DisplayThread;
import com.android.server.IntentResolver;
import com.android.server.IoThread;
import com.android.server.LocalServices;
@@ -2262,7 +2263,8 @@ public class ActivityManagerService extends IActivityManager.Stub
mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
mActivityTaskManager = atm;
- mActivityTaskManager.setActivityManagerService(mIntentFirewall, mPendingIntentController);
+ mActivityTaskManager.initialize(mIntentFirewall, mPendingIntentController,
+ DisplayThread.get().getLooper());
mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mProcessCpuThread = new Thread("CpuTracker") {
@@ -19346,7 +19348,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public boolean isAppStorageSandboxed(int pid, int uid) {
- if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (!StorageManager.hasIsolatedStorage()) {
return false;
}
if (uid == SHELL_UID || uid == ROOT_UID) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index a0977be0e25d..8c39d75ea6a4 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -58,8 +58,9 @@ import java.util.Set;
/**
* BROADCASTS
*
- * We keep two broadcast queues and associated bookkeeping, one for those at
- * foreground priority, and one for normal (background-priority) broadcasts.
+ * We keep three broadcast queues and associated bookkeeping, one for those at
+ * foreground priority, and one for normal (background-priority) broadcasts, and one to
+ * offload special broadcasts that we know take a long time, such as BOOT_COMPLETED.
*/
public final class BroadcastQueue {
private static final String TAG = "BroadcastQueue";
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 4b19398cc73d..7991783e08c8 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -27,7 +27,6 @@ import static android.os.Process.getFreeMemory;
import static android.os.Process.getTotalMemory;
import static android.os.Process.killProcessQuiet;
import static android.os.Process.startWebView;
-import static android.os.storage.StorageManager.PROP_ISOLATED_STORAGE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
@@ -73,6 +72,7 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
+import android.os.storage.StorageManager;
import android.os.storage.StorageManagerInternal;
import android.text.TextUtils;
import android.util.EventLog;
@@ -1281,8 +1281,7 @@ public final class ProcessList {
final IPackageManager pm = AppGlobals.getPackageManager();
permGids = pm.getPackageGids(app.info.packageName,
MATCH_DIRECT_BOOT_AUTO, app.userId);
- if (SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false)
- && mountExtStorageFull) {
+ if (StorageManager.hasIsolatedStorage() && mountExtStorageFull) {
mountExternal = Zygote.MOUNT_EXTERNAL_FULL;
} else {
StorageManagerInternal storageManagerInternal = LocalServices.getService(
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index 2c2d4045b205..eaa7a83fab0d 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -19,19 +19,11 @@ package com.android.server.biometrics;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.BiometricPrompt;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.os.Bundle;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.security.KeyStore;
-import android.text.TextUtils;
import android.util.Slog;
-import com.android.internal.statusbar.IStatusBarService;
-
import java.util.ArrayList;
/**
@@ -39,88 +31,15 @@ import java.util.ArrayList;
*/
public abstract class AuthenticationClient extends ClientMonitor {
private long mOpId;
- private Handler mHandler;
public abstract int handleFailedAttempt();
public abstract void resetFailedAttempts();
- public abstract String getErrorString(int error, int vendorCode);
- public abstract String getAcquiredString(int acquireInfo, int vendorCode);
- /**
- * @return one of {@link #TYPE_FINGERPRINT} {@link #TYPE_IRIS} or {@link #TYPE_FACE}
- */
- public abstract int getBiometricType();
public static final int LOCKOUT_NONE = 0;
public static final int LOCKOUT_TIMED = 1;
public static final int LOCKOUT_PERMANENT = 2;
private final boolean mRequireConfirmation;
- // Callback mechanism received from the client
- // (BiometricPrompt -> BiometricPromptService -> <Biometric>Service -> AuthenticationClient)
- private IBiometricPromptReceiver mDialogReceiverFromClient;
- private Bundle mBundle;
- private IStatusBarService mStatusBarService;
- private boolean mInLockout;
- private TokenEscrow mEscrow;
- protected boolean mDialogDismissed;
-
- /**
- * Container that holds the identifier and authToken. For biometrics that require user
- * confirmation, these should not be sent to their final destinations until the user confirms.
- */
- class TokenEscrow {
- final BiometricAuthenticator.Identifier mIdentifier;
- final ArrayList<Byte> mToken;
-
- TokenEscrow(BiometricAuthenticator.Identifier identifier, ArrayList<Byte> token) {
- mIdentifier = identifier;
- mToken = token;
- }
-
- BiometricAuthenticator.Identifier getIdentifier() {
- return mIdentifier;
- }
-
- ArrayList<Byte> getToken() {
- return mToken;
- }
- }
-
- // Receives events from SystemUI and handles them before forwarding them to BiometricDialog
- protected IBiometricPromptReceiver mDialogReceiver = new IBiometricPromptReceiver.Stub() {
- @Override // binder call
- public void onDialogDismissed(int reason) {
- if (mBundle != null && mDialogReceiverFromClient != null) {
- try {
- if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
- // Positive button is used by passive modalities as a "confirm" button,
- // do not send to client
- mDialogReceiverFromClient.onDialogDismissed(reason);
- }
- if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
- onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
- 0 /* vendorCode */);
- } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
- // Have the service send the token to KeyStore, and send onAuthenticated
- // to the application.
- if (mEscrow != null) {
- if (DEBUG) Slog.d(getLogTag(), "Confirmed");
- addTokenToKeyStore(mEscrow.getToken());
- notifyClientAuthenticationSucceeded(mEscrow.getIdentifier());
- mEscrow = null;
- onAuthenticationConfirmed();
- } else {
- Slog.e(getLogTag(), "Escrow is null!!!");
- }
- }
- mDialogDismissed = true;
- } catch (RemoteException e) {
- Slog.e(getLogTag(), "Remote exception", e);
- }
- stop(true /* initiatedByClient */);
- }
- }
- };
/**
* This method is called when authentication starts.
@@ -133,25 +52,13 @@ public abstract class AuthenticationClient extends ClientMonitor {
*/
public abstract void onStop();
- /**
- * This method is called when biometric authentication was confirmed by the user. The client
- * should be removed.
- */
- public abstract void onAuthenticationConfirmed();
-
public AuthenticationClient(Context context, Metrics metrics,
BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId,
- boolean restricted, String owner, Bundle bundle,
- IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
- boolean requireConfirmation) {
+ boolean restricted, String owner, int cookie, boolean requireConfirmation) {
super(context, metrics, daemon, halDeviceId, token, listener, targetUserId, groupId,
- restricted, owner);
+ restricted, owner, cookie);
mOpId = opId;
- mBundle = bundle;
- mDialogReceiverFromClient = dialogReceiver;
- mStatusBarService = statusBarService;
- mHandler = new Handler(Looper.getMainLooper());
mRequireConfirmation = requireConfirmation;
}
@@ -164,175 +71,99 @@ public abstract class AuthenticationClient extends ClientMonitor {
stop(false /* initiatedByClient */);
}
- @Override
- public boolean onAcquired(int acquiredInfo, int vendorCode) {
- // If the dialog is showing, the client doesn't need to receive onAcquired messages.
- if (mBundle != null) {
- try {
- if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
- mStatusBarService.onBiometricHelp(getAcquiredString(acquiredInfo, vendorCode));
- }
- return false; // acquisition continues
- } catch (RemoteException e) {
- Slog.e(getLogTag(), "Remote exception when sending acquired message", e);
- return true; // client failed
- } finally {
- // Good scans will keep the device awake
- if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
- notifyUserActivity();
- }
- }
- } else {
- return super.onAcquired(acquiredInfo, vendorCode);
- }
- }
-
- @Override
- public boolean onError(long deviceId, int error, int vendorCode) {
- if (mDialogDismissed) {
- // If user cancels authentication, the application has already received the
- // ERROR_USER_CANCELED message from onDialogDismissed()
- // and stopped the biometric hardware, so there is no need to send a
- // ERROR_CANCELED message.
- return true;
- }
- if (mBundle != null && error != BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED) {
- try {
- mStatusBarService.onBiometricError(getErrorString(error, vendorCode));
- } catch (RemoteException e) {
- Slog.e(getLogTag(), "Remote exception when sending error", e);
- }
- }
- return super.onError(deviceId, error, vendorCode);
- }
-
- public void setTitleIfEmpty(CharSequence title) {
- if (TextUtils.isEmpty(mBundle.getCharSequence(BiometricPrompt.KEY_TITLE))) {
- mBundle.putCharSequence(BiometricPrompt.KEY_TITLE, title);
- }
- }
-
public boolean isBiometricPrompt() {
- return mBundle != null;
- }
-
- private void notifyClientAuthenticationSucceeded(BiometricAuthenticator.Identifier identifier)
- throws RemoteException {
- final BiometricServiceBase.ServiceListener listener = getListener();
- // Explicitly have if/else here to make it super obvious in case the code is
- // touched in the future.
- if (!getIsRestricted()) {
- listener.onAuthenticationSucceeded(
- getHalDeviceId(), identifier, getTargetUserId());
- } else {
- listener.onAuthenticationSucceeded(
- getHalDeviceId(), null, getTargetUserId());
- }
+ return getCookie() != 0;
}
- private void addTokenToKeyStore(ArrayList<Byte> token) {
- // Send the token to KeyStore
- final byte[] byteToken = new byte[token.size()];
- for (int i = 0; i < token.size(); i++) {
- byteToken[i] = token.get(i);
- }
- KeyStore.getInstance().addAuthToken(byteToken);
+ public boolean getRequireConfirmation() {
+ return mRequireConfirmation;
}
@Override
public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
boolean authenticated, ArrayList<Byte> token) {
- if (authenticated) {
- mAlreadyDone = true;
- if (mRequireConfirmation) {
- // Store the token so it can be sent to keystore after the user presses confirm
- mEscrow = new TokenEscrow(identifier, token);
- } else {
- addTokenToKeyStore(token);
- }
- }
+ final BiometricServiceBase.ServiceListener listener = getListener();
+ mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated);
boolean result = false;
- // If the biometric dialog is showing, notify authentication succeeded
- if (mBundle != null) {
- try {
- if (authenticated) {
- mStatusBarService.onBiometricAuthenticated();
- } else {
- mStatusBarService.onBiometricHelp(getContext().getResources().getString(
- com.android.internal.R.string.biometric_not_recognized));
+ try {
+ if (authenticated) {
+ mAlreadyDone = true;
+ if (DEBUG) Slog.v(getLogTag(), "onAuthenticated(" + getOwnerString()
+ + ", ID:" + identifier.getBiometricId()
+ + ", isBP: " + isBiometricPrompt()
+ + ", listener: " + listener
+ + ", requireConfirmation: " + mRequireConfirmation);
+ if (listener != null) {
+ vibrateSuccess();
}
- } catch (RemoteException e) {
- Slog.e(getLogTag(), "Failed to notify Authenticated:", e);
- }
- }
+ result = true;
+ resetFailedAttempts();
+ onStop();
- final BiometricServiceBase.ServiceListener listener = getListener();
- if (listener != null) {
- try {
- mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated);
- if (!authenticated) {
- listener.onAuthenticationFailed(getHalDeviceId());
- } else {
- if (DEBUG) {
- Slog.v(getLogTag(), "onAuthenticated(owner=" + getOwnerString()
- + ", id=" + identifier.getBiometricId());
- }
- if (!mRequireConfirmation) {
- notifyClientAuthenticationSucceeded(identifier);
+ final byte[] byteToken = new byte[token.size()];
+ for (int i = 0; i < token.size(); i++) {
+ byteToken[i] = token.get(i);
+ }
+ if (isBiometricPrompt() && listener != null) {
+ // BiometricService will add the token to keystore
+ listener.onAuthenticationSucceededInternal(mRequireConfirmation, byteToken);
+ } else if (!isBiometricPrompt() && listener != null) {
+ KeyStore.getInstance().addAuthToken(byteToken);
+ try {
+ // Explicitly have if/else here to make it super obvious in case the code is
+ // touched in the future.
+ if (!getIsRestricted()) {
+ listener.onAuthenticationSucceeded(
+ getHalDeviceId(), identifier, getTargetUserId());
+ } else {
+ listener.onAuthenticationSucceeded(
+ getHalDeviceId(), null, getTargetUserId());
+ }
+ } catch (RemoteException e) {
+ Slog.e(getLogTag(), "Remote exception", e);
}
+ } else {
+ // Client not listening
+ Slog.w(getLogTag(), "Client not listening");
+ result = true;
}
- } catch (RemoteException e) {
- Slog.w(getLogTag(), "Failed to notify Authenticated:", e);
- result = true; // client failed
- }
- } else {
- result = true; // client not listening
- }
- if (!authenticated) {
- if (listener != null) {
- vibrateError();
- }
- // allow system-defined limit of number of attempts before giving up
- int lockoutMode = handleFailedAttempt();
- if (lockoutMode != LOCKOUT_NONE) {
- try {
- mInLockout = true;
- Slog.w(getLogTag(), "Forcing lockout (fp driver code should do this!), mode(" +
- lockoutMode + ")");
+ } else {
+ if (listener != null) {
+ vibrateError();
+ }
+ // Allow system-defined limit of number of attempts before giving up
+ final int lockoutMode = handleFailedAttempt();
+ if (lockoutMode != LOCKOUT_NONE) {
+ Slog.w(getLogTag(), "Forcing lockout (driver code should do this!), mode("
+ + lockoutMode + ")");
stop(false);
- int errorCode = lockoutMode == LOCKOUT_TIMED ?
- BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
- BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
-
- // Send the lockout message to the system dialog
- if (mBundle != null) {
- mStatusBarService.onBiometricError(
- getErrorString(errorCode, 0 /* vendorCode */));
- mHandler.postDelayed(() -> {
- try {
- listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
- } catch (RemoteException e) {
- Slog.w(getLogTag(), "RemoteException while sending error");
- }
- }, BiometricPrompt.HIDE_DIALOG_DELAY);
- } else {
- listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
+ final int errorCode = lockoutMode == LOCKOUT_TIMED
+ ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
+ : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+ if (listener != null) {
+ listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */,
+ getCookie());
+ }
+ } else {
+ // Don't send onAuthenticationFailed if we're in lockout, it causes a
+ // janky UI on Keyguard/BiometricPrompt since "authentication failed"
+ // will show briefly and be replaced by "device locked out" message.
+ if (listener != null) {
+ if (isBiometricPrompt()) {
+ listener.onAuthenticationFailedInternal(getCookie(),
+ getRequireConfirmation());
+ } else {
+ listener.onAuthenticationFailed(getHalDeviceId());
+ }
}
- } catch (RemoteException e) {
- Slog.w(getLogTag(), "Failed to notify lockout:", e);
}
+ result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
}
- result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
- } else {
- if (listener != null) {
- vibrateSuccess();
- }
- // we have a valid biometric that doesn't require confirmation, done
- result |= !mRequireConfirmation;
- resetFailedAttempts();
- onStop();
+ } catch (RemoteException e) {
+ Slog.e(getLogTag(), "Remote exception", e);
+ result = true;
}
return result;
}
@@ -353,16 +184,6 @@ public abstract class AuthenticationClient extends ClientMonitor {
return result;
}
if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is authenticating...");
-
- // If authenticating with system dialog, show the dialog
- if (mBundle != null) {
- try {
- mStatusBarService.showBiometricDialog(mBundle, mDialogReceiver,
- getBiometricType(), mRequireConfirmation, getTargetUserId());
- } catch (RemoteException e) {
- Slog.e(getLogTag(), "Unable to show biometric dialog", e);
- }
- }
} catch (RemoteException e) {
Slog.e(getLogTag(), "startAuthentication failed", e);
return ERROR_ESRCH;
@@ -390,18 +211,6 @@ public abstract class AuthenticationClient extends ClientMonitor {
} catch (RemoteException e) {
Slog.e(getLogTag(), "stopAuthentication failed", e);
return ERROR_ESRCH;
- } finally {
- // If the user already cancelled authentication (via some interaction with the
- // dialog, we do not need to hide it since it's already hidden.
- // If the device is in lockout, don't hide the dialog - it will automatically hide
- // after BiometricPrompt.HIDE_DIALOG_DELAY
- if (mBundle != null && !mDialogDismissed && !mInLockout) {
- try {
- mStatusBarService.hideBiometricDialog();
- } catch (RemoteException e) {
- Slog.e(getLogTag(), "Unable to hide biometric dialog", e);
- }
- }
}
mAlreadyCancelled = true;
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 5f09189bd84a..add55eaad166 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -19,9 +19,17 @@ package com.android.server.biometrics;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
+import android.app.IActivityTaskManager;
+import android.app.TaskStackListener;
import android.app.UserSwitchObserver;
import android.content.ContentResolver;
import android.content.Context;
@@ -32,9 +40,9 @@ import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
-import android.hardware.biometrics.IBiometricPromptReceiver;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.face.FaceManager;
import android.hardware.face.IFaceService;
import android.hardware.fingerprint.FingerprintManager;
@@ -50,14 +58,21 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
+import android.security.KeyStore;
+import android.text.TextUtils;
import android.util.Pair;
import android.util.Slog;
import com.android.internal.R;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.server.SystemService;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.Random;
/**
* System service that arbitrates the modality for BiometricPrompt to use.
@@ -66,32 +81,10 @@ public class BiometricService extends SystemService {
private static final String TAG = "BiometricService";
- /**
- * No biometric methods or nothing has been enrolled.
- * Move/expose these in BiometricPrompt if we ever want to allow applications to "blacklist"
- * modalities when calling authenticate().
- */
- private static final int BIOMETRIC_NONE = 0;
-
- /**
- * Constant representing fingerprint.
- */
- private static final int BIOMETRIC_FINGERPRINT = 1 << 0;
-
- /**
- * Constant representing iris.
- */
- private static final int BIOMETRIC_IRIS = 1 << 1;
-
- /**
- * Constant representing face.
- */
- private static final int BIOMETRIC_FACE = 1 << 2;
-
private static final int[] FEATURE_ID = {
- BIOMETRIC_FINGERPRINT,
- BIOMETRIC_IRIS,
- BIOMETRIC_FACE
+ TYPE_FINGERPRINT,
+ TYPE_IRIS,
+ TYPE_FACE
};
private final AppOpsManager mAppOps;
@@ -242,10 +235,367 @@ public class BiometricService extends SystemService {
*/
private final class BiometricServiceWrapper extends IBiometricService.Stub {
+ /**
+ * Authentication either just called and we have not transitioned to the CALLED state, or
+ * authentication terminated (success or error).
+ */
+ private static final int STATE_AUTH_IDLE = 0;
+ /**
+ * Authentication was called and we are waiting for the <Biometric>Services to return their
+ * cookies before starting the hardware and showing the BiometricPrompt.
+ */
+ private static final int STATE_AUTH_CALLED = 1;
+ /**
+ * Authentication started, BiometricPrompt is showing and the hardware is authenticating.
+ */
+ private static final int STATE_AUTH_STARTED = 2;
+ /**
+ * Authentication is paused, waiting for the user to press "try again" button. Since the
+ * try again button requires us to cancel authentication, this represents the state where
+ * ERROR_CANCELED is not received yet.
+ */
+ private static final int STATE_AUTH_PAUSED = 3;
+ /**
+ * Same as above, except the ERROR_CANCELED has been received.
+ */
+ private static final int STATE_AUTH_PAUSED_CANCELED = 4;
+ /**
+ * Authentication is successful, but we're waiting for the user to press "confirm" button.
+ */
+ private static final int STATE_AUTH_PENDING_CONFIRM = 5;
+
+ final class AuthSession {
+ // Map of Authenticator/Cookie pairs. We expect to receive the cookies back from
+ // <Biometric>Services before we can start authenticating. Pairs that have been returned
+ // are moved to mModalitiesMatched.
+ final HashMap<Integer, Integer> mModalitiesWaiting;
+ // Pairs that have been matched.
+ final HashMap<Integer, Integer> mModalitiesMatched = new HashMap<>();
+
+ // The following variables are passed to authenticateInternal, which initiates the
+ // appropriate <Biometric>Services.
+ final IBinder mToken;
+ final long mSessionId;
+ final int mUserId;
+ // Original receiver from BiometricPrompt.
+ final IBiometricServiceReceiver mClientReceiver;
+ final String mOpPackageName;
+ // Info to be shown on BiometricDialog when all cookies are returned.
+ final Bundle mBundle;
+ final int mCallingUid;
+ final int mCallingPid;
+ final int mCallingUserId;
+ // Continue authentication with the same modality/modalities after "try again" is
+ // pressed
+ final int mModality;
+
+ // The current state, which can be either idle, called, or started
+ private int mState = STATE_AUTH_IDLE;
+ // For explicit confirmation, do not send to keystore until the user has confirmed
+ // the authentication.
+ byte[] mTokenEscrow;
+
+ AuthSession(HashMap<Integer, Integer> modalities, IBinder token, long sessionId,
+ int userId, IBiometricServiceReceiver receiver, String opPackageName,
+ Bundle bundle, int callingUid, int callingPid, int callingUserId,
+ int modality) {
+ mModalitiesWaiting = modalities;
+ mToken = token;
+ mSessionId = sessionId;
+ mUserId = userId;
+ mClientReceiver = receiver;
+ mOpPackageName = opPackageName;
+ mBundle = bundle;
+ mCallingUid = callingUid;
+ mCallingPid = callingPid;
+ mCallingUserId = callingUserId;
+ mModality = modality;
+ }
+
+ boolean containsCookie(int cookie) {
+ if (mModalitiesWaiting != null && mModalitiesWaiting.containsValue(cookie)) {
+ return true;
+ }
+ if (mModalitiesMatched != null && mModalitiesMatched.containsValue(cookie)) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ final class BiometricTaskStackListener extends TaskStackListener {
+ @Override
+ public void onTaskStackChanged() {
+ try {
+ final List<ActivityManager.RunningTaskInfo> runningTasks =
+ mActivityTaskManager.getTasks(1);
+ if (!runningTasks.isEmpty()) {
+ final String topPackage = runningTasks.get(0).topActivity.getPackageName();
+ if (mCurrentAuthSession != null
+ && !topPackage.contentEquals(mCurrentAuthSession.mOpPackageName)
+ && mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
+ // We only care about this state, since <Biometric>Service will
+ // cancel any client that's still in STATE_AUTH_STARTED
+ mStatusBarService.hideBiometricDialog();
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ mCurrentAuthSession.mClientReceiver.onError(
+ BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+ getContext().getString(
+ com.android.internal.R.string.biometric_error_canceled)
+ );
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to get running tasks", e);
+ }
+ }
+ }
+
+ private final IActivityTaskManager mActivityTaskManager = getContext().getSystemService(
+ ActivityTaskManager.class).getService();
+ private final IStatusBarService mStatusBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ private final BiometricTaskStackListener mTaskStackListener =
+ new BiometricTaskStackListener();
+ private final Random mRandom = new Random();
+
+ // The current authentication session, null if idle/done. We need to track both the current
+ // and pending sessions since errors may be sent to either.
+ private AuthSession mCurrentAuthSession;
+ private AuthSession mPendingAuthSession;
+
+ // Wrap the client's receiver so we can do things with the BiometricDialog first
+ private final IBiometricServiceReceiverInternal mInternalReceiver =
+ new IBiometricServiceReceiverInternal.Stub() {
+ @Override
+ public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token)
+ throws RemoteException {
+ try {
+ if (!requireConfirmation) {
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ KeyStore.getInstance().addAuthToken(token);
+ mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ } else {
+ // Store the auth token and submit it to keystore after the confirmation
+ // button has been pressed.
+ mCurrentAuthSession.mTokenEscrow = token;
+ mCurrentAuthSession.mState = STATE_AUTH_PENDING_CONFIRM;
+ }
+
+ // Notify SysUI that the biometric has been authenticated. SysUI already knows
+ // the implicit/explicit state and will react accordingly.
+ mStatusBarService.onBiometricAuthenticated();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ @Override
+ public void onAuthenticationFailed(int cookie, boolean requireConfirmation)
+ throws RemoteException {
+ try {
+ mStatusBarService.onBiometricHelp(getContext().getResources().getString(
+ com.android.internal.R.string.biometric_not_recognized));
+ if (requireConfirmation) {
+ mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
+ mStatusBarService.showBiometricTryAgain();
+ // Cancel authentication. Skip the token/package check since we are
+ // cancelling from system server. The interface is permission protected so
+ // this is fine.
+ cancelInternal(null /* token */, null /* package */,
+ false /* fromClient */);
+ }
+ mCurrentAuthSession.mClientReceiver.onAuthenticationFailed();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ @Override
+ public void onError(int cookie, int error, String message) throws RemoteException {
+ Slog.d(TAG, "Error: " + error + " cookie: " + cookie);
+ // Errors can either be from the current auth session or the pending auth session.
+ // The pending auth session may receive errors such as ERROR_LOCKOUT before
+ // it becomes the current auth session. Similarly, the current auth session may
+ // receive errors such as ERROR_CANCELED while the pending auth session is preparing
+ // to be started. Thus we must match error messages with their cookies to be sure
+ // of their intended receivers.
+ try {
+ if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) {
+ if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) {
+ mStatusBarService.onBiometricError(message);
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
+ mCurrentAuthSession.mClientReceiver.onError(error, message);
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ mStatusBarService.hideBiometricDialog();
+ } else {
+ // Send errors after the dialog is dismissed.
+ mHandler.postDelayed(() -> {
+ try {
+ mCurrentAuthSession.mClientReceiver.onError(error, message);
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }, BiometricPrompt.HIDE_DIALOG_DELAY);
+ }
+ } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED
+ || mCurrentAuthSession.mState == STATE_AUTH_PAUSED_CANCELED) {
+ if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED
+ && error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
+ // Skip the first ERROR_CANCELED message when this happens, since
+ // "try again" requires us to cancel authentication but keep
+ // the prompt showing.
+ mCurrentAuthSession.mState = STATE_AUTH_PAUSED_CANCELED;
+ } else {
+ // In the "try again" state, we should forward canceled errors to
+ // the client and and clean up.
+ mCurrentAuthSession.mClientReceiver.onError(error, message);
+ mStatusBarService.onBiometricError(message);
+ mActivityTaskManager.unregisterTaskStackListener(
+ mTaskStackListener);
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ }
+ } else {
+ Slog.e(TAG, "Impossible session error state: "
+ + mCurrentAuthSession.mState);
+ }
+ } else if (mPendingAuthSession != null
+ && mPendingAuthSession.containsCookie(cookie)) {
+ if (mPendingAuthSession.mState == STATE_AUTH_CALLED) {
+ mPendingAuthSession.mClientReceiver.onError(error, message);
+ mPendingAuthSession.mState = STATE_AUTH_IDLE;
+ mPendingAuthSession = null;
+ } else {
+ Slog.e(TAG, "Impossible pending session error state: "
+ + mPendingAuthSession.mState);
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ @Override
+ public void onAcquired(int acquiredInfo, String message) throws RemoteException {
+ if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
+ try {
+ mStatusBarService.onBiometricHelp(message);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+ }
+
+ @Override
+ public void onDialogDismissed(int reason) throws RemoteException {
+ if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
+ // Positive button is used by passive modalities as a "confirm" button,
+ // do not send to client
+ mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason);
+ // Cancel authentication. Skip the token/package check since we are cancelling
+ // from system server. The interface is permission protected so this is fine.
+ cancelInternal(null /* token */, null /* package */, false /* fromClient */);
+ }
+ if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
+ mCurrentAuthSession.mClientReceiver.onError(
+ BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
+ getContext().getString(
+ com.android.internal.R.string.biometric_error_user_canceled));
+ } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
+ // Have the service send the token to KeyStore, and send onAuthenticated
+ // to the application
+ KeyStore.getInstance().addAuthToken(mCurrentAuthSession.mTokenEscrow);
+ mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
+ }
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ }
+
+ @Override
+ public void onTryAgainPressed() {
+ Slog.d(TAG, "onTryAgainPressed");
+ // No need to check permission, since it can only be invoked by SystemUI
+ // (or system server itself).
+ mHandler.post(() -> {
+ authenticateInternal(mCurrentAuthSession.mToken,
+ mCurrentAuthSession.mSessionId,
+ mCurrentAuthSession.mUserId,
+ mCurrentAuthSession.mClientReceiver,
+ mCurrentAuthSession.mOpPackageName,
+ mCurrentAuthSession.mBundle,
+ mCurrentAuthSession.mCallingUid,
+ mCurrentAuthSession.mCallingPid,
+ mCurrentAuthSession.mCallingUserId,
+ mCurrentAuthSession.mModality);
+ });
+ }
+ };
+
+ @Override // Binder call
+ public void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId) {
+ checkInternalPermission();
+
+ Iterator it = mPendingAuthSession.mModalitiesWaiting.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
+ if (pair.getValue() == cookie) {
+ mPendingAuthSession.mModalitiesMatched.put(pair.getKey(), pair.getValue());
+ mPendingAuthSession.mModalitiesWaiting.remove(pair.getKey());
+ Slog.d(TAG, "Matched cookie: " + cookie + ", "
+ + mPendingAuthSession.mModalitiesWaiting.size() + " remaining");
+ break;
+ }
+ }
+
+ if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) {
+ final boolean mContinuing = mCurrentAuthSession != null
+ && mCurrentAuthSession.mState == STATE_AUTH_PAUSED;
+ mCurrentAuthSession = mPendingAuthSession;
+ mPendingAuthSession = null;
+
+ mCurrentAuthSession.mState = STATE_AUTH_STARTED;
+ try {
+ int modality = TYPE_NONE;
+ it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
+ if (pair.getKey() == TYPE_FINGERPRINT) {
+ mFingerprintService.startPreparedClient(pair.getValue());
+ } else if (pair.getKey() == TYPE_IRIS) {
+ Slog.e(TAG, "Iris unsupported");
+ } else if (pair.getKey() == TYPE_FACE) {
+ mFaceService.startPreparedClient(pair.getValue());
+ } else {
+ Slog.e(TAG, "Unknown modality: " + pair.getKey());
+ }
+ modality |= pair.getKey();
+ }
+
+ if (!mContinuing) {
+ mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle,
+ mInternalReceiver, modality, requireConfirmation, userId);
+ mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+ }
+
@Override // Binder call
public void authenticate(IBinder token, long sessionId, int userId,
- IBiometricServiceReceiver receiver, int flags, String opPackageName,
- Bundle bundle, IBiometricPromptReceiver dialogReceiver) throws RemoteException {
+ IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle)
+ throws RemoteException {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
@@ -261,16 +611,45 @@ public class BiometricService extends SystemService {
checkInternalPermission();
}
- if (token == null || receiver == null || opPackageName == null || bundle == null
- || dialogReceiver == null) {
+ if (token == null || receiver == null || opPackageName == null || bundle == null) {
Slog.e(TAG, "Unable to authenticate, one or more null arguments");
return;
}
// Check the usage of this in system server. Need to remove this check if it becomes
// a public API.
- if (bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false)) {
+ final boolean useDefaultTitle =
+ bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false);
+ if (useDefaultTitle) {
checkInternalPermission();
+ // Set the default title if necessary
+ try {
+ if (useDefaultTitle) {
+ final List<ActivityManager.RunningAppProcessInfo> procs =
+ ActivityManager.getService().getRunningAppProcesses();
+ for (int i = 0; i < procs.size(); i++) {
+ final ActivityManager.RunningAppProcessInfo info = procs.get(i);
+ if (info.uid == callingUid
+ && info.importance == IMPORTANCE_FOREGROUND) {
+ PackageManager pm = getContext().getPackageManager();
+ final CharSequence label = pm.getApplicationLabel(
+ pm.getApplicationInfo(info.processName,
+ PackageManager.GET_META_DATA));
+ final String title = getContext()
+ .getString(R.string.biometric_dialog_default_title, label);
+ if (TextUtils.isEmpty(
+ bundle.getCharSequence(BiometricPrompt.KEY_TITLE))) {
+ bundle.putCharSequence(BiometricPrompt.KEY_TITLE, title);
+ }
+ break;
+ }
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Name not found", e);
+ }
}
mHandler.post(() -> {
@@ -285,13 +664,13 @@ public class BiometricService extends SystemService {
getContext().getString(R.string.biometric_error_hw_unavailable);
switch (error) {
case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
- receiver.onError(0 /* deviceId */, error, hardwareUnavailable);
+ receiver.onError(error, hardwareUnavailable);
break;
case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE:
- receiver.onError(0 /* deviceId */, error, hardwareUnavailable);
+ receiver.onError(error, hardwareUnavailable);
break;
case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS:
- receiver.onError(0 /* deviceId */, error,
+ receiver.onError(error,
getErrorString(modality, error, 0 /* vendorCode */));
break;
default:
@@ -304,60 +683,91 @@ public class BiometricService extends SystemService {
return;
}
- // Actually start authentication
mCurrentModality = modality;
- try {
- // No polymorphism :(
- if (mCurrentModality == BIOMETRIC_FINGERPRINT) {
- mFingerprintService.authenticateFromService(token, sessionId, userId,
- receiver, flags, opPackageName, bundle, dialogReceiver,
- callingUid, callingPid, callingUserId);
- } else if (mCurrentModality == BIOMETRIC_IRIS) {
- Slog.w(TAG, "Unsupported modality");
- } else if (mCurrentModality == BIOMETRIC_FACE) {
- mFaceService.authenticateFromService(true /* requireConfirmation */,
- token, sessionId, userId, receiver, flags, opPackageName,
- bundle, dialogReceiver, callingUid, callingPid, callingUserId);
- } else {
- Slog.w(TAG, "Unsupported modality");
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to start authentication", e);
- }
+
+ // Actually start authentication
+ authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
+ callingUid, callingPid, callingUserId, modality);
});
}
+ /**
+ * authenticate() (above) which is called from BiometricPrompt determines which
+ * modality/modalities to start authenticating with. authenticateInternal() should only be
+ * used for:
+ * 1) Preparing <Biometric>Services for authentication when BiometricPrompt#authenticate is,
+ * invoked, shortly after which BiometricPrompt is shown and authentication starts
+ * 2) Preparing <Biometric>Services for authentication when BiometricPrompt is already shown
+ * and the user has pressed "try again"
+ */
+ private void authenticateInternal(IBinder token, long sessionId, int userId,
+ IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
+ int callingUid, int callingPid, int callingUserId, int modality) {
+ try {
+ // Generate random cookies to pass to the services that should prepare to start
+ // authenticating. Store the cookie here and wait for all services to "ack"
+ // with the cookie. Once all cookies are received, we can show the prompt
+ // and let the services start authenticating. The cookie should be non-zero.
+ final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
+ Slog.d(TAG, "Creating auth session. Modality: " + modality
+ + ", cookie: " + cookie);
+ final HashMap<Integer, Integer> authenticators = new HashMap<>();
+ authenticators.put(modality, cookie);
+ mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId,
+ receiver, opPackageName, bundle, callingUid, callingPid, callingUserId,
+ modality);
+ mPendingAuthSession.mState = STATE_AUTH_CALLED;
+ // No polymorphism :(
+ if ((modality & TYPE_FINGERPRINT) != 0) {
+ mFingerprintService.prepareForAuthentication(token, sessionId, userId,
+ mInternalReceiver, opPackageName, cookie,
+ callingUid, callingPid, callingUserId);
+ }
+ if ((modality & TYPE_IRIS) != 0) {
+ Slog.w(TAG, "Iris unsupported");
+ }
+ if ((modality & TYPE_FACE) != 0) {
+ mFaceService.prepareForAuthentication(true /* requireConfirmation */,
+ token, sessionId, userId, mInternalReceiver, opPackageName,
+ cookie, callingUid, callingPid, callingUserId);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to start authentication", e);
+ }
+ }
+
@Override // Binder call
public void cancelAuthentication(IBinder token, String opPackageName)
throws RemoteException {
checkPermission();
-
if (token == null || opPackageName == null) {
Slog.e(TAG, "Unable to cancel, one or more null arguments");
return;
}
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int callingUserId = UserHandle.getCallingUserId();
-
- mHandler.post(() -> {
- try {
- if (mCurrentModality == BIOMETRIC_FINGERPRINT) {
- mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
- callingUid, callingPid, callingUserId);
- } else if (mCurrentModality == BIOMETRIC_IRIS) {
- Slog.w(TAG, "Unsupported modality");
- } else if (mCurrentModality == BIOMETRIC_FACE) {
- mFaceService.cancelAuthenticationFromService(token, opPackageName,
- callingUid, callingPid, callingUserId);
- } else {
- Slog.w(TAG, "Unsupported modality");
+ // We need to check the current authenticators state. If we're pending confirm
+ // or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client,
+ // since we won't be getting an onError from the driver.
+ if (mCurrentAuthSession != null && mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
+ mHandler.post(() -> {
+ try {
+ // Send error to client
+ mCurrentAuthSession.mClientReceiver.onError(
+ BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+ getContext().getString(
+ com.android.internal.R.string.biometric_error_user_canceled)
+ );
+
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ mStatusBarService.hideBiometricDialog();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
}
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to cancel authentication");
- }
- });
+ });
+ } else {
+ cancelInternal(token, opPackageName, true /* fromClient */);
+ }
}
@Override // Binder call
@@ -402,6 +812,31 @@ public class BiometricService extends SystemService {
Binder.restoreCallingIdentity(ident);
}
}
+
+ void cancelInternal(IBinder token, String opPackageName, boolean fromClient) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int callingUserId = UserHandle.getCallingUserId();
+ mHandler.post(() -> {
+ try {
+ // TODO: For multiple modalities, send a single ERROR_CANCELED only when all
+ // drivers have canceled authentication.
+ if ((mCurrentModality & TYPE_FINGERPRINT) != 0) {
+ mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
+ callingUid, callingPid, callingUserId, fromClient);
+ }
+ if ((mCurrentModality & TYPE_IRIS) != 0) {
+ Slog.w(TAG, "Iris unsupported");
+ }
+ if ((mCurrentModality & TYPE_FACE) != 0) {
+ mFaceService.cancelAuthenticationFromService(token, opPackageName,
+ callingUid, callingPid, callingUserId, fromClient);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to cancel authentication");
+ }
+ });
+ }
}
private void checkAppOp(String opPackageName, int callingUid) {
@@ -413,7 +848,7 @@ public class BiometricService extends SystemService {
}
private void checkInternalPermission() {
- getContext().enforceCallingPermission(USE_BIOMETRIC_INTERNAL,
+ getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL,
"Must have USE_BIOMETRIC_INTERNAL permission");
}
@@ -490,16 +925,19 @@ public class BiometricService extends SystemService {
* returns errors through the callback (no biometric feature, hardware not detected, no
* templates enrolled, etc). This service must not start authentication if errors are sent.
*
- * @Returns A pair [Modality, Error] with Modality being one of {@link #BIOMETRIC_NONE},
- * {@link #BIOMETRIC_FINGERPRINT}, {@link #BIOMETRIC_IRIS}, {@link #BIOMETRIC_FACE}
+ * @Returns A pair [Modality, Error] with Modality being one of
+ * {@link BiometricAuthenticator#TYPE_NONE},
+ * {@link BiometricAuthenticator#TYPE_FINGERPRINT},
+ * {@link BiometricAuthenticator#TYPE_IRIS},
+ * {@link BiometricAuthenticator#TYPE_FACE}
* and the error containing one of the {@link BiometricConstants} errors.
*/
private Pair<Integer, Integer> checkAndGetBiometricModality(int callingUid) {
- int modality = BIOMETRIC_NONE;
+ int modality = TYPE_NONE;
// No biometric features, send error
if (mAuthenticators.isEmpty()) {
- return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
+ return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
}
// Assuming that authenticators are listed in priority-order, the rest of this function
@@ -512,13 +950,13 @@ public class BiometricService extends SystemService {
boolean hasTemplatesEnrolled = false;
boolean enabledForApps = false;
- int firstHwAvailable = BIOMETRIC_NONE;
+ int firstHwAvailable = TYPE_NONE;
for (int i = 0; i < mAuthenticators.size(); i++) {
modality = mAuthenticators.get(i).getType();
BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator();
if (authenticator.isHardwareDetected()) {
isHardwareDetected = true;
- if (firstHwAvailable == BIOMETRIC_NONE) {
+ if (firstHwAvailable == TYPE_NONE) {
// Store the first one since we want to return the error in correct priority
// order.
firstHwAvailable = modality;
@@ -538,13 +976,13 @@ public class BiometricService extends SystemService {
// Check error conditions
if (!isHardwareDetected) {
- return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
+ return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
} else if (!hasTemplatesEnrolled) {
// Return the modality here so the correct error string can be sent. This error is
// preferred over !enabledForApps
return new Pair<>(firstHwAvailable, BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
} else if (!enabledForApps) {
- return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
+ return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
}
return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS);
@@ -552,11 +990,11 @@ public class BiometricService extends SystemService {
private boolean isEnabledForApp(int modality) {
switch(modality) {
- case BIOMETRIC_FINGERPRINT:
+ case TYPE_FINGERPRINT:
return true;
- case BIOMETRIC_IRIS:
+ case TYPE_IRIS:
return true;
- case BIOMETRIC_FACE:
+ case TYPE_FACE:
return mSettingObserver.getFaceEnabledForApps();
default:
Slog.w(TAG, "Unsupported modality: " + modality);
@@ -566,12 +1004,12 @@ public class BiometricService extends SystemService {
private String getErrorString(int type, int error, int vendorCode) {
switch (type) {
- case BIOMETRIC_FINGERPRINT:
+ case TYPE_FINGERPRINT:
return FingerprintManager.getErrorString(getContext(), error, vendorCode);
- case BIOMETRIC_IRIS:
+ case TYPE_IRIS:
Slog.w(TAG, "Modality not supported");
return null; // not supported
- case BIOMETRIC_FACE:
+ case TYPE_FACE:
return FaceManager.getErrorString(getContext(), error, vendorCode);
default:
Slog.w(TAG, "Unable to get error string for modality: " + type);
@@ -581,12 +1019,12 @@ public class BiometricService extends SystemService {
private BiometricAuthenticator getAuthenticator(int type) {
switch (type) {
- case BIOMETRIC_FINGERPRINT:
+ case TYPE_FINGERPRINT:
return (FingerprintManager)
getContext().getSystemService(Context.FINGERPRINT_SERVICE);
- case BIOMETRIC_IRIS:
+ case TYPE_IRIS:
return null;
- case BIOMETRIC_FACE:
+ case TYPE_FACE:
return (FaceManager)
getContext().getSystemService(Context.FACE_SERVICE);
default:
@@ -596,11 +1034,11 @@ public class BiometricService extends SystemService {
private boolean hasFeature(int type) {
switch (type) {
- case BIOMETRIC_FINGERPRINT:
+ case TYPE_FINGERPRINT:
return mHasFeatureFingerprint;
- case BIOMETRIC_IRIS:
+ case TYPE_IRIS:
return mHasFeatureIris;
- case BIOMETRIC_FACE:
+ case TYPE_FACE:
return mHasFeatureFace;
default:
return false;
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 74d742af5b84..9649ccd3c750 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -16,7 +16,6 @@
package com.android.server.biometrics;
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
import android.app.ActivityManager;
@@ -36,8 +35,9 @@ import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.fingerprint.Fingerprint;
import android.os.Binder;
import android.os.Bundle;
@@ -56,7 +56,6 @@ import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
-import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.server.SystemService;
@@ -106,6 +105,7 @@ public abstract class BiometricServiceBase extends SystemService
protected final AppOpsManager mAppOps;
protected final H mHandler = new H();
+ private IBiometricService mBiometricService;
private ClientMonitor mCurrentClient;
private ClientMonitor mPendingClient;
private PerformanceStats mPerformanceStats;
@@ -223,12 +223,9 @@ public abstract class BiometricServiceBase extends SystemService
public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
- boolean restricted, String owner, Bundle bundle,
- IBiometricPromptReceiver dialogReceiver,
- IStatusBarService statusBarService, boolean requireConfirmation) {
- super(context, getMetrics(), daemon, halDeviceId, token, listener,
- targetUserId, groupId, opId, restricted, owner, bundle, dialogReceiver,
- statusBarService, requireConfirmation);
+ boolean restricted, String owner, int cookie, boolean requireConfirmation) {
+ super(context, getMetrics(), daemon, halDeviceId, token, listener, targetUserId,
+ groupId, opId, restricted, owner, cookie, requireConfirmation);
}
@Override
@@ -279,11 +276,6 @@ public abstract class BiometricServiceBase extends SystemService
}
return AuthenticationClient.LOCKOUT_NONE;
}
-
- @Override
- public void onAuthenticationConfirmed() {
- removeClient(mCurrentClient);
- }
}
protected class EnrollClientImpl extends EnrollClient {
@@ -345,18 +337,28 @@ public abstract class BiometricServiceBase extends SystemService
default void onEnrollResult(BiometricAuthenticator.Identifier identifier,
int remaining) throws RemoteException {};
- void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
- throws RemoteException;
+ void onAcquired(long deviceId, int acquiredInfo, int vendorCode) throws RemoteException;
- void onAuthenticationSucceeded(long deviceId,
- BiometricAuthenticator.Identifier biometric, int userId)
- throws RemoteException;
+ default void onAuthenticationSucceeded(long deviceId,
+ BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
+ throw new UnsupportedOperationException("Stub!");
+ }
- void onAuthenticationFailed(long deviceId)
- throws RemoteException;
+ default void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
+ throws RemoteException {
+ throw new UnsupportedOperationException("Stub!");
+ }
- void onError(long deviceId, int error, int vendorCode)
- throws RemoteException;
+ default void onAuthenticationFailed(long deviceId) throws RemoteException {
+ throw new UnsupportedOperationException("Stub!");
+ }
+
+ default void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
+ throws RemoteException {
+ throw new UnsupportedOperationException("Stub!");
+ }
+
+ void onError(long deviceId, int error, int vendorCode, int cookie) throws RemoteException;
default void onRemoved(BiometricAuthenticator.Identifier identifier,
int remaining) throws RemoteException {};
@@ -366,6 +368,37 @@ public abstract class BiometricServiceBase extends SystemService
}
/**
+ * Wraps the callback interface from Service -> BiometricPrompt
+ */
+ protected abstract class BiometricServiceListener implements ServiceListener {
+ private IBiometricServiceReceiverInternal mWrapperReceiver;
+
+ public BiometricServiceListener(IBiometricServiceReceiverInternal wrapperReceiver) {
+ mWrapperReceiver = wrapperReceiver;
+ }
+
+ public IBiometricServiceReceiverInternal getWrapperReceiver() {
+ return mWrapperReceiver;
+ }
+
+ @Override
+ public void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
+ throws RemoteException {
+ if (getWrapperReceiver() != null) {
+ getWrapperReceiver().onAuthenticationSucceeded(requireConfirmation, token);
+ }
+ }
+
+ @Override
+ public void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
+ throws RemoteException {
+ if (getWrapperReceiver() != null) {
+ getWrapperReceiver().onAuthenticationFailed(cookie, requireConfirmation);
+ }
+ }
+ }
+
+ /**
* Wraps a portion of the interface from Service -> Daemon that is used by the ClientMonitor
* subclasses.
*/
@@ -706,30 +739,6 @@ public abstract class BiometricServiceBase extends SystemService
}
mHandler.post(() -> {
- if (client.isBiometricPrompt()) {
- try {
- final List<ActivityManager.RunningAppProcessInfo> procs =
- ActivityManager.getService().getRunningAppProcesses();
- for (int i = 0; i < procs.size(); i++) {
- final ActivityManager.RunningAppProcessInfo info = procs.get(i);
- if (info.uid == callingUid && info.importance == IMPORTANCE_FOREGROUND) {
- PackageManager pm = getContext().getPackageManager();
- final CharSequence label = pm.getApplicationLabel(
- pm.getApplicationInfo(info.processName,
- PackageManager.GET_META_DATA));
- final String title = getContext()
- .getString(R.string.biometric_dialog_default_title, label);
- client.setTitleIfEmpty(title);
- break;
- }
- }
- } catch (RemoteException e) {
- Slog.e(getTag(), "Unable to get application name", e);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(getTag(), "Unable to get application name", e);
- }
- }
-
mMetricsLogger.histogram(getMetrics().tagAuthToken(), opId != 0L ? 1 : 0);
// Get performance stats object for this user.
@@ -751,29 +760,37 @@ public abstract class BiometricServiceBase extends SystemService
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
- cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, callingUserId);
+ cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, callingUserId,
+ true /* fromClient */);
}
protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName,
- int callingUid, int callingPid, int callingUserId) {
- if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
- callingUserId)) {
- if (DEBUG) Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName);
- return;
+ int callingUid, int callingPid, int callingUserId, boolean fromClient) {
+ if (fromClient) {
+ // Only check this if cancel was called from the client (app). If cancel was called
+ // from BiometricService, it means the dialog was dismissed due to user interaction.
+ if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
+ callingUserId)) {
+ if (DEBUG) Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName);
+ return;
+ }
}
mHandler.post(() -> {
ClientMonitor client = mCurrentClient;
if (client instanceof AuthenticationClient) {
- if (client.getToken() == token) {
- if (DEBUG) Slog.v(getTag(), "stop client " + client.getOwnerString());
+ if (client.getToken() == token || !fromClient) {
+ if (DEBUG) Slog.v(getTag(), "Stopping client " + client.getOwnerString()
+ + ", fromClient: " + fromClient);
+ // If cancel was from BiometricService, it means the dialog was dismissed
+ // and authentication should be canceled.
client.stop(client.getToken() == token);
} else {
- if (DEBUG) Slog.v(getTag(), "can't stop client "
- + client.getOwnerString() + " since tokens don't match");
+ if (DEBUG) Slog.v(getTag(), "Can't stop client " + client.getOwnerString()
+ + " since tokens don't match. fromClient: " + fromClient);
}
} else if (client != null) {
- if (DEBUG) Slog.v(getTag(), "can't cancel non-authenticating client "
+ if (DEBUG) Slog.v(getTag(), "Can't cancel non-authenticating client "
+ client.getOwnerString());
}
});
@@ -805,8 +822,7 @@ public abstract class BiometricServiceBase extends SystemService
int lockoutMode = getLockoutMode();
if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
- Slog.v(getTag(), "In lockout mode(" + lockoutMode +
- ") ; disallowing authentication");
+ Slog.v(getTag(), "In lockout mode(" + lockoutMode + ") ; disallowing authentication");
int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
@@ -919,7 +935,6 @@ public abstract class BiometricServiceBase extends SystemService
if (currentClient != null) {
if (DEBUG) Slog.v(getTag(), "request stop current client " +
currentClient.getOwnerString());
-
// This check only matters for FingerprintService, since enumerate may call back
// multiple times.
if (currentClient instanceof FingerprintService.EnumerateClientImpl ||
@@ -940,15 +955,49 @@ public abstract class BiometricServiceBase extends SystemService
mHandler.removeCallbacks(mResetClientState);
mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
} else if (newClient != null) {
+ // For BiometricPrompt clients, do not start until
+ // <Biometric>Service#startPreparedClient is called. BiometricService waits until all
+ // modalities are ready before initiating authentication.
+ if (newClient instanceof AuthenticationClient) {
+ AuthenticationClient client = (AuthenticationClient) newClient;
+ if (client.isBiometricPrompt()) {
+ if (DEBUG) Slog.v(getTag(), "Returning cookie: " + client.getCookie());
+ mCurrentClient = newClient;
+ if (mBiometricService == null) {
+ mBiometricService = IBiometricService.Stub.asInterface(
+ ServiceManager.getService(Context.BIOMETRIC_SERVICE));
+ }
+ try {
+ mBiometricService.onReadyForAuthentication(client.getCookie(),
+ client.getRequireConfirmation(), client.getTargetUserId());
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Remote exception", e);
+ }
+ return;
+ }
+ }
+
+ // We are not a BiometricPrompt client, start the client immediately
mCurrentClient = newClient;
- if (DEBUG) Slog.v(getTag(), "starting client "
- + newClient.getClass().getSuperclass().getSimpleName()
- + "(" + newClient.getOwnerString() + ")"
- + ", initiatedByClient = " + initiatedByClient);
- notifyClientActiveCallbacks(true);
+ startCurrentClient(mCurrentClient.getCookie());
+ }
+ }
- newClient.start();
+ protected void startCurrentClient(int cookie) {
+ if (mCurrentClient == null) {
+ Slog.e(getTag(), "Trying to start null client!");
+ return;
+ }
+ if (DEBUG) Slog.v(getTag(), "starting client "
+ + mCurrentClient.getClass().getSuperclass().getSimpleName()
+ + "(" + mCurrentClient.getOwnerString() + ")"
+ + " cookie: " + cookie + "/" + mCurrentClient.getCookie());
+ if (cookie != mCurrentClient.getCookie()) {
+ Slog.e(getTag(), "Mismatched cookie");
+ return;
}
+ notifyClientActiveCallbacks(true);
+ mCurrentClient.start();
}
protected void removeClient(ClientMonitor client) {
diff --git a/services/core/java/com/android/server/biometrics/ClientMonitor.java b/services/core/java/com/android/server/biometrics/ClientMonitor.java
index a7ada2f6556c..d19aff69b832 100644
--- a/services/core/java/com/android/server/biometrics/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/ClientMonitor.java
@@ -58,6 +58,9 @@ public abstract class ClientMonitor implements IBinder.DeathRecipient {
private IBinder mToken;
private BiometricServiceBase.ServiceListener mListener;
+ // Currently only used for authentication client. The cookie generated by BiometricService
+ // is never 0.
+ private final int mCookie;
protected final MetricsLogger mMetricsLogger;
protected final Metrics mMetrics;
@@ -80,7 +83,7 @@ public abstract class ClientMonitor implements IBinder.DeathRecipient {
public ClientMonitor(Context context, Metrics metrics,
BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
BiometricServiceBase.ServiceListener listener, int userId, int groupId,
- boolean restricted, String owner) {
+ boolean restricted, String owner, int cookie) {
mContext = context;
mMetrics = metrics;
mDaemon = daemon;
@@ -91,6 +94,7 @@ public abstract class ClientMonitor implements IBinder.DeathRecipient {
mGroupId = groupId;
mIsRestricted = restricted;
mOwner = owner;
+ mCookie = cookie;
mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
mMetricsLogger = new MetricsLogger();
@@ -107,6 +111,10 @@ public abstract class ClientMonitor implements IBinder.DeathRecipient {
return mMetrics.logTag();
}
+ public int getCookie() {
+ return mCookie;
+ }
+
/**
* Contacts the biometric's HAL to start the client.
* @return 0 on success, errno from driver on failure
@@ -174,7 +182,7 @@ public abstract class ClientMonitor implements IBinder.DeathRecipient {
public boolean onError(long deviceId, int error, int vendorCode) {
try {
if (mListener != null) {
- mListener.onError(deviceId, error, vendorCode);
+ mListener.onError(deviceId, error, vendorCode, getCookie());
}
} catch (RemoteException e) {
Slog.w(getLogTag(), "Failed to invoke sendError", e);
diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java
index 76dc5a91bdbe..f858ef5ec6f8 100644
--- a/services/core/java/com/android/server/biometrics/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/EnrollClient.java
@@ -40,7 +40,7 @@ public abstract class EnrollClient extends ClientMonitor {
BiometricServiceBase.ServiceListener listener, int userId, int groupId,
byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils) {
super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
- owner);
+ owner, 0 /* cookie */);
mBiometricUtils = utils;
mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
}
diff --git a/services/core/java/com/android/server/biometrics/EnumerateClient.java b/services/core/java/com/android/server/biometrics/EnumerateClient.java
index 47dc7ffda456..df6220cfd94b 100644
--- a/services/core/java/com/android/server/biometrics/EnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/EnumerateClient.java
@@ -34,7 +34,7 @@ public abstract class EnumerateClient extends ClientMonitor {
BiometricServiceBase.ServiceListener listener, int groupId, int userId,
boolean restricted, String owner) {
super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
- owner);
+ owner, 0 /* cookie */);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/RemovalClient.java b/services/core/java/com/android/server/biometrics/RemovalClient.java
index 15b3773314bf..be233ec89342 100644
--- a/services/core/java/com/android/server/biometrics/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/RemovalClient.java
@@ -37,7 +37,7 @@ public abstract class RemovalClient extends ClientMonitor {
BiometricServiceBase.ServiceListener listener, int biometricId, int groupId, int userId,
boolean restricted, String owner, BiometricUtils utils) {
super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
- owner);
+ owner, 0 /* cookie */);
mBiometricId = biometricId;
mBiometricUtils = utils;
}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 7aa2e47300dd..557af0478b87 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -27,9 +27,8 @@ import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.IBiometricPromptReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
-import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
import android.hardware.biometrics.face.V1_0.Status;
@@ -38,7 +37,6 @@ import android.hardware.face.FaceManager;
import android.hardware.face.IFaceService;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
-import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
@@ -50,7 +48,6 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.biometrics.BiometricServiceBase;
@@ -89,27 +86,9 @@ public class FaceService extends BiometricServiceBase {
public FaceAuthClient(Context context,
DaemonWrapper daemon, long halDeviceId, IBinder token,
ServiceListener listener, int targetUserId, int groupId, long opId,
- boolean restricted, String owner, Bundle bundle,
- IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
- boolean requireConfirmation) {
+ boolean restricted, String owner, int cookie, boolean requireConfirmation) {
super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
- restricted, owner, bundle, dialogReceiver, statusBarService,
- requireConfirmation);
- }
-
- @Override
- public String getErrorString(int error, int vendorCode) {
- return FaceManager.getErrorString(getContext(), error, vendorCode);
- }
-
- @Override
- public String getAcquiredString(int acquireInfo, int vendorCode) {
- return FaceManager.getAcquiredString(getContext(), acquireInfo, vendorCode);
- }
-
- @Override
- public int getBiometricType() {
- return BiometricAuthenticator.TYPE_FACE;
+ restricted, owner, cookie, requireConfirmation);
}
}
@@ -162,28 +141,33 @@ public class FaceService extends BiometricServiceBase {
final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
- null /* bundle */, null /* dialogReceiver */, mStatusBarService,
- false /* requireConfirmation */);
+ 0 /* cookie */, false /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName);
}
@Override // Binder call
- public void authenticateFromService(boolean requireConfirmation, IBinder token, long opId,
- int groupId, IBiometricServiceReceiver receiver, int flags,
- String opPackageName, Bundle bundle, IBiometricPromptReceiver dialogReceiver,
- int callingUid, int callingPid, int callingUserId) {
+ public void prepareForAuthentication(boolean requireConfirmation, IBinder token, long opId,
+ int groupId, IBiometricServiceReceiverInternal wrapperReceiver,
+ String opPackageName, int cookie, int callingUid, int callingPid,
+ int callingUserId) {
checkPermission(USE_BIOMETRIC_INTERNAL);
final boolean restricted = true; // BiometricPrompt is always restricted
final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token,
- new BiometricPromptServiceListenerImpl(receiver),
- mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
- bundle, dialogReceiver, mStatusBarService, true /* requireConfirmation */);
+ new BiometricPromptServiceListenerImpl(wrapperReceiver),
+ mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, cookie,
+ true /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
callingUserId);
}
@Override // Binder call
+ public void startPreparedClient(int cookie) {
+ checkPermission(MANAGE_BIOMETRIC);
+ startCurrentClient(cookie);
+ }
+
+ @Override // Binder call
public void cancelAuthentication(final IBinder token, final String opPackageName) {
checkPermission(USE_BIOMETRIC_INTERNAL);
cancelAuthenticationInternal(token, opPackageName);
@@ -191,10 +175,10 @@ public class FaceService extends BiometricServiceBase {
@Override // Binder call
public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
- int callingUid, int callingPid, int callingUserId) {
+ int callingUid, int callingPid, int callingUserId, boolean fromClient) {
checkPermission(USE_BIOMETRIC_INTERNAL);
- cancelAuthenticationInternal(token, opPackageName,
- callingUid, callingPid, callingUserId);
+ cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid,
+ callingUserId, fromClient);
}
@Override // Binder call
@@ -405,12 +389,9 @@ public class FaceService extends BiometricServiceBase {
* Receives callbacks from the ClientMonitor implementations. The results are forwarded to
* BiometricPrompt.
*/
- private class BiometricPromptServiceListenerImpl implements ServiceListener {
-
- private IBiometricServiceReceiver mBiometricServiceReceiver;
-
- public BiometricPromptServiceListenerImpl(IBiometricServiceReceiver receiver) {
- mBiometricServiceReceiver = receiver;
+ private class BiometricPromptServiceListenerImpl extends BiometricServiceListener {
+ BiometricPromptServiceListenerImpl(IBiometricServiceReceiverInternal wrapperReceiver) {
+ super(wrapperReceiver);
}
@Override
@@ -419,32 +400,18 @@ public class FaceService extends BiometricServiceBase {
/**
* Map the acquired codes onto existing {@link BiometricConstants} acquired codes.
*/
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onAcquired(deviceId,
+ if (getWrapperReceiver() != null) {
+ getWrapperReceiver().onAcquired(
FaceManager.getMappedAcquiredInfo(acquiredInfo, vendorCode),
FaceManager.getAcquiredString(getContext(), acquiredInfo, vendorCode));
}
}
@Override
- public void onAuthenticationSucceeded(long deviceId,
- BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onAuthenticationSucceeded(deviceId);
- }
- }
-
- @Override
- public void onAuthenticationFailed(long deviceId) throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onAuthenticationFailed(deviceId);
- }
- }
-
- @Override
- public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onError(deviceId, error,
+ public void onError(long deviceId, int error, int vendorCode, int cookie)
+ throws RemoteException {
+ if (getWrapperReceiver() != null) {
+ getWrapperReceiver().onError(cookie, error,
FaceManager.getErrorString(getContext(), error, vendorCode));
}
}
@@ -455,7 +422,6 @@ public class FaceService extends BiometricServiceBase {
* the FaceManager.
*/
private class ServiceListenerImpl implements ServiceListener {
-
private IFaceServiceReceiver mFaceServiceReceiver;
public ServiceListenerImpl(IFaceServiceReceiver receiver) {
@@ -501,7 +467,8 @@ public class FaceService extends BiometricServiceBase {
}
@Override
- public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
+ public void onError(long deviceId, int error, int vendorCode, int cookie)
+ throws RemoteException {
if (mFaceServiceReceiver != null) {
mFaceServiceReceiver.onError(deviceId, error, vendorCode);
}
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index b0b788fbe589..6a5bc61f2eb6 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -30,9 +30,8 @@ import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
import android.hardware.fingerprint.Fingerprint;
@@ -42,7 +41,6 @@ import android.hardware.fingerprint.IFingerprintService;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.Binder;
import android.os.Build;
-import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
@@ -55,7 +53,6 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.biometrics.AuthenticationClient;
@@ -109,27 +106,10 @@ public class FingerprintService extends BiometricServiceBase {
public FingerprintAuthClient(Context context,
DaemonWrapper daemon, long halDeviceId, IBinder token,
ServiceListener listener, int targetUserId, int groupId, long opId,
- boolean restricted, String owner, Bundle bundle,
- IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
+ boolean restricted, String owner, int cookie,
boolean requireConfirmation) {
super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
- restricted, owner, bundle, dialogReceiver, statusBarService,
- requireConfirmation);
- }
-
- @Override
- public String getErrorString(int error, int vendorCode) {
- return FingerprintManager.getErrorString(getContext(), error, vendorCode);
- }
-
- @Override
- public String getAcquiredString(int acquireInfo, int vendorCode) {
- return FingerprintManager.getAcquiredString(getContext(), acquireInfo, vendorCode);
- }
-
- @Override
- public int getBiometricType() {
- return BiometricAuthenticator.TYPE_FINGERPRINT;
+ restricted, owner, cookie, requireConfirmation);
}
}
@@ -182,38 +162,44 @@ public class FingerprintService extends BiometricServiceBase {
final boolean restricted = isRestricted();
final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
- mCurrentUserId, groupId, opId, restricted, opPackageName, null /* bundle */,
- null /* dialogReceiver */, mStatusBarService, false /* requireConfirmation */);
+ mCurrentUserId, groupId, opId, restricted, opPackageName,
+ 0 /* cookie */, false /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName);
}
@Override // Binder call
- public void authenticateFromService(IBinder token, long opId, int groupId,
- IBiometricServiceReceiver receiver, int flags, String opPackageName,
- Bundle bundle, IBiometricPromptReceiver dialogReceiver,
- int callingUid, int callingPid, int callingUserId) {
+ public void prepareForAuthentication(IBinder token, long opId, int groupId,
+ IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName,
+ int cookie, int callingUid, int callingPid, int callingUserId) {
checkPermission(MANAGE_BIOMETRIC);
final boolean restricted = true; // BiometricPrompt is always restricted
final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token,
- new BiometricPromptServiceListenerImpl(receiver),
- mCurrentUserId, groupId, opId, restricted, opPackageName, bundle,
- dialogReceiver, mStatusBarService, false /* requireConfirmation */);
+ new BiometricPromptServiceListenerImpl(wrapperReceiver),
+ mCurrentUserId, groupId, opId, restricted, opPackageName, cookie,
+ false /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
callingUserId);
}
@Override // Binder call
+ public void startPreparedClient(int cookie) {
+ checkPermission(MANAGE_BIOMETRIC);
+ startCurrentClient(cookie);
+ }
+
+
+ @Override // Binder call
public void cancelAuthentication(final IBinder token, final String opPackageName) {
cancelAuthenticationInternal(token, opPackageName);
}
@Override // Binder call
public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
- int callingUid, int callingPid, int callingUserId) {
+ int callingUid, int callingPid, int callingUserId, boolean fromClient) {
checkPermission(MANAGE_BIOMETRIC);
- cancelAuthenticationInternal(token, opPackageName,
- callingUid, callingPid, callingUserId);
+ cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid,
+ callingUserId, fromClient);
}
@Override // Binder call
@@ -388,43 +374,25 @@ public class FingerprintService extends BiometricServiceBase {
* Receives callbacks from the ClientMonitor implementations. The results are forwarded to
* BiometricPrompt.
*/
- private class BiometricPromptServiceListenerImpl implements ServiceListener {
-
- private IBiometricServiceReceiver mBiometricServiceReceiver;
-
- public BiometricPromptServiceListenerImpl(IBiometricServiceReceiver receiver) {
- mBiometricServiceReceiver = receiver;
+ private class BiometricPromptServiceListenerImpl extends BiometricServiceListener {
+ BiometricPromptServiceListenerImpl(IBiometricServiceReceiverInternal wrapperReceiver) {
+ super(wrapperReceiver);
}
@Override
public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onAcquired(deviceId, acquiredInfo,
- FingerprintManager.getAcquiredString(
+ if (getWrapperReceiver() != null) {
+ getWrapperReceiver().onAcquired(acquiredInfo, FingerprintManager.getAcquiredString(
getContext(), acquiredInfo, vendorCode));
}
}
@Override
- public void onAuthenticationSucceeded(long deviceId,
- BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onAuthenticationSucceeded(deviceId);
- }
- }
-
- @Override
- public void onAuthenticationFailed(long deviceId) throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onAuthenticationFailed(deviceId);
- }
- }
-
- @Override
- public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onError(deviceId, error,
+ public void onError(long deviceId, int error, int vendorCode, int cookie)
+ throws RemoteException {
+ if (getWrapperReceiver() != null) {
+ getWrapperReceiver().onError(cookie, error,
FingerprintManager.getErrorString(getContext(), error, vendorCode));
}
}
@@ -435,7 +403,6 @@ public class FingerprintService extends BiometricServiceBase {
* the FingerprintManager.
*/
private class ServiceListenerImpl implements ServiceListener {
-
private IFingerprintServiceReceiver mFingerprintServiceReceiver;
public ServiceListenerImpl(IFingerprintServiceReceiver receiver) {
@@ -483,7 +450,8 @@ public class FingerprintService extends BiometricServiceBase {
}
@Override
- public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
+ public void onError(long deviceId, int error, int vendorCode, int cookie)
+ throws RemoteException {
if (mFingerprintServiceReceiver != null) {
mFingerprintServiceReceiver.onError(deviceId, error, vendorCode);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ae27d0c07ea8..6c2549e9b04d 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -198,6 +198,7 @@ import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
@@ -206,6 +207,7 @@ import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.server.DeviceIdleController;
import com.android.server.EventLogTags;
+import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.lights.Light;
@@ -264,11 +266,12 @@ public class NotificationManagerService extends SystemService {
// message codes
static final int MESSAGE_DURATION_REACHED = 2;
- static final int MESSAGE_SAVE_POLICY_FILE = 3;
+ // 3: removed to a different handler
static final int MESSAGE_SEND_RANKING_UPDATE = 4;
static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
static final int MESSAGE_FINISH_TOKEN_TIMEOUT = 7;
+ static final int MESSAGE_ON_PACKAGE_CHANGED = 8;
// ranking thread messages
private static final int MESSAGE_RECONSIDER_RANKING = 1000;
@@ -570,7 +573,7 @@ public class NotificationManagerService extends SystemService {
mListeners.migrateToXml();
mAssistants.migrateToXml();
mConditionProviders.migrateToXml();
- savePolicyFile();
+ handleSavePolicyFile();
}
mAssistants.ensureAssistant();
@@ -600,31 +603,28 @@ public class NotificationManagerService extends SystemService {
}
}
- public void savePolicyFile() {
- mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
- mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
- }
-
private void handleSavePolicyFile() {
- if (DBG) Slog.d(TAG, "handleSavePolicyFile");
- synchronized (mPolicyFile) {
- final FileOutputStream stream;
- try {
- stream = mPolicyFile.startWrite();
- } catch (IOException e) {
- Slog.w(TAG, "Failed to save policy file", e);
- return;
- }
+ IoThread.getHandler().post(() -> {
+ if (DBG) Slog.d(TAG, "handleSavePolicyFile");
+ synchronized (mPolicyFile) {
+ final FileOutputStream stream;
+ try {
+ stream = mPolicyFile.startWrite();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to save policy file", e);
+ return;
+ }
- try {
- writePolicyXml(stream, false /*forBackup*/);
- mPolicyFile.finishWrite(stream);
- } catch (IOException e) {
- Slog.w(TAG, "Failed to save policy file, restoring backup", e);
- mPolicyFile.failWrite(stream);
+ try {
+ writePolicyXml(stream, false /*forBackup*/);
+ mPolicyFile.finishWrite(stream);
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to save policy file, restoring backup", e);
+ mPolicyFile.failWrite(stream);
+ }
}
- }
- BackupManager.dataChanged(getContext().getPackageName());
+ BackupManager.dataChanged(getContext().getPackageName());
+ });
}
private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
@@ -1133,12 +1133,7 @@ public class NotificationManagerService extends SystemService {
}
}
- mListeners.onPackagesChanged(removingPackage, pkgList, uidList);
- mAssistants.onPackagesChanged(removingPackage, pkgList, uidList);
- mConditionProviders.onPackagesChanged(removingPackage, pkgList, uidList);
- mPreferencesHelper.onPackagesChanged(
- removingPackage, changeUserId, pkgList, uidList);
- savePolicyFile();
+ mHandler.scheduleOnPackageChanged(removingPackage, changeUserId, pkgList, uidList);
}
}
};
@@ -1205,7 +1200,7 @@ public class NotificationManagerService extends SystemService {
mListeners.onUserRemoved(userId);
mConditionProviders.onUserRemoved(userId);
mAssistants.onUserRemoved(userId);
- savePolicyFile();
+ handleSavePolicyFile();
} else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
mUserProfiles.updateCache(context);
@@ -1456,7 +1451,7 @@ public class NotificationManagerService extends SystemService {
mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
@Override
public void onConfigChanged() {
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -1755,7 +1750,7 @@ public class NotificationManagerService extends SystemService {
modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
}
- savePolicyFile();
+ handleSavePolicyFile();
}
private void maybeNotifyChannelOwner(String pkg, int uid, NotificationChannel preUpdate,
@@ -2223,7 +2218,7 @@ public class NotificationManagerService extends SystemService {
Slog.w(TAG, "Can't notify app about app block change", e);
}
- savePolicyFile();
+ handleSavePolicyFile();
}
/**
@@ -2280,7 +2275,7 @@ public class NotificationManagerService extends SystemService {
public void setShowBadge(String pkg, int uid, boolean showBadge) {
checkCallerIsSystem();
mPreferencesHelper.setShowBadge(pkg, uid, showBadge);
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -2296,7 +2291,7 @@ public class NotificationManagerService extends SystemService {
if (info != null) {
mPreferencesHelper.setNotificationDelegate(
callingPkg, callingUid, delegate, info.uid);
- savePolicyFile();
+ handleSavePolicyFile();
}
} catch (RemoteException e) {
// :(
@@ -2307,7 +2302,7 @@ public class NotificationManagerService extends SystemService {
public void revokeNotificationDelegate(String callingPkg) {
checkCallerIsSameApp(callingPkg);
mPreferencesHelper.revokeNotificationDelegate(callingPkg, Binder.getCallingUid());
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -2342,7 +2337,7 @@ public class NotificationManagerService extends SystemService {
NotificationChannelGroup group) throws RemoteException {
enforceSystemOrSystemUI("Caller not system or systemui");
createNotificationChannelGroup(pkg, uid, group, false, false);
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -2355,7 +2350,7 @@ public class NotificationManagerService extends SystemService {
final NotificationChannelGroup group = groups.get(i);
createNotificationChannelGroup(pkg, Binder.getCallingUid(), group, true, false);
}
- savePolicyFile();
+ handleSavePolicyFile();
}
private void createNotificationChannelsImpl(String pkg, int uid,
@@ -2373,7 +2368,7 @@ public class NotificationManagerService extends SystemService {
mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
}
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -2418,7 +2413,7 @@ public class NotificationManagerService extends SystemService {
UserHandle.getUserHandleForUid(callingUid),
mPreferencesHelper.getNotificationChannel(pkg, callingUid, channelId, true),
NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -2460,7 +2455,7 @@ public class NotificationManagerService extends SystemService {
mListeners.notifyNotificationChannelGroupChanged(
pkg, UserHandle.getUserHandleForUid(callingUid), groupToDelete,
NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
- savePolicyFile();
+ handleSavePolicyFile();
}
}
@@ -2593,7 +2588,7 @@ public class NotificationManagerService extends SystemService {
true, UserHandle.getCallingUserId(), packages, uids);
}
- savePolicyFile();
+ handleSavePolicyFile();
}
@@ -3381,7 +3376,7 @@ public class NotificationManagerService extends SystemService {
final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
try {
readPolicyXml(bais, true /*forRestore*/);
- savePolicyFile();
+ handleSavePolicyFile();
} catch (NumberFormatException | XmlPullParserException | IOException e) {
Slog.w(TAG, "applyRestore: error reading payload", e);
}
@@ -3422,7 +3417,7 @@ public class NotificationManagerService extends SystemService {
.setPackage(pkg)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
UserHandle.of(userId), null);
- savePolicyFile();
+ handleSavePolicyFile();
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -3566,7 +3561,7 @@ public class NotificationManagerService extends SystemService {
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
UserHandle.of(userId), null);
- savePolicyFile();
+ handleSavePolicyFile();
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -3592,7 +3587,7 @@ public class NotificationManagerService extends SystemService {
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
UserHandle.of(userId), null);
- savePolicyFile();
+ handleSavePolicyFile();
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -3675,7 +3670,7 @@ public class NotificationManagerService extends SystemService {
verifyPrivilegedListener(token, user, false);
createNotificationChannelGroup(
pkg, getUidForPackageAndUser(pkg, user), group, false, true);
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -3724,7 +3719,7 @@ public class NotificationManagerService extends SystemService {
}
if (allow != mLockScreenAllowSecureNotifications) {
mLockScreenAllowSecureNotifications = allow;
- savePolicyFile();
+ handleSavePolicyFile();
}
}
@@ -4247,18 +4242,7 @@ public class NotificationManagerService extends SystemService {
// Fix the notification as best we can.
try {
- final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
- pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
- (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId);
- Notification.addFieldsFromContext(ai, notification);
-
- int canColorize = mPackageManagerClient.checkPermission(
- android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
- if (canColorize == PERMISSION_GRANTED) {
- notification.flags |= Notification.FLAG_CAN_COLORIZE;
- } else {
- notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
- }
+ fixNotification(notification, pkg, userId);
} catch (NameNotFoundException e) {
Slog.e(TAG, "Cannot create a context for sending app", e);
@@ -4359,6 +4343,33 @@ public class NotificationManagerService extends SystemService {
mHandler.post(new EnqueueNotificationRunnable(userId, r));
}
+ @VisibleForTesting
+ protected void fixNotification(Notification notification, String pkg, int userId)
+ throws NameNotFoundException {
+ final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
+ pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+ (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId);
+ Notification.addFieldsFromContext(ai, notification);
+
+ int canColorize = mPackageManagerClient.checkPermission(
+ android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
+ if (canColorize == PERMISSION_GRANTED) {
+ notification.flags |= Notification.FLAG_CAN_COLORIZE;
+ } else {
+ notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
+ }
+
+ if (ai.targetSdkVersion >= Build.VERSION_CODES.Q) {
+ int fullscreenIntentPermission = mPackageManagerClient.checkPermission(
+ android.Manifest.permission.USE_FULL_SCREEN_INTENT, pkg);
+ if (fullscreenIntentPermission != PERMISSION_GRANTED) {
+ notification.fullScreenIntent = null;
+ Log.w(TAG, "Package " + pkg +
+ ": Use of fullScreenIntent requires the USE_FULL_SCREEN_INTENT permission");
+ }
+ }
+ }
+
private void doChannelWarningToast(CharSequence toastText) {
Binder.withCleanCallingIdentity(() -> {
final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0;
@@ -4467,7 +4478,7 @@ public class NotificationManagerService extends SystemService {
Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
}
mSnoozeHelper.update(userId, r);
- savePolicyFile();
+ handleSavePolicyFile();
return false;
}
@@ -4598,7 +4609,7 @@ public class NotificationManagerService extends SystemService {
mSnoozeHelper.snooze(r, mDuration);
}
r.recordSnoozed();
- savePolicyFile();
+ handleSavePolicyFile();
}
}
@@ -4676,7 +4687,7 @@ public class NotificationManagerService extends SystemService {
if (mReason != REASON_SNOOZED) {
final boolean wasSnoozed = mSnoozeHelper.cancel(mUserId, mPkg, mTag, mId);
if (wasSnoozed) {
- savePolicyFile();
+ handleSavePolicyFile();
}
}
}
@@ -5691,6 +5702,16 @@ public class NotificationManagerService extends SystemService {
}
}
+ private void handleOnPackageChanged(boolean removingPackage, int changeUserId,
+ String[] pkgList, int[] uidList) {
+ mListeners.onPackagesChanged(removingPackage, pkgList, uidList);
+ mAssistants.onPackagesChanged(removingPackage, pkgList, uidList);
+ mConditionProviders.onPackagesChanged(removingPackage, pkgList, uidList);
+ mPreferencesHelper.onPackagesChanged(
+ removingPackage, changeUserId, pkgList, uidList);
+ handleSavePolicyFile();
+ }
+
protected class WorkerHandler extends Handler
{
public WorkerHandler(Looper looper) {
@@ -5703,13 +5724,10 @@ public class NotificationManagerService extends SystemService {
switch (msg.what)
{
case MESSAGE_DURATION_REACHED:
- handleDurationReached((ToastRecord)msg.obj);
+ handleDurationReached((ToastRecord) msg.obj);
break;
case MESSAGE_FINISH_TOKEN_TIMEOUT:
- handleKillTokenTimeout((ToastRecord)msg.obj);
- break;
- case MESSAGE_SAVE_POLICY_FILE:
- handleSavePolicyFile();
+ handleKillTokenTimeout((ToastRecord) msg.obj);
break;
case MESSAGE_SEND_RANKING_UPDATE:
handleSendRankingUpdate();
@@ -5720,6 +5738,12 @@ public class NotificationManagerService extends SystemService {
case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
handleListenerInterruptionFilterChanged(msg.arg1);
break;
+ case MESSAGE_ON_PACKAGE_CHANGED:
+ SomeArgs args = (SomeArgs) msg.obj;
+ handleOnPackageChanged((boolean) args.arg1, args.argi1, (String[]) args.arg2,
+ (int[]) args.arg3);
+ args.recycle();
+ break;
}
}
@@ -5735,6 +5759,16 @@ public class NotificationManagerService extends SystemService {
sendMessage(Message.obtain(this, cancelRunnable));
}
}
+
+ protected void scheduleOnPackageChanged(boolean removingPackage, int changeUserId,
+ String[] pkgList, int[] uidList) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = removingPackage;
+ args.argi1 = changeUserId;
+ args.arg2 = pkgList;
+ args.arg3 = uidList;
+ sendMessage(Message.obtain(this, MESSAGE_ON_PACKAGE_CHANGED, args));
+ }
}
private final class RankingHandlerWorker extends Handler implements RankingHandler
@@ -6212,7 +6246,7 @@ public class NotificationManagerService extends SystemService {
Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
}
mSnoozeHelper.repost(key);
- savePolicyFile();
+ handleSavePolicyFile();
}
@GuardedBy("mNotificationLock")
diff --git a/services/core/java/com/android/server/oemlock/OemLock.java b/services/core/java/com/android/server/oemlock/OemLock.java
index ee70c29b7da4..352884b606a0 100644
--- a/services/core/java/com/android/server/oemlock/OemLock.java
+++ b/services/core/java/com/android/server/oemlock/OemLock.java
@@ -19,6 +19,9 @@ package com.android.server.oemlock;
import android.annotation.Nullable;
abstract class OemLock {
+ @Nullable
+ abstract String getLockName();
+
abstract void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature);
abstract boolean isOemUnlockAllowedByCarrier();
diff --git a/services/core/java/com/android/server/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java
index a6200bf433e4..6e82c24a6e9b 100644
--- a/services/core/java/com/android/server/oemlock/OemLockService.java
+++ b/services/core/java/com/android/server/oemlock/OemLockService.java
@@ -113,6 +113,19 @@ public class OemLockService extends SystemService {
*/
private final IBinder mService = new IOemLockService.Stub() {
@Override
+ @Nullable
+ public String getLockName() {
+ enforceManageCarrierOemUnlockPermission();
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mOemLock.getLockName();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature) {
enforceManageCarrierOemUnlockPermission();
enforceUserIsAdmin();
diff --git a/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java b/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java
index d9362d4b0f28..a1c27d6432f1 100644
--- a/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java
+++ b/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java
@@ -40,6 +40,12 @@ class PersistentDataBlockLock extends OemLock {
}
@Override
+ @Nullable
+ String getLockName() {
+ return "";
+ }
+
+ @Override
void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature) {
// Note: this implementation does not require a signature
if (signature != null) {
diff --git a/services/core/java/com/android/server/oemlock/VendorLock.java b/services/core/java/com/android/server/oemlock/VendorLock.java
index 1b9de3612367..37540d039b9e 100644
--- a/services/core/java/com/android/server/oemlock/VendorLock.java
+++ b/services/core/java/com/android/server/oemlock/VendorLock.java
@@ -22,8 +22,6 @@ import android.hardware.oemlock.V1_0.IOemLock;
import android.hardware.oemlock.V1_0.OemLockSecureStatus;
import android.hardware.oemlock.V1_0.OemLockStatus;
import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
import android.util.Slog;
import java.util.ArrayList;
@@ -55,14 +53,49 @@ class VendorLock extends OemLock {
}
@Override
+ @Nullable
+ String getLockName() {
+ final Integer[] requestStatus = new Integer[1];
+ final String[] lockName = new String[1];
+
+ try {
+ mOemLock.getName((status, name) -> {
+ requestStatus[0] = status;
+ lockName[0] = name;
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get name from HAL", e);
+ throw e.rethrowFromSystemServer();
+ }
+
+ switch (requestStatus[0]) {
+ case OemLockStatus.OK:
+ // Success
+ return lockName[0];
+
+ case OemLockStatus.FAILED:
+ Slog.e(TAG, "Failed to get OEM lock name.");
+ return null;
+
+ default:
+ Slog.e(TAG, "Unknown return value indicates code is out of sync with HAL");
+ return null;
+ }
+ }
+
+ @Override
void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature) {
try {
- switch (mOemLock.setOemUnlockAllowedByCarrier(allowed, toByteArrayList(signature))) {
+ ArrayList<Byte> signatureBytes = toByteArrayList(signature);
+ switch (mOemLock.setOemUnlockAllowedByCarrier(allowed, signatureBytes)) {
case OemLockSecureStatus.OK:
Slog.i(TAG, "Updated carrier allows OEM lock state to: " + allowed);
return;
case OemLockSecureStatus.INVALID_SIGNATURE:
+ if (signatureBytes.isEmpty()) {
+ throw new IllegalArgumentException("Signature required for carrier unlock");
+ }
throw new SecurityException(
"Invalid signature used in attempt to carrier unlock");
@@ -154,9 +187,9 @@ class VendorLock extends OemLock {
}
}
- private ArrayList toByteArrayList(byte[] data) {
+ private ArrayList<Byte> toByteArrayList(byte[] data) {
if (data == null) {
- return null;
+ return new ArrayList<Byte>();
}
ArrayList<Byte> result = new ArrayList<Byte>(data.length);
for (final byte b : data) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6a6a5be53409..8abb5000d544 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -116,8 +116,7 @@ import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS;
-import static com.android.server.pm.permission.PermissionsState
- .PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
import android.Manifest;
import android.annotation.IntDef;
@@ -314,8 +313,7 @@ import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.permission.BasePermission;
import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
-import com.android.server.pm.permission.DefaultPermissionGrantPolicy
- .DefaultPermissionGrantedCallback;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
import com.android.server.pm.permission.PermissionManagerInternal;
import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
import com.android.server.pm.permission.PermissionManagerService;
@@ -374,6 +372,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import java.util.function.Predicate;
/**
@@ -20136,7 +20135,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (Process.isIsolated(uid)) {
return Zygote.MOUNT_EXTERNAL_NONE;
}
- if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (StorageManager.hasIsolatedStorage()) {
return checkUidPermission(WRITE_MEDIA_STORAGE, uid) == PERMISSION_GRANTED
? Zygote.MOUNT_EXTERNAL_FULL
: Zygote.MOUNT_EXTERNAL_WRITE;
@@ -23207,6 +23206,45 @@ public class PackageManagerService extends IPackageManager.Stub
throws IOException {
PackageManagerService.this.freeStorage(volumeUuid, bytes, storageFlags);
}
+
+ @Override
+ public void forEachPackage(Consumer<PackageParser.Package> actionLocked) {
+ PackageManagerService.this.forEachPackage(actionLocked);
+ }
+
+ @Override
+ public ArraySet<String> getEnabledComponents(String packageName, int userId) {
+ synchronized (mPackages) {
+ PackageSetting setting = mSettings.getPackageLPr(packageName);
+ if (setting == null) {
+ return new ArraySet<>();
+ }
+ return setting.getEnabledComponents(userId);
+ }
+ }
+
+ @Override
+ public ArraySet<String> getDisabledComponents(String packageName, int userId) {
+ synchronized (mPackages) {
+ PackageSetting setting = mSettings.getPackageLPr(packageName);
+ if (setting == null) {
+ return new ArraySet<>();
+ }
+ return setting.getDisabledComponents(userId);
+ }
+ }
+
+ @Override
+ public @PackageManager.EnabledState int getApplicationEnabledState(
+ String packageName, int userId) {
+ synchronized (mPackages) {
+ PackageSetting setting = mSettings.getPackageLPr(packageName);
+ if (setting == null) {
+ return COMPONENT_ENABLED_STATE_DEFAULT;
+ }
+ return setting.getEnabled(userId);
+ }
+ }
}
@GuardedBy("mPackages")
@@ -23329,6 +23367,15 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ void forEachPackage(Consumer<PackageParser.Package> actionLocked) {
+ synchronized (mPackages) {
+ int numPackages = mPackages.size();
+ for (int i = 0; i < numPackages; i++) {
+ actionLocked.accept(mPackages.valueAt(i));
+ }
+ }
+ }
+
private static void enforceSystemOrPhoneCaller(String tag) {
int callingUid = Binder.getCallingUid();
if (callingUid != Process.PHONE_UID && callingUid != Process.SYSTEM_UID) {
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 e9b9930600a0..68fe1d8a05f8 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -189,7 +189,7 @@ public final class DefaultPermissionGrantPolicy {
private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
static {
// STOPSHIP(b/112545973): remove once feature enabled by default
- if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (!StorageManager.hasIsolatedStorage()) {
STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
@@ -198,7 +198,7 @@ public final class DefaultPermissionGrantPolicy {
private static final Set<String> MEDIA_AURAL_PERMISSIONS = new ArraySet<>();
static {
// STOPSHIP(b/112545973): remove once feature enabled by default
- if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (StorageManager.hasIsolatedStorage()) {
MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_AUDIO);
}
}
@@ -206,7 +206,7 @@ public final class DefaultPermissionGrantPolicy {
private static final Set<String> MEDIA_VISUAL_PERMISSIONS = new ArraySet<>();
static {
// STOPSHIP(b/112545973): remove once feature enabled by default
- if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (StorageManager.hasIsolatedStorage()) {
MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VIDEO);
MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES);
}
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index b390eebf3d7e..b065470fe98c 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -30,20 +30,27 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.Signature;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.PackageUtils;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.BitUtils;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.FunctionalUtils;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
import java.util.ArrayList;
import java.util.Collections;
@@ -117,13 +124,18 @@ public class RoleManagerService extends SystemService {
@Override
public void onStartUser(@UserIdInt int userId) {
+ RoleUserState userState;
synchronized (mLock) {
- //TODO only call into PermissionController if it or system upgreaded (for boot time)
- getUserStateLocked(userId);
+ userState = getUserStateLocked(userId);
}
- //TODO consider calling grants only when certain conditions are met
- // such as OS or PermissionController upgrade
- if (RemoteRoleControllerService.DEBUG) {
+ String packagesHash = computeComponentStateHash(userId);
+ boolean needGrant;
+ synchronized (mLock) {
+ needGrant = !packagesHash.equals(userState.getLastGrantPackagesHashLocked());
+ }
+ if (needGrant) {
+ // Some vital packages state has changed since last role grant
+ // Run grants again
Slog.i(LOG_TAG, "Granting default permissions...");
CompletableFuture<Void> result = new CompletableFuture<>();
getControllerService(userId).onGrantDefaultRoles(
@@ -140,12 +152,47 @@ public class RoleManagerService extends SystemService {
});
try {
result.get(5, TimeUnit.SECONDS);
+ synchronized (mLock) {
+ userState.setLastGrantPackagesHashLocked(packagesHash);
+ }
} catch (InterruptedException | ExecutionException | TimeoutException e) {
Slog.e(LOG_TAG, "Failed to grant defaults for user " + userId, e);
}
+ } else if (RemoteRoleControllerService.DEBUG) {
+ Slog.i(LOG_TAG, "Already ran grants for package state " + packagesHash);
}
}
+ private String computeComponentStateHash(int userId) {
+ PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ pm.forEachPackage(FunctionalUtils.uncheckExceptions(pkg -> {
+ out.write(pkg.packageName.getBytes());
+ out.write(BitUtils.toBytes(pkg.getLongVersionCode()));
+ out.write(pm.getApplicationEnabledState(pkg.packageName, userId));
+
+ ArraySet<String> enabledComponents =
+ pm.getEnabledComponents(pkg.packageName, userId);
+ int numComponents = CollectionUtils.size(enabledComponents);
+ for (int i = 0; i < numComponents; i++) {
+ out.write(enabledComponents.valueAt(i).getBytes());
+ }
+
+ ArraySet<String> disabledComponents =
+ pm.getDisabledComponents(pkg.packageName, userId);
+ numComponents = CollectionUtils.size(disabledComponents);
+ for (int i = 0; i < numComponents; i++) {
+ out.write(disabledComponents.valueAt(i).getBytes());
+ }
+ for (Signature signature : pkg.mSigningDetails.signatures) {
+ out.write(signature.toByteArray());
+ }
+ }));
+
+ return PackageUtils.computeSha256Digest(out.toByteArray());
+ }
+
@GuardedBy("mLock")
@NonNull
private RoleUserState getUserStateLocked(@UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index 9c43f4d02ad0..f218d3a5834b 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -31,6 +31,7 @@ import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.function.pooled.PooledLambda;
import libcore.io.IoUtils;
@@ -63,6 +64,7 @@ public class RoleUserState {
private static final String TAG_HOLDER = "holder";
private static final String ATTRIBUTE_VERSION = "version";
private static final String ATTRIBUTE_NAME = "name";
+ private static final String ATTRIBUTE_PACKAGES_HASH = "packagesHash";
@UserIdInt
private final int mUserId;
@@ -70,11 +72,15 @@ public class RoleUserState {
@GuardedBy("RoleManagerService.mLock")
private int mVersion;
+ @GuardedBy("RoleManagerService.mLock")
+ private String mLastGrantPackagesHash = null;
+
/**
* Maps role names to its holders' package names. The values should never be null.
*/
@GuardedBy("RoleManagerService.mLock")
- private ArrayMap<String, ArraySet<String>> mRoles;
+ @Nullable
+ private ArrayMap<String, ArraySet<String>> mRoles = null;
@GuardedBy("RoleManagerService.mLock")
private boolean mDestroyed;
@@ -110,6 +116,23 @@ public class RoleUserState {
}
/**
+ * Get the hash representing the state of packages during the last time initial grants was run
+ */
+ @GuardedBy("RoleManagerService.mLock")
+ public String getLastGrantPackagesHashLocked() {
+ return mLastGrantPackagesHash;
+ }
+
+ /**
+ * Set the hash representing the state of packages during the last time initial grants was run
+ */
+ @GuardedBy("RoleManagerService.mLock")
+ public void setLastGrantPackagesHashLocked(String lastGrantPackagesHash) {
+ mLastGrantPackagesHash = lastGrantPackagesHash;
+ writeAsyncLocked();
+ }
+
+ /**
* Get whether the role is available.
*
* @param roleName the name of the role to get the holders for
@@ -227,11 +250,11 @@ public class RoleUserState {
* Schedule writing the state to file.
*/
@GuardedBy("RoleManagerService.mLock")
- private void writeAsyncLocked() {
+ void writeAsyncLocked() {
throwIfDestroyedLocked();
int version = mVersion;
ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
- for (int i = 0, size = mRoles.size(); i < size; ++i) {
+ for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) {
String roleName = mRoles.keyAt(i);
ArraySet<String> roleHolders = mRoles.valueAt(i);
roleHolders = new ArraySet<>(roleHolders);
@@ -240,11 +263,12 @@ public class RoleUserState {
mWriteHandler.removeCallbacksAndMessages(null);
// TODO: Throttle writes.
mWriteHandler.sendMessage(PooledLambda.obtainMessage(
- RoleUserState::writeSync, this, version, roles));
+ RoleUserState::writeSync, this, version, roles, mLastGrantPackagesHash));
}
@WorkerThread
- private void writeSync(int version, @NonNull ArrayMap<String, ArraySet<String>> roles) {
+ private void writeSync(int version, @NonNull ArrayMap<String, ArraySet<String>> roles,
+ String packagesHash) {
AtomicFile atomicFile = new AtomicFile(getFile(mUserId), "roles-" + mUserId);
FileOutputStream out = null;
try {
@@ -256,7 +280,7 @@ public class RoleUserState {
"http://xmlpull.org/v1/doc/features.html#indent-output", true);
serializer.startDocument(null, true);
- serializeRoles(serializer, version, roles);
+ serializeRoles(serializer, version, roles, packagesHash);
serializer.endDocument();
atomicFile.finishWrite(out);
@@ -272,9 +296,11 @@ public class RoleUserState {
@WorkerThread
private void serializeRoles(@NonNull XmlSerializer serializer, int version,
- @NonNull ArrayMap<String, ArraySet<String>> roles) throws IOException {
+ @NonNull ArrayMap<String, ArraySet<String>> roles, String packagesHash)
+ throws IOException {
serializer.startTag(null, TAG_ROLES);
serializer.attribute(null, ATTRIBUTE_VERSION, Integer.toString(version));
+ serializer.attribute(null, ATTRIBUTE_PACKAGES_HASH, packagesHash);
for (int i = 0, size = roles.size(); i < size; ++i) {
String roleName = roles.keyAt(i);
ArraySet<String> roleHolders = roles.valueAt(i);
@@ -341,6 +367,7 @@ public class RoleUserState {
private void parseRolesLocked(@NonNull XmlPullParser parser) throws IOException,
XmlPullParserException {
mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_VERSION));
+ mLastGrantPackagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH);
mRoles = new ArrayMap<>();
int type;
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 21adc47b1e30..f0ebb7512015 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -195,10 +195,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
"/system/bin/traced", // Perfetto.
"/system/bin/traced_probes", // Perfetto.
"webview_zygote",
- // Temporarily excluded zygote to investigate its forking consequences in
- // NativeProcessMemoryState.
- // "zygote",
- // "zygote64",
+ "zygote",
+ "zygote64",
};
private static final int CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES = 8;
@@ -1090,6 +1088,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private void pullNativeProcessMemoryState(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
+ final List<String> processNames = Arrays.asList(MEMORY_INTERESTING_NATIVE_PROCESSES);
int[] pids = getPidsForCommands(MEMORY_INTERESTING_NATIVE_PROCESSES);
for (int i = 0; i < pids.length; i++) {
int pid = pids[i];
@@ -1099,6 +1098,12 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
int uid = getUidForPid(pid);
String processName = readCmdlineFromProcfs(pid);
+ // Sometimes we get here processName that is not included in the whitelist. It comes
+ // from forking the zygote for an app. We can ignore that sample because this process
+ // is collected by ProcessMemoryState.
+ if (!processNames.contains(processName)) {
+ continue;
+ }
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(uid);
e.writeString(processName);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 0d66a2c8b442..e645b84e83a0 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -24,7 +24,7 @@ import android.app.StatusBarManager;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Rect;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -598,8 +598,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
- boolean requireConfirmation, int userId) {
+ public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
+ int type, boolean requireConfirmation, int userId) {
enforceBiometricDialog();
if (mBar != null) {
try {
@@ -654,6 +654,17 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
}
@Override
+ public void showBiometricTryAgain() {
+ enforceBiometricDialog();
+ if (mBar != null) {
+ try {
+ mBar.showBiometricTryAgain();
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ @Override
public void disable(int what, IBinder token, String pkg) {
disableForUser(what, token, pkg, mCurrentUserId);
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index d5e59c8dfd6a..1163d3916cb1 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -626,7 +626,7 @@ public final class TvInputManagerService extends SystemService {
updateServiceConnectionLocked(serviceState.component, userId);
}
- private void createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
+ private boolean createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
int userId) {
UserState userState = getOrCreateUserStateLocked(userId);
SessionState sessionState = userState.sessionStateMap.get(sessionToken);
@@ -638,6 +638,7 @@ public final class TvInputManagerService extends SystemService {
// Set up a callback to send the session token.
ITvInputSessionCallback callback = new SessionCallback(sessionState, channels);
+ boolean created = true;
// Create a session. When failed, send a null token immediately.
try {
if (sessionState.isRecordingSession) {
@@ -647,11 +648,12 @@ public final class TvInputManagerService extends SystemService {
}
} catch (RemoteException e) {
Slog.e(TAG, "error in createSession", e);
- removeSessionStateLocked(sessionToken, userId);
sendSessionTokenToClientLocked(sessionState.client, sessionState.inputId, null,
null, sessionState.seq);
+ created = false;
}
channels[1].dispose();
+ return created;
}
private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
@@ -1193,8 +1195,10 @@ public final class TvInputManagerService extends SystemService {
serviceState.sessionTokens.add(sessionToken);
if (serviceState.service != null) {
- createSessionInternalLocked(serviceState.service, sessionToken,
- resolvedUserId);
+ if (!createSessionInternalLocked(serviceState.service, sessionToken,
+ resolvedUserId)) {
+ removeSessionStateLocked(sessionToken, resolvedUserId);
+ }
} else {
updateServiceConnectionLocked(info.getComponent(), resolvedUserId);
}
@@ -2282,9 +2286,17 @@ public final class TvInputManagerService extends SystemService {
}
}
+ List<IBinder> tokensToBeRemoved = new ArrayList<>();
+
// And create sessions, if any.
for (IBinder sessionToken : serviceState.sessionTokens) {
- createSessionInternalLocked(serviceState.service, sessionToken, mUserId);
+ if (!createSessionInternalLocked(serviceState.service, sessionToken, mUserId)) {
+ tokensToBeRemoved.add(sessionToken);
+ }
+ }
+
+ for (IBinder sessionToken : tokensToBeRemoved) {
+ removeSessionStateLocked(sessionToken, mUserId);
}
for (TvInputState inputState : userState.inputMap.values()) {
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 8045fd50adee..f3c5630b53f0 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -662,31 +662,29 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags,
int filterCallingUid) {
- synchronized (mService.mGlobalLock) {
- try {
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
- int modifiedFlags = flags
- | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS;
- if (intent.isWebIntent()
- || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
- modifiedFlags |= PackageManager.MATCH_INSTANT;
- }
+ try {
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
+ int modifiedFlags = flags
+ | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS;
+ if (intent.isWebIntent()
+ || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
+ modifiedFlags |= PackageManager.MATCH_INSTANT;
+ }
- // In order to allow cross-profile lookup, we clear the calling identity here.
- // Note the binder identity won't affect the result, but filterCallingUid will.
+ // In order to allow cross-profile lookup, we clear the calling identity here.
+ // Note the binder identity won't affect the result, but filterCallingUid will.
- // Cross-user/profile call check are done at the entry points
- // (e.g. AMS.startActivityAsUser).
- final long token = Binder.clearCallingIdentity();
- try {
- return mService.getPackageManagerInternalLocked().resolveIntent(
- intent, resolvedType, modifiedFlags, userId, true, filterCallingUid);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ // Cross-user/profile call check are done at the entry points
+ // (e.g. AMS.startActivityAsUser).
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mService.getPackageManagerInternalLocked().resolveIntent(
+ intent, resolvedType, modifiedFlags, userId, true, filterCallingUid);
} finally {
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Binder.restoreCallingIdentity(token);
}
+ } finally {
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 4ad97bb99b7e..0967afda6d2d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -190,6 +190,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.IUserManager;
import android.os.LocaleList;
+import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.PowerManager;
@@ -246,7 +247,6 @@ import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
-import com.android.server.DisplayThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
@@ -753,9 +753,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return mGlobalLock;
}
- public void setActivityManagerService(IntentFirewall intentFirewall,
- PendingIntentController intentController) {
- mH = new H();
+ public void initialize(IntentFirewall intentFirewall, PendingIntentController intentController,
+ Looper looper) {
+ mH = new H(looper);
mUiHandler = new UiHandler();
mIntentFirewall = intentFirewall;
final File systemDir = SystemServiceManager.ensureSystemDir();
@@ -5593,8 +5593,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_SUPERVISOR_STACK_MSG = 200;
- public H() {
- super(DisplayThread.get().getLooper());
+ H(Looper looper) {
+ super(looper);
}
@Override
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 871ceaf4d231..05e82676a40a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -29,12 +29,12 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_PRIVATE;
+import static android.view.InsetsState.TYPE_IME;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.View.GONE;
-import static android.view.InsetsState.TYPE_IME;
import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_TOP;
@@ -4736,4 +4736,16 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|| mDisplayId == mWmService.mVr2dDisplayId
|| mWmService.mForceDesktopModeOnExternalDisplays;
}
+
+ /**
+ * Re-parent the DisplayContent's top surfaces, {@link #mWindowingLayer} and
+ * {@link #mOverlayLayer} to the specified surfaceControl.
+ *
+ * @param surfaceControlHandle The handle for the new SurfaceControl, where the DisplayContent's
+ * surfaces will be re-parented to.
+ */
+ void reparentDisplayContent(IBinder surfaceControlHandle) {
+ mPendingTransaction.reparent(mWindowingLayer, surfaceControlHandle)
+ .reparent(mOverlayLayer, surfaceControlHandle);
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4085f3d8062a..52b24b3e7307 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -66,6 +66,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN;
import static com.android.server.LockGuard.INDEX_WINDOW;
@@ -208,6 +209,7 @@ import android.view.IWindowSessionCallback;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEventReceiver;
+import android.view.InsetsState;
import android.view.KeyEvent;
import android.view.MagnificationSpec;
import android.view.MotionEvent;
@@ -218,7 +220,6 @@ import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
import android.view.WindowContentFrameStats;
-import android.view.InsetsState;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.RemoveContentMode;
@@ -5640,22 +5641,12 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- // TODO: Make the callers use getNavBarPosition(int) only.
- /**
- * Used by SystemUI and shared SystemUI libraries.
- * @see DisplayPolicy#getNavBarPosition()
- */
- @Override
- @WindowManagerPolicy.NavigationBarPosition
- public int getNavBarPosition() {
- return getNavBarPosition(Display.DEFAULT_DISPLAY);
- }
-
/**
* Used by ActivityManager to determine where to position an app with aspect ratio shorter then
* the screen is.
* @see DisplayPolicy#getNavBarPosition()
*/
+ @Override
@WindowManagerPolicy.NavigationBarPosition
public int getNavBarPosition(int displayId) {
synchronized (mGlobalLock) {
@@ -5665,7 +5656,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (displayContent == null) {
Slog.w(TAG, "getNavBarPosition with invalid displayId=" + displayId
+ " callers=" + Debug.getCallers(3));
- return -1;
+ return NAV_BAR_INVALID;
}
displayContent.performLayout(false /* initial */,
false /* updateInputWindows */);
@@ -7457,4 +7448,29 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
}
+
+ @Override
+ public void reparentDisplayContent(int displayId, IBinder surfaceControlHandle) {
+ final Display display = mDisplayManager.getDisplay(displayId);
+ if (display == null) {
+ throw new IllegalArgumentException(
+ "Can't reparent display for non-existent displayId: " + displayId);
+ }
+
+ final int callingUid = Binder.getCallingUid();
+ final int displayOwnerUid = display.getOwnerUid();
+ if (callingUid != displayOwnerUid) {
+ throw new SecurityException("Only owner of the display can reparent surfaces to it.");
+ }
+
+ synchronized (mGlobalLock) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
+ displayContent.reparentDisplayContent(surfaceControlHandle);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index b36a8a7cdf19..43d2dcf7e0d1 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1750,7 +1750,7 @@ int register_android_server_InputManager(JNIEnv* env) {
jclass clazz;
FIND_CLASS(clazz, "com/android/server/input/InputManagerService");
- gServiceClassInfo.clazz = clazz;
+ gServiceClassInfo.clazz = reinterpret_cast<jclass>(env->NewGlobalRef(clazz));
GET_METHOD_ID(gServiceClassInfo.notifyConfigurationChanged, clazz,
"notifyConfigurationChanged", "(J)V");
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index ead9731782e8..3a56419f67ae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -34,7 +34,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
@@ -403,11 +402,10 @@ class ActivityTestsBase {
doReturn(true).when(this).isBackgroundActivityStartsEnabled();
}
- void setActivityManagerService(IntentFirewall intentFirewall,
- PendingIntentController intentController, ActivityManagerInternal amInternal,
- WindowManagerService wm) {
+ void setup(IntentFirewall intentFirewall, PendingIntentController intentController,
+ ActivityManagerInternal amInternal, WindowManagerService wm, Looper looper) {
mAmInternal = amInternal;
- setActivityManagerService(intentFirewall, intentController);
+ initialize(intentFirewall, intentController, looper);
initRootActivityContainerMocks(wm);
setWindowManager(wm);
}
@@ -517,8 +515,8 @@ class ActivityTestsBase {
mWindowManager = prepareMockWindowManager();
mUgmInternal = mock(UriGrantsManagerInternal.class);
- atm.setActivityManagerService(mIntentFirewall, mPendingIntentController,
- new LocalService(), mWindowManager);
+ atm.setup(mIntentFirewall, mPendingIntentController, new LocalService(), mWindowManager,
+ testInjector.mHandlerThread.getLooper());
mActivityTaskManager = atm;
mAtmInternal = atm.mInternal;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ef290d23fcdb..d54da0905e18 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4217,7 +4217,8 @@ public class TelephonyManager {
}
/**
- * Returns the voice mail count for a subscription. Return 0 if unavailable.
+ * Returns the voice mail count for a subscription. Return 0 if unavailable or the caller does
+ * not have the READ_PHONE_STATE permission.
* @param subId whose voice message count is returned
* @hide
*/
@@ -4228,7 +4229,7 @@ public class TelephonyManager {
ITelephony telephony = getITelephony();
if (telephony == null)
return 0;
- return telephony.getVoiceMessageCountForSubscriber(subId);
+ return telephony.getVoiceMessageCountForSubscriber(subId, getOpPackageName());
} catch (RemoteException ex) {
return 0;
} catch (NullPointerException ex) {
@@ -8582,7 +8583,8 @@ public class TelephonyManager {
/**
* Return the application ID for the uicc application type like {@link #APPTYPE_CSIM}.
- * All uicc applications are uniquely identified by application ID. See ETSI 102.221 and 101.220
+ * All uicc applications are uniquely identified by application ID, represented by the hex
+ * string. e.g, A00000015141434C00. See ETSI 102.221 and 101.220
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
*
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index fc42de58319e..32e939a0c925 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -393,16 +393,11 @@ interface ITelephony {
int getDataActivationState(int subId, String callingPackage);
/**
- * Returns the unread count of voicemails
- */
- int getVoiceMessageCount();
-
- /**
* Returns the unread count of voicemails for a subId.
* @param subId user preferred subId.
* Returns the unread count of voicemails
*/
- int getVoiceMessageCountForSubscriber(int subId);
+ int getVoiceMessageCountForSubscriber(int subId, String callingPackage);
/**
* Returns true if current state supports both voice and data
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
index fdc800bcc177..2f1e53ca5065 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists.py
@@ -15,23 +15,54 @@
# limitations under the License.
"""
Generate API lists for non-SDK API enforcement.
-
-usage: generate-hiddenapi-lists.py [-h]
- --input-public INPUT_PUBLIC
- --input-private INPUT_PRIVATE
- [--input-whitelists [INPUT_WHITELISTS [INPUT_WHITELISTS ...]]]
- [--input-greylists [INPUT_GREYLISTS [INPUT_GREYLISTS ...]]]
- [--input-blacklists [INPUT_BLACKLISTS [INPUT_BLACKLISTS ...]]]
- --output-whitelist OUTPUT_WHITELIST
- --output-light-greylist OUTPUT_LIGHT_GREYLIST
- --output-dark-greylist OUTPUT_DARK_GREYLIST
- --output-blacklist OUTPUT_BLACKLIST
"""
import argparse
import os
import sys
import re
+# Names of flags recognized by the `hiddenapi` tool.
+FLAG_WHITELIST = "whitelist"
+FLAG_GREYLIST = "greylist"
+FLAG_BLACKLIST = "blacklist"
+FLAG_GREYLIST_MAX_O = "greylist-max-o"
+
+# List of all known flags.
+FLAGS = [
+ FLAG_WHITELIST,
+ FLAG_GREYLIST,
+ FLAG_BLACKLIST,
+ FLAG_GREYLIST_MAX_O,
+]
+FLAGS_SET = set(FLAGS)
+
+# Suffix used in command line args to express that only known and
+# otherwise unassigned entries should be assign the given flag.
+# For example, the P dark greylist is checked in as it was in P,
+# but signatures have changes since then. The flag instructs this
+# script to skip any entries which do not exist any more.
+FLAG_IGNORE_CONFLICTS_SUFFIX = "-ignore-conflicts"
+
+# Regex patterns of fields/methods used in serialization. These are
+# considered public API despite being hidden.
+SERIALIZATION_PATTERNS = [
+ r'readObject\(Ljava/io/ObjectInputStream;\)V',
+ r'readObjectNoData\(\)V',
+ r'readResolve\(\)Ljava/lang/Object;',
+ r'serialVersionUID:J',
+ r'serialPersistentFields:\[Ljava/io/ObjectStreamField;',
+ r'writeObject\(Ljava/io/ObjectOutputStream;\)V',
+ r'writeReplace\(\)Ljava/lang/Object;',
+]
+
+# Single regex used to match serialization API. It combines all the
+# SERIALIZATION_PATTERNS into a single regular expression.
+SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) + r')$')
+
+# Predicates to be used with filter_apis.
+IS_UNASSIGNED = lambda api, flags: not flags
+IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api)
+
def get_args():
"""Parses command line arguments.
@@ -39,21 +70,21 @@ def get_args():
Namespace: dictionary of parsed arguments
"""
parser = argparse.ArgumentParser()
- parser.add_argument('--input-public', required=True, help='List of all public members')
- parser.add_argument('--input-private', required=True, help='List of all private members')
- parser.add_argument(
- '--input-whitelists', nargs='*',
- help='Lists of members to force on whitelist')
- parser.add_argument(
- '--input-greylists', nargs='*',
- help='Lists of members to force on light greylist')
- parser.add_argument(
- '--input-blacklists', nargs='*',
- help='Lists of members to force on blacklist')
- parser.add_argument('--output-whitelist', required=True)
- parser.add_argument('--output-light-greylist', required=True)
- parser.add_argument('--output-dark-greylist', required=True)
- parser.add_argument('--output-blacklist', required=True)
+ parser.add_argument('--output', required=True)
+ parser.add_argument('--public', required=True, help='list of all public entries')
+ parser.add_argument('--private', required=True, help='list of all private entries')
+ parser.add_argument('--csv', nargs='*', default=[], metavar='CSV_FILE',
+ help='CSV files to be merged into output')
+
+ for flag in FLAGS:
+ ignore_conflicts_flag = flag + FLAG_IGNORE_CONFLICTS_SUFFIX
+ parser.add_argument('--' + flag, dest=flag, nargs='*', default=[], metavar='TXT_FILE',
+ help='lists of entries with flag "' + flag + '"')
+ parser.add_argument('--' + ignore_conflicts_flag, dest=ignore_conflicts_flag, nargs='*',
+ default=[], metavar='TXT_FILE',
+ help='lists of entries with flag "' + flag +
+ '". skip entry if missing or flag conflict.')
+
return parser.parse_args()
def read_lines(filename):
@@ -65,10 +96,13 @@ def read_lines(filename):
filename (string): Path to the file to read from.
Returns:
- list: Lines of the loaded file as a list of strings.
+ Lines of the file as a list of string.
"""
with open(filename, 'r') as f:
- return filter(lambda line: not line.startswith('#'), f.readlines())
+ lines = f.readlines();
+ lines = filter(lambda line: not line.startswith('#'), lines)
+ lines = map(lambda line: line.strip(), lines)
+ return set(lines)
def write_lines(filename, lines):
"""Writes list of lines into a file, overwriting the file it it exists.
@@ -77,167 +111,168 @@ def write_lines(filename, lines):
filename (string): Path to the file to be writting into.
lines (list): List of strings to write into the file.
"""
+ lines = map(lambda line: line + '\n', lines)
with open(filename, 'w') as f:
f.writelines(lines)
-def move_between_sets(subset, src, dst, source = "<unknown>"):
- """Removes a subset of elements from one set and add it to another.
-
- Args:
- subset (set): The subset of `src` to be moved from `src` to `dst`.
- src (set): Source set. Must be a superset of `subset`.
- dst (set): Destination set. Must be disjoint with `subset`.
- """
- assert src.issuperset(subset), (
- "Error processing: {}\n"
- "The following entries were not found:\n"
- "{}"
- "Please visit go/hiddenapi for more information.").format(
- source, "".join(map(lambda x: " " + str(x), subset.difference(src))))
- assert dst.isdisjoint(subset)
- # Order matters if `src` and `subset` are the same object.
- dst.update(subset)
- src.difference_update(subset)
-
-def get_package_name(signature):
- """Returns the package name prefix of a class member signature.
-
- Example: "Ljava/lang/String;->hashCode()J" --> "Ljava/lang/"
-
- Args:
- signature (string): Member signature
-
- Returns
- string: Package name of the given member
- """
- class_name_end = signature.find("->")
- assert class_name_end != -1, "Invalid signature: {}".format(signature)
- package_name_end = signature.rfind("/", 0, class_name_end)
- assert package_name_end != -1, "Invalid signature: {}".format(signature)
- return signature[:package_name_end + 1]
-
-def all_package_names(*args):
- """Returns a set of packages names in given lists of member signatures.
-
- Example: args = [ set([ "Lpkg1/ClassA;->foo()V", "Lpkg2/ClassB;->bar()J" ]),
- set([ "Lpkg1/ClassC;->baz()Z" ]) ]
- return value = set([ "Lpkg1/", "Lpkg2" ])
-
- Args:
- *args (list): List of sets to iterate over and extract the package names
- of its elements (member signatures)
-
- Returns:
- set: All package names extracted from the given lists of signatures.
- """
- packages = set()
- for arg in args:
- packages = packages.union(map(get_package_name, arg))
- return packages
-
-def move_all(src, dst):
- """Moves all elements of one set to another.
-
- Args:
- src (set): Source set. Will become empty.
- dst (set): Destination set. Will contain all elements of `src`.
- """
- move_between_sets(src, src, dst)
-
-def move_from_files(filenames, src, dst):
- """Loads member signatures from a list of files and moves them to a given set.
-
- Opens files in `filenames`, reads all their lines and moves those from `src`
- set to `dst` set.
-
- Args:
- filenames (list): List of paths to files to be loaded.
- src (set): Set that loaded lines should be moved from.
- dst (set): Set that loaded lines should be moved to.
- """
- if filenames:
- for filename in filenames:
- move_between_sets(set(read_lines(filename)), src, dst, filename)
-
-def move_serialization(src, dst):
- """Moves all members matching serialization API signatures between given sets.
-
- Args:
- src (set): Set that will be searched for serialization API and that API
- will be removed from it.
- dst (set): Set that serialization API will be moved to.
- """
- serialization_patterns = [
- r'readObject\(Ljava/io/ObjectInputStream;\)V',
- r'readObjectNoData\(\)V',
- r'readResolve\(\)Ljava/lang/Object;',
- r'serialVersionUID:J',
- r'serialPersistentFields:\[Ljava/io/ObjectStreamField;',
- r'writeObject\(Ljava/io/ObjectOutputStream;\)V',
- r'writeReplace\(\)Ljava/lang/Object;',
- ]
- regex = re.compile(r'.*->(' + '|'.join(serialization_patterns) + r')$')
- move_between_sets(filter(lambda api: regex.match(api), src), src, dst)
-
-def move_from_packages(packages, src, dst):
- """Moves all members of given package names from one set to another.
-
- Args:
- packages (list): List of string package names.
- src (set): Set that will be searched for API matching one of the given
- package names. Surch API will be removed from the set.
- dst (set): Set that matching API will be moved to.
- """
- move_between_sets(filter(lambda api: get_package_name(api) in packages, src), src, dst)
+class FlagsDict:
+ def __init__(self, public_api, private_api):
+ # Bootstrap the entries dictionary.
+
+ # Check that the two sets do not overlap.
+ public_api_set = set(public_api)
+ private_api_set = set(private_api)
+ assert public_api_set.isdisjoint(private_api_set), (
+ "Lists of public and private API overlap. " +
+ "This suggests an issue with the `hiddenapi` build tool.")
+
+ # Compute the whole key set
+ self._dict_keyset = public_api_set.union(private_api_set)
+
+ # Create a dict that creates entries for both public and private API,
+ # and assigns public API to the whitelist.
+ self._dict = {}
+ for api in public_api:
+ self._dict[api] = set([ FLAG_WHITELIST ])
+ for api in private_api:
+ self._dict[api] = set()
+
+ def _check_entries_set(self, keys_subset, source):
+ assert isinstance(keys_subset, set)
+ assert keys_subset.issubset(self._dict_keyset), (
+ "Error processing: {}\n"
+ "The following entries were unexpected:\n"
+ "{}"
+ "Please visit go/hiddenapi for more information.").format(
+ source, "".join(map(lambda x: " " + str(x), keys_subset - self._dict_keyset)))
+
+ def _check_flags_set(self, flags_subset, source):
+ assert isinstance(flags_subset, set)
+ assert flags_subset.issubset(FLAGS_SET), (
+ "Error processing: {}\n"
+ "The following flags were not recognized: \n"
+ "{}\n"
+ "Please visit go/hiddenapi for more information.").format(
+ source, "\n".join(flags_subset - FLAGS_SET))
+
+ def filter_apis(self, filter_fn):
+ """Returns APIs which match a given predicate.
+
+ This is a helper function which allows to filter on both signatures (keys) and
+ flags (values). The built-in filter() invokes the lambda only with dict's keys.
+
+ Args:
+ filter_fn : Function which takes two arguments (signature/flags) and returns a boolean.
+
+ Returns:
+ A set of APIs which match the predicate.
+ """
+ return set(filter(lambda x: filter_fn(x, self._dict[x]), self._dict_keyset))
+
+ def get_valid_subset_of_unassigned_apis(self, api_subset):
+ """Sanitizes a key set input to only include keys which exist in the dictionary
+ and have not been assigned any flags.
+
+ Args:
+ entries_subset (set/list): Key set to be sanitized.
+
+ Returns:
+ Sanitized key set.
+ """
+ assert isinstance(api_subset, set)
+ return api_subset.intersection(self.filter_apis(IS_UNASSIGNED))
+
+ def generate_csv(self):
+ """Constructs CSV entries from a dictionary.
+
+ Returns:
+ List of lines comprising a CSV file. See "parse_and_merge_csv" for format description.
+ """
+ return sorted(map(lambda api: ",".join([api] + sorted(self._dict[api])), self._dict))
+
+ def parse_and_merge_csv(self, csv_lines, source = "<unknown>"):
+ """Parses CSV entries and merges them into a given dictionary.
+
+ The expected CSV format is:
+ <api signature>,<flag1>,<flag2>,...,<flagN>
+
+ Args:
+ csv_lines (list of strings): Lines read from a CSV file.
+ source (string): Origin of `csv_lines`. Will be printed in error messages.
+
+ Throws:
+ AssertionError if parsed API signatures of flags are invalid.
+ """
+ # Split CSV lines into arrays of values.
+ csv_values = [ line.split(',') for line in csv_lines ]
+
+ # Check that all entries exist in the dict.
+ csv_keys = set([ csv[0] for csv in csv_values ])
+ self._check_entries_set(csv_keys, source)
+
+ # Check that all flags are known.
+ csv_flags = set(reduce(lambda x, y: set(x).union(y), [ csv[1:] for csv in csv_values ], []))
+ self._check_flags_set(csv_flags, source)
+
+ # Iterate over all CSV lines, find entry in dict and append flags to it.
+ for csv in csv_values:
+ self._dict[csv[0]].update(csv[1:])
+
+ def assign_flag(self, flag, apis, source="<unknown>"):
+ """Assigns a flag to given subset of entries.
+
+ Args:
+ flag (string): One of FLAGS.
+ apis (set): Subset of APIs to recieve the flag.
+ source (string): Origin of `entries_subset`. Will be printed in error messages.
+
+ Throws:
+ AssertionError if parsed API signatures of flags are invalid.
+ """
+ # Check that all APIs exist in the dict.
+ self._check_entries_set(apis, source)
+
+ # Check that the flag is known.
+ self._check_flags_set(set([ flag ]), source)
+
+ # Iterate over the API subset, find each entry in dict and assign the flag to it.
+ for api in apis:
+ self._dict[api].add(flag)
def main(argv):
- args = get_args()
-
- # Initialize API sets by loading lists of public and private API. Public API
- # are all members resolvable from SDK API stubs, other members are private.
- # As an optimization, skip the step of moving public API from a full set of
- # members and start with a populated whitelist.
- whitelist = set(read_lines(args.input_public))
- uncategorized = set(read_lines(args.input_private))
- light_greylist = set()
- dark_greylist = set()
- blacklist = set()
-
- # Assert that there is no overlap between public and private API.
- assert whitelist.isdisjoint(uncategorized)
- num_all_api = len(whitelist) + len(uncategorized)
-
- # Read all files which manually assign members to specific lists.
- move_from_files(args.input_whitelists, uncategorized, whitelist)
- move_from_files(args.input_greylists, uncategorized, light_greylist)
- move_from_files(args.input_blacklists, uncategorized, blacklist)
-
- # Iterate over all uncategorized members and move serialization API to whitelist.
- move_serialization(uncategorized, whitelist)
-
- # Extract package names of members from whitelist and light greylist, which
- # are assumed to have been finalized at this point. Assign all uncategorized
- # members from the same packages to the dark greylist.
- dark_greylist_packages = all_package_names(whitelist, light_greylist)
- move_from_packages(dark_greylist_packages, uncategorized, dark_greylist)
-
- # Assign all uncategorized members to the blacklist.
- move_all(uncategorized, blacklist)
-
- # Assert we have not missed anything.
- assert whitelist.isdisjoint(light_greylist)
- assert whitelist.isdisjoint(dark_greylist)
- assert whitelist.isdisjoint(blacklist)
- assert light_greylist.isdisjoint(dark_greylist)
- assert light_greylist.isdisjoint(blacklist)
- assert dark_greylist.isdisjoint(blacklist)
- assert num_all_api == len(whitelist) + len(light_greylist) + len(dark_greylist) + len(blacklist)
-
- # Write final lists to disk.
- write_lines(args.output_whitelist, whitelist)
- write_lines(args.output_light_greylist, light_greylist)
- write_lines(args.output_dark_greylist, dark_greylist)
- write_lines(args.output_blacklist, blacklist)
+ # Parse arguments.
+ args = vars(get_args())
+
+ flags = FlagsDict(read_lines(args["public"]), read_lines(args["private"]))
+
+ # Combine inputs which do not require any particular order.
+ # (1) Assign serialization API to whitelist.
+ flags.assign_flag(FLAG_WHITELIST, flags.filter_apis(IS_SERIALIZATION))
+
+ # (2) Merge input CSV files into the dictionary.
+ for filename in args["csv"]:
+ flags.parse_and_merge_csv(read_lines(filename), filename)
+
+ # (3) Merge text files with a known flag into the dictionary.
+ for flag in FLAGS:
+ for filename in args[flag]:
+ flags.assign_flag(flag, read_lines(filename), filename)
+
+ # Merge text files where conflicts should be ignored.
+ # This will only assign the given flag if:
+ # (a) the entry exists, and
+ # (b) it has not been assigned any other flag.
+ # Because of (b), this must run after all strict assignments have been performed.
+ for flag in FLAGS:
+ for filename in args[flag + FLAG_IGNORE_CONFLICTS_SUFFIX]:
+ valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(filename))
+ flags.assign_flag(flag, valid_entries, filename)
+
+ # Assign all remaining entries to the blacklist.
+ flags.assign_flag(FLAG_BLACKLIST, flags.filter_apis(IS_UNASSIGNED))
+
+ # Write output.
+ write_lines(args["output"], flags.generate_csv())
if __name__ == "__main__":
main(sys.argv)
diff --git a/tools/hiddenapi/generate_hiddenapi_lists_test.py b/tools/hiddenapi/generate_hiddenapi_lists_test.py
index 4716241940b5..249f37db5a82 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists_test.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists_test.py
@@ -2,14 +2,14 @@
#
# Copyright (C) 2018 The Android Open Source Project
#
-# Licensed under the Apache License, Version 2.0 (the "License");
+# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
+# 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.
@@ -18,90 +18,90 @@ import unittest
from generate_hiddenapi_lists import *
class TestHiddenapiListGeneration(unittest.TestCase):
+ def test_init(self):
+ # Check empty lists
+ flags = FlagsDict([], [])
+ self.assertEquals(flags.generate_csv(), [])
- def test_move_between_sets(self):
- A = set([1, 2, 3, 4])
- B = set([5, 6, 7, 8])
- move_between_sets(set([2, 4]), A, B)
- self.assertEqual(A, set([1, 3]))
- self.assertEqual(B, set([2, 4, 5, 6, 7, 8]))
-
- def test_move_between_sets_fail_not_superset(self):
- A = set([1, 2, 3, 4])
- B = set([5, 6, 7, 8])
- with self.assertRaises(AssertionError) as ar:
- move_between_sets(set([0, 2]), A, B)
-
- def test_move_between_sets_fail_not_disjoint(self):
- A = set([1, 2, 3, 4])
- B = set([4, 5, 6, 7, 8])
- with self.assertRaises(AssertionError) as ar:
- move_between_sets(set([1, 4]), A, B)
-
- def test_get_package_name(self):
- self.assertEqual(get_package_name("Ljava/lang/String;->clone()V"), "Ljava/lang/")
-
- def test_get_package_name_fail_no_arrow(self):
- with self.assertRaises(AssertionError) as ar:
- get_package_name("Ljava/lang/String;-clone()V")
- with self.assertRaises(AssertionError) as ar:
- get_package_name("Ljava/lang/String;>clone()V")
- with self.assertRaises(AssertionError) as ar:
- get_package_name("Ljava/lang/String;__clone()V")
-
- def test_get_package_name_fail_no_package(self):
- with self.assertRaises(AssertionError) as ar:
- get_package_name("LString;->clone()V")
-
- def test_all_package_names(self):
- self.assertEqual(all_package_names(), set())
- self.assertEqual(all_package_names(set(["Lfoo/Bar;->baz()V"])), set(["Lfoo/"]))
- self.assertEqual(
- all_package_names(set(["Lfoo/Bar;->baz()V", "Lfoo/BarX;->bazx()I"])),
- set(["Lfoo/"]))
- self.assertEqual(
- all_package_names(
- set(["Lfoo/Bar;->baz()V"]),
- set(["Lfoo/BarX;->bazx()I", "Labc/xyz/Mno;->ijk()J"])),
- set(["Lfoo/", "Labc/xyz/"]))
-
- def test_move_all(self):
- src = set([ "abc", "xyz" ])
- dst = set([ "def" ])
- move_all(src, dst)
- self.assertEqual(src, set())
- self.assertEqual(dst, set([ "abc", "def", "xyz" ]))
-
- def test_move_from_packages(self):
- src = set([ "Lfoo/bar/ClassA;->abc()J", # will be moved
- "Lfoo/bar/ClassA;->def()J", # will be moved
- "Lcom/pkg/example/ClassD;->ijk:J", # not moved: different package
- "Lfoo/bar/xyz/ClassC;->xyz()Z" ]) # not moved: subpackage
- dst = set()
- packages = set([ "Lfoo/bar/" ])
- move_from_packages(packages, src, dst)
- self.assertEqual(
- src, set([ "Lfoo/bar/xyz/ClassC;->xyz()Z", "Lcom/pkg/example/ClassD;->ijk:J" ]))
+ # Check valid input - two public and two private API signatures.
+ flags = FlagsDict(['A', 'B'], ['C', 'D'])
+ self.assertEquals(flags.generate_csv(),
+ [ 'A,' + FLAG_WHITELIST, 'B,' + FLAG_WHITELIST, 'C', 'D' ])
+
+ # Check invalid input - overlapping public/private API signatures.
+ with self.assertRaises(AssertionError):
+ flags = FlagsDict(['A', 'B'], ['B', 'C', 'D'])
+
+ def test_filter_apis(self):
+ # Initialize flags so that A and B are put on the whitelist and
+ # C, D, E are left unassigned. Try filtering for the unassigned ones.
+ flags = FlagsDict(['A', 'B'], ['C', 'D', 'E'])
+ filter_set = flags.filter_apis(lambda api, flags: not flags)
+ self.assertTrue(isinstance(filter_set, set))
+ self.assertEqual(filter_set, set([ 'C', 'D', 'E' ]))
+
+ def test_get_valid_subset_of_unassigned_keys(self):
+ # Create flags where only A is unassigned.
+ flags = FlagsDict(['A'], ['B', 'C'])
+ flags.assign_flag(FLAG_GREYLIST, set(['C']))
+ self.assertEquals(flags.generate_csv(),
+ [ 'A,' + FLAG_WHITELIST, 'B', 'C,' + FLAG_GREYLIST ])
+
+ # Check three things:
+ # (1) B is selected as valid unassigned
+ # (2) A is not selected because it is assigned 'whitelist'
+ # (3) D is not selected because it is not a valid key
self.assertEqual(
- dst, set([ "Lfoo/bar/ClassA;->abc()J", "Lfoo/bar/ClassA;->def()J" ]))
-
- def test_move_serialization(self):
- # All the entries should be moved apart from the last one
- src = set([ "Lfoo/bar/ClassA;->readObject(Ljava/io/ObjectInputStream;)V",
- "Lfoo/bar/ClassA;->readObjectNoData()V",
- "Lfoo/bar/ClassA;->readResolve()Ljava/lang/Object;",
- "Lfoo/bar/ClassA;->serialVersionUID:J",
- "Lfoo/bar/ClassA;->serialPersistentFields:[Ljava/io/ObjectStreamField;",
- "Lfoo/bar/ClassA;->writeObject(Ljava/io/ObjectOutputStream;)V",
- "Lfoo/bar/ClassA;->writeReplace()Ljava/lang/Object;",
- # Should not be moved as signature does not match
- "Lfoo/bar/ClassA;->readObject(Ljava/io/ObjectInputStream;)I"])
- expectedToMove = len(src) - 1
- dst = set()
- packages = set([ "Lfoo/bar/" ])
- move_serialization(src, dst)
- self.assertEqual(len(src), 1)
- self.assertEqual(len(dst), expectedToMove)
+ flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])), set([ 'B' ]))
+
+ def test_parse_and_merge_csv(self):
+ flags = FlagsDict(['A'], ['B'])
+ self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
+
+ # Test empty CSV entry.
+ flags.parse_and_merge_csv(['B'])
+ self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
+
+ # Test assigning an already assigned flag.
+ flags.parse_and_merge_csv(['A,' + FLAG_WHITELIST])
+ self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
+
+ # Test new additions.
+ flags.parse_and_merge_csv([
+ 'A,' + FLAG_GREYLIST,
+ 'B,' + FLAG_BLACKLIST + ',' + FLAG_GREYLIST_MAX_O ])
+ self.assertEqual(flags.generate_csv(),
+ [ 'A,' + FLAG_GREYLIST + "," + FLAG_WHITELIST,
+ 'B,' + FLAG_BLACKLIST + "," + FLAG_GREYLIST_MAX_O ])
+
+ # Test unknown API signature.
+ with self.assertRaises(AssertionError):
+ flags.parse_and_merge_csv([ 'C' ])
+
+ # Test unknown flag.
+ with self.assertRaises(AssertionError):
+ flags.parse_and_merge_csv([ 'A,foo' ])
+
+ def test_assign_flag(self):
+ flags = FlagsDict(['A'], ['B'])
+ self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
+
+ # Test assigning an already assigned flag.
+ flags.assign_flag(FLAG_WHITELIST, set([ 'A' ]))
+ self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
+
+ # Test new additions.
+ flags.assign_flag(FLAG_GREYLIST, set([ 'A', 'B' ]))
+ self.assertEquals(flags.generate_csv(),
+ [ 'A,' + FLAG_GREYLIST + "," + FLAG_WHITELIST, 'B,' + FLAG_GREYLIST ])
+
+ # Test invalid API signature.
+ with self.assertRaises(AssertionError):
+ flags.assign_flag(FLAG_WHITELIST, set([ 'C' ]))
+
+ # Test invalid flag.
+ with self.assertRaises(AssertionError):
+ flags.assign_flag('foo', set([ 'A' ]))
if __name__ == '__main__':
unittest.main()
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 364d5084fbc9..c6acd026bd39 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -190,8 +190,8 @@ interface IWifiManager
void unregisterNetworkRequestMatchCallback(int callbackIdentifier);
- boolean addNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
+ int addNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
- boolean removeNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
+ int removeNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 8dd6c771a924..9789319ed6b9 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -25,6 +25,7 @@ import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.UnsupportedAppUsage;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.net.ConnectivityManager;
@@ -137,6 +138,55 @@ public class WifiManager {
public static final int ERROR_AUTH_FAILURE_EAP_FAILURE = 3;
/**
+ * Maximum number of active network suggestions allowed per app.
+ * @hide
+ */
+ public static final int NETWORK_SUGGESTIONS_MAX_PER_APP =
+ ActivityManager.isLowRamDeviceStatic() ? 256 : 1024;
+
+ /**
+ * Reason code if all of the network suggestions were successfully added or removed.
+ */
+ public static final int STATUS_NETWORK_SUGGESTIONS_SUCCESS = 0;
+
+ /**
+ * Reason code if there was an internal error in the platform while processing the addition or
+ * removal of suggestions.
+ */
+ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1;
+
+ /**
+ * Reason code if one or more of the network suggestions added already exists in platform's
+ * database.
+ * @see WifiNetworkSuggestion#equals(Object)
+ */
+ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 2;
+
+ /**
+ * Reason code if the number of network suggestions provided by the app crosses the max
+ * threshold set per app.
+ * @see #getMaxNumberOfNetworkSuggestionsPerApp()
+ */
+ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 3;
+
+ /**
+ * Reason code if one or more of the network suggestions removed does not exist in platform's
+ * database.
+ */
+ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 4;
+
+ @IntDef(prefix = { "STATUS_NETWORK_SUGGESTIONS_" }, value = {
+ STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL,
+ STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE,
+ STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP,
+ STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID,
+ })
+
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NetworkSuggestionsStatusCode {}
+
+ /**
* Broadcast intent action indicating whether Wi-Fi scanning is allowed currently
* @hide
*/
@@ -1126,7 +1176,6 @@ public class WifiManager {
* @throws UnsupportedOperationException if Passpoint is not enabled on the device.
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
public List<OsuProvider> getMatchingOsuProviders(List<ScanResult> scanResults) {
try {
@@ -1497,12 +1546,13 @@ public class WifiManager {
* suggestion back using this API.</li>
*
* @param networkSuggestions List of network suggestions provided by the app.
- * @return true on success, false if any of the suggestions match (See
+ * @return Status code corresponding to the values in {@link NetworkSuggestionsStatusCode}.
* {@link WifiNetworkSuggestion#equals(Object)} any previously provided suggestions by the app.
* @throws {@link SecurityException} if the caller is missing required permissions.
*/
@RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
- public boolean addNetworkSuggestions(@NonNull List<WifiNetworkSuggestion> networkSuggestions) {
+ public @NetworkSuggestionsStatusCode int addNetworkSuggestions(
+ @NonNull List<WifiNetworkSuggestion> networkSuggestions) {
try {
return mService.addNetworkSuggestions(networkSuggestions, mContext.getOpPackageName());
} catch (RemoteException e) {
@@ -1510,21 +1560,20 @@ public class WifiManager {
}
}
-
/**
- * Remove a subset of or all of networks from previously provided suggestions by the app to the
- * device.
+ * Remove some or all of the network suggestions that were previously provided by the app.
* See {@link WifiNetworkSuggestion} for a detailed explanation of the parameters.
* See {@link WifiNetworkSuggestion#equals(Object)} for the equivalence evaluation used.
*
* @param networkSuggestions List of network suggestions to be removed. Pass an empty list
* to remove all the previous suggestions provided by the app.
- * @return true on success, false if any of the suggestions do not match any suggestions
- * previously provided by the app. Any matching suggestions are removed from the device and
- * will not be considered for any further connection attempts.
+ * @return Status code corresponding to the values in
+ * {@link NetworkSuggestionsStatusCode}.
+ * Any matching suggestions are removed from the device and will not be considered for any
+ * further connection attempts.
*/
@RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
- public boolean removeNetworkSuggestions(
+ public @NetworkSuggestionsStatusCode int removeNetworkSuggestions(
@NonNull List<WifiNetworkSuggestion> networkSuggestions) {
try {
return mService.removeNetworkSuggestions(
@@ -1535,6 +1584,15 @@ public class WifiManager {
}
/**
+ * Returns the max number of network suggestions that are allowed per app on the device.
+ * @see #addNetworkSuggestions(List)
+ * @see #removeNetworkSuggestions(List)
+ */
+ public int getMaxNumberOfNetworkSuggestionsPerApp() {
+ return NETWORK_SUGGESTIONS_MAX_PER_APP;
+ }
+
+ /**
* Add or update a Passpoint configuration. The configuration provides a credential
* for connecting to Passpoint networks that are operated by the Passpoint
* service provider specified in the configuration.
diff --git a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
index 87706b936f03..f73b9e5e2a00 100644
--- a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
+++ b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
@@ -42,8 +42,10 @@ import java.util.List;
public class WifiNetworkConfigBuilder {
private static final String MATCH_ALL_SSID_PATTERN_PATH = ".*";
private static final String MATCH_EMPTY_SSID_PATTERN_PATH = "";
- private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN =
+ private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN1 =
new Pair(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS);
+ private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN2 =
+ new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.BROADCAST_ADDRESS);
private static final Pair<MacAddress, MacAddress> MATCH_ALL_BSSID_PATTERN =
new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
private static final MacAddress MATCH_EXACT_BSSID_PATTERN_MASK =
@@ -189,7 +191,13 @@ public class WifiNetworkConfigBuilder {
* Set the BSSID to use for filtering networks from scan results. Will only match network whose
* BSSID is identical to the specified value.
* <p>
- * <li>Only allowed for creating network specifier, i.e {@link #buildNetworkSpecifier()}. </li>
+ * <li>For network requests ({@link NetworkSpecifier}), built using
+ * {@link #buildNetworkSpecifier}, sets the BSSID to use for filtering networks from scan
+ * results. Will only match networks whose BSSID is identical to specified value.</li>
+ * <li>For network suggestions ({@link WifiNetworkSuggestion}), built using
+ * {@link #buildNetworkSuggestion()}, sets a specific BSSID for the network suggestion.
+ * If set, only the specified BSSID with the specified SSID will be considered for connection.
+ * If not set, all BSSIDs with the specified SSID will be considered for connection.</li>
* <li>Overrides any previous value set using {@link #setBssid(MacAddress)} or
* {@link #setBssidPattern(MacAddress, MacAddress)}.</li>
*
@@ -432,6 +440,9 @@ public class WifiNetworkConfigBuilder {
if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) {
wifiConfiguration.SSID = "\"" + mSsidPatternMatcher.getPath() + "\"";
}
+ if (mBssidPatternMatcher.second == MATCH_EXACT_BSSID_PATTERN_MASK) {
+ wifiConfiguration.BSSID = mBssidPatternMatcher.first.toString();
+ }
setSecurityParamsInWifiConfiguration(wifiConfiguration);
wifiConfiguration.hiddenSSID = mIsHiddenSSID;
wifiConfiguration.priority = mPriority;
@@ -460,7 +471,10 @@ public class WifiNetworkConfigBuilder {
&& mSsidPatternMatcher.getPath().equals(MATCH_EMPTY_SSID_PATTERN_PATH)) {
return true;
}
- if (mBssidPatternMatcher.equals(MATCH_NO_BSSID_PATTERN)) {
+ if (mBssidPatternMatcher.equals(MATCH_NO_BSSID_PATTERN1)) {
+ return true;
+ }
+ if (mBssidPatternMatcher.equals(MATCH_NO_BSSID_PATTERN2)) {
return true;
}
return false;
@@ -474,6 +488,16 @@ public class WifiNetworkConfigBuilder {
return false;
}
+ private boolean hasSetMatchExactPattern() {
+ // exact ssid match with either match-all bssid or match-exact bssid.
+ if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL
+ && (mBssidPatternMatcher.equals(MATCH_ALL_BSSID_PATTERN)
+ || mBssidPatternMatcher.second.equals(MATCH_EXACT_BSSID_PATTERN_MASK))) {
+ return true;
+ }
+ return false;
+ }
+
private void validateSecurityParams() {
int numSecurityTypes = 0;
numSecurityTypes += mIsEnhancedOpen ? 1 : 0;
@@ -566,9 +590,42 @@ public class WifiNetworkConfigBuilder {
}
/**
- * Create a network suggestion object use in
- * {@link WifiManager#addNetworkSuggestions(List)}.
+ * Create a network suggestion object use in {@link WifiManager#addNetworkSuggestions(List)}.
* See {@link WifiNetworkSuggestion}.
+ *<p>
+ * Note: Apps can set a combination of SSID using {@link #setSsid(String)} and BSSID
+ * using {@link #setBssid(MacAddress)} to provide more fine grained network suggestions to the
+ * platform.
+ * </p>
+ *
+ * For example:
+ * To provide credentials for one open, one WPA2 and one WPA3 network with their
+ * corresponding SSID's:
+ * {@code
+ * final WifiNetworkSuggestion suggestion1 =
+ * new WifiNetworkConfigBuilder()
+ * .setSsid("test111111")
+ * .buildNetworkSuggestion()
+ * final WifiNetworkSuggestion suggestion2 =
+ * new WifiNetworkConfigBuilder()
+ * .setSsid("test222222")
+ * .setWpa2Passphrase("test123456")
+ * .buildNetworkSuggestion()
+ * final WifiNetworkSuggestion suggestion3 =
+ * new WifiNetworkConfigBuilder()
+ * .setSsid("test333333")
+ * .setWpa3Passphrase("test6789")
+ * .buildNetworkSuggestion()
+ * final List<WifiNetworkSuggestion> suggestionsList = new ArrayList<WifiNetworkSuggestion> {{
+ * add(suggestion1);
+ * add(suggestion2);
+ * add(suggestion3);
+ * }};
+ * final WifiManager wifiManager =
+ * context.getSystemService(Context.WIFI_SERVICE);
+ * wifiManager.addNetworkSuggestions(suggestionsList);
+ * ...
+ * }
*
* @return Instance of {@link WifiNetworkSuggestion}.
* @throws IllegalStateException on invalid params set.
@@ -577,11 +634,14 @@ public class WifiNetworkConfigBuilder {
if (mSsidPatternMatcher == null) {
throw new IllegalStateException("setSsid should be invoked for suggestion");
}
- if (mSsidPatternMatcher.getType() != PatternMatcher.PATTERN_LITERAL
- || mBssidPatternMatcher != null) {
- throw new IllegalStateException("none of setSsidPattern/setBssidPattern/setBssid are"
+ setMatchAnyPatternIfUnset();
+ if (!hasSetMatchExactPattern()) {
+ throw new IllegalStateException("none of setSsidPattern/setBssidPattern are"
+ " allowed for suggestion");
}
+ if (hasSetMatchNonePattern()) {
+ throw new IllegalStateException("cannot set match-none for suggestion");
+ }
validateSecurityParams();
return new WifiNetworkSuggestion(
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 25121e2dc8c7..760f1e6bc5e2 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -104,8 +104,8 @@ public final class WifiNetworkSuggestion implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.allowedKeyManagement,
- suggestorUid);
+ return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.BSSID,
+ wifiConfiguration.allowedKeyManagement, suggestorUid);
}
/**
@@ -121,6 +121,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
}
WifiNetworkSuggestion lhs = (WifiNetworkSuggestion) obj;
return Objects.equals(this.wifiConfiguration.SSID, lhs.wifiConfiguration.SSID)
+ && Objects.equals(this.wifiConfiguration.BSSID, lhs.wifiConfiguration.BSSID)
&& Objects.equals(this.wifiConfiguration.allowedKeyManagement,
lhs.wifiConfiguration.allowedKeyManagement)
&& suggestorUid == lhs.suggestorUid;
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 6622a2571870..fc5caf0a47d7 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -253,6 +253,14 @@ public class WifiScanner {
*/
@RequiresPermission(android.Manifest.permission.NETWORK_STACK)
public int type = TYPE_LOW_LATENCY;
+ /**
+ * This scan request may ignore location settings while receiving scans. This should only
+ * be used in emergency situations.
+ * {@hide}
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ public boolean ignoreLocationSettings;
/** Implement the Parcelable interface {@hide} */
public int describeContents() {
@@ -270,6 +278,7 @@ public class WifiScanner {
dest.writeInt(stepCount);
dest.writeInt(isPnoScan ? 1 : 0);
dest.writeInt(type);
+ dest.writeInt(ignoreLocationSettings ? 1 : 0);
if (channels != null) {
dest.writeInt(channels.length);
for (int i = 0; i < channels.length; i++) {
@@ -304,6 +313,7 @@ public class WifiScanner {
settings.stepCount = in.readInt();
settings.isPnoScan = in.readInt() == 1;
settings.type = in.readInt();
+ settings.ignoreLocationSettings = in.readInt() == 1;
int num_channels = in.readInt();
settings.channels = new ChannelSpec[num_channels];
for (int i = 0; i < num_channels; i++) {
diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/AbstractWifiService.java
index aa526d248d14..0f4e3a8ba20f 100644
--- a/wifi/java/com/android/server/wifi/AbstractWifiService.java
+++ b/wifi/java/com/android/server/wifi/AbstractWifiService.java
@@ -442,13 +442,13 @@ public abstract class AbstractWifiService extends IWifiManager.Stub {
}
@Override
- public boolean addNetworkSuggestions(
+ public int addNetworkSuggestions(
List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName) {
throw new UnsupportedOperationException();
}
@Override
- public boolean removeNetworkSuggestions(
+ public int removeNetworkSuggestions(
List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName) {
throw new UnsupportedOperationException();
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 8fe5af998dcf..13c8c9ea7ead 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -1298,13 +1298,26 @@ i * Verify that a call to cancel WPS immediately returns a failure.
*/
@Test
public void addRemoveNetworkSuggestions() throws Exception {
- when(mWifiService.addNetworkSuggestions(any(List.class), anyString())).thenReturn(true);
- when(mWifiService.removeNetworkSuggestions(any(List.class), anyString())).thenReturn(true);
+ when(mWifiService.addNetworkSuggestions(any(List.class), anyString()))
+ .thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS);
+ when(mWifiService.removeNetworkSuggestions(any(List.class), anyString()))
+ .thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS);
- assertTrue(mWifiManager.addNetworkSuggestions(new ArrayList<>()));
+ assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ mWifiManager.addNetworkSuggestions(new ArrayList<>()));
verify(mWifiService).addNetworkSuggestions(anyList(), eq(TEST_PACKAGE_NAME));
- assertTrue(mWifiManager.removeNetworkSuggestions(new ArrayList<>()));
+ assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ mWifiManager.removeNetworkSuggestions(new ArrayList<>()));
verify(mWifiService).removeNetworkSuggestions(anyList(), eq(TEST_PACKAGE_NAME));
}
+
+ /**
+ * Verify call to {@link WifiManager#getMaxNumberOfNetworkSuggestionsPerApp()}.
+ */
+ @Test
+ public void getMaxNumberOfNetworkSuggestionsPerApp() {
+ assertEquals(WifiManager.NETWORK_SUGGESTIONS_MAX_PER_APP,
+ mWifiManager.getMaxNumberOfNetworkSuggestionsPerApp());
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
index c455c6f0836d..2505499c85d7 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
@@ -244,7 +244,7 @@ public class WifiNetworkConfigBuilderTest {
* when match-none SSID pattern is set.
*/
@Test(expected = IllegalStateException.class)
- public void testWifiNetworkSpecifierBuilderWithMatchNoneSsidPattern() {
+ public void testWifiNetworkSpecifierBuilderWithMatchNoneSsidPattern1() {
new WifiNetworkConfigBuilder()
.setSsidPattern(new PatternMatcher("", PatternMatcher.PATTERN_LITERAL))
.buildNetworkSpecifier();
@@ -252,10 +252,21 @@ public class WifiNetworkConfigBuilderTest {
/**
* Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+ * when match-none SSID pattern is set.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSpecifierBuilderWithMatchNoneSsidPattern2() {
+ new WifiNetworkConfigBuilder()
+ .setSsid("")
+ .buildNetworkSpecifier();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
* when match-none BSSID pattern is set.
*/
@Test(expected = IllegalStateException.class)
- public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern() {
+ public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern1() {
new WifiNetworkConfigBuilder()
.setBssidPattern(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS)
.buildNetworkSpecifier();
@@ -263,6 +274,28 @@ public class WifiNetworkConfigBuilderTest {
/**
* Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+ * when match-none BSSID pattern is set.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern2() {
+ new WifiNetworkConfigBuilder()
+ .setBssid(MacAddress.BROADCAST_ADDRESS)
+ .buildNetworkSpecifier();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+ * when match-none BSSID pattern is set.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern3() {
+ new WifiNetworkConfigBuilder()
+ .setBssid(MacAddress.ALL_ZEROS_ADDRESS)
+ .buildNetworkSpecifier();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
* when SSID pattern is set for hidden network.
*/
@Test(expected = IllegalStateException.class)
@@ -429,13 +462,15 @@ public class WifiNetworkConfigBuilderTest {
* {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for OWE network.
*/
@Test
- public void testWifiNetworkSuggestionBuilderForEnhancedOpenNetwork() {
+ public void testWifiNetworkSuggestionBuilderForEnhancedOpenNetworkWithBssid() {
WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
.setSsid(TEST_SSID)
+ .setBssid(MacAddress.fromString(TEST_BSSID))
.setIsEnhancedOpen()
.buildNetworkSuggestion();
assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+ assertEquals(TEST_BSSID, suggestion.wifiConfiguration.BSSID);
assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
.get(WifiConfiguration.KeyMgmt.OWE));
assertNull(suggestion.wifiConfiguration.preSharedKey);
@@ -505,7 +540,7 @@ public class WifiNetworkConfigBuilderTest {
/**
* Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
- * when {@link WifiNetworkConfigBuilder#setBssid(MacAddress)} is set.
+ * when {@link WifiNetworkConfigBuilder#setBssidPattern(MacAddress, MacAddress)} is set.
*/
@Test(expected = IllegalStateException.class)
public void testWifiNetworkSuggestionBuilderWithBssidPattern() {
@@ -518,23 +553,46 @@ public class WifiNetworkConfigBuilderTest {
/**
* Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
- * when {@link WifiNetworkConfigBuilder#setBssidPattern(MacAddress, MacAddress)} is set.
+ * when {@link WifiNetworkConfigBuilder#setSsid(String)} is not set.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSuggestionBuilderWithNoSsid() {
+ new WifiNetworkConfigBuilder()
+ .buildNetworkSuggestion();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
+ * when {@link WifiNetworkConfigBuilder#setSsid(String)} is invoked with an invalid value.
*/
@Test(expected = IllegalStateException.class)
- public void testWifiNetworkSuggestionBuilderWithBssid() {
+ public void testWifiNetworkSuggestionBuilderWithInvalidSsid() {
+ new WifiNetworkConfigBuilder()
+ .setSsid("")
+ .buildNetworkSuggestion();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
+ * when {@link WifiNetworkConfigBuilder#setBssid(MacAddress)} is invoked with an invalid value.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSuggestionBuilderWithInvalidBroadcastBssid() {
new WifiNetworkConfigBuilder()
.setSsid(TEST_SSID)
- .setBssid(MacAddress.fromString(TEST_BSSID))
+ .setBssid(MacAddress.BROADCAST_ADDRESS)
.buildNetworkSuggestion();
}
/**
* Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
- * when {@link WifiNetworkConfigBuilder#setSsid(String)} is not set.
+ * when {@link WifiNetworkConfigBuilder#setBssid(MacAddress)} is invoked with an invalid value.
*/
@Test(expected = IllegalStateException.class)
- public void testWifiNetworkSuggestionBuilderWithNoSsid() {
+ public void testWifiNetworkSuggestionBuilderWithInvalidAllZeroBssid() {
new WifiNetworkConfigBuilder()
+ .setSsid(TEST_SSID)
+ .setBssid(MacAddress.ALL_ZEROS_ADDRESS)
.buildNetworkSuggestion();
}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index 6bab60dd480b..5cc821717462 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -29,6 +29,7 @@ import org.junit.Test;
@SmallTest
public class WifiNetworkSuggestionTest {
private static final String TEST_SSID = "\"Test123\"";
+ private static final String TEST_BSSID = "12:12:12:12:12:12";
private static final String TEST_SSID_1 = "\"Test1234\"";
/**
@@ -38,6 +39,7 @@ public class WifiNetworkSuggestionTest {
public void testWifiNetworkSuggestionParcel() {
WifiConfiguration configuration = new WifiConfiguration();
configuration.SSID = TEST_SSID;
+ configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
new WifiNetworkSuggestion(configuration, false, true, 0);
@@ -65,18 +67,20 @@ public class WifiNetworkSuggestionTest {
/**
* Check NetworkSuggestion equals returns {@code true} for 2 network suggestions with the same
- * SSID, key mgmt and UID.
+ * SSID, BSSID, key mgmt and UID.
*/
@Test
public void testWifiNetworkSuggestionEqualsSame() {
WifiConfiguration configuration = new WifiConfiguration();
configuration.SSID = TEST_SSID;
+ configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion =
new WifiNetworkSuggestion(configuration, true, false, 0);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID;
+ configuration1.BSSID = TEST_BSSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion1 =
new WifiNetworkSuggestion(configuration1, false, true, 0);
@@ -86,7 +90,7 @@ public class WifiNetworkSuggestionTest {
/**
* Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
- * key mgmt and UID, but different SSID.
+ * BSSID, key mgmt and UID, but different SSID.
*/
@Test
public void testWifiNetworkSuggestionEqualsFailsWhenSsidIsDifferent() {
@@ -107,7 +111,29 @@ public class WifiNetworkSuggestionTest {
/**
* Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
- * SSID and UID, but different key mgmt.
+ * SSID, key mgmt and UID, but different BSSID.
+ */
+ @Test
+ public void testWifiNetworkSuggestionEqualsFailsWhenBssidIsDifferent() {
+ WifiConfiguration configuration = new WifiConfiguration();
+ configuration.SSID = TEST_SSID;
+ configuration.BSSID = TEST_BSSID;
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ WifiNetworkSuggestion suggestion =
+ new WifiNetworkSuggestion(configuration, false, false, 0);
+
+ WifiConfiguration configuration1 = new WifiConfiguration();
+ configuration1.SSID = TEST_SSID;
+ configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ WifiNetworkSuggestion suggestion1 =
+ new WifiNetworkSuggestion(configuration1, false, false, 0);
+
+ assertNotEquals(suggestion, suggestion1);
+ }
+
+ /**
+ * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
+ * SSID, BSSID and UID, but different key mgmt.
*/
@Test
public void testWifiNetworkSuggestionEqualsFailsWhenKeyMgmtIsDifferent() {
@@ -128,7 +154,7 @@ public class WifiNetworkSuggestionTest {
/**
* Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
- * SSID and key mgmt, but different UID.
+ * SSID, BSSID and key mgmt, but different UID.
*/
@Test
public void testWifiNetworkSuggestionEqualsFailsWhenUidIsDifferent() {