Merge "Add SystemApi params to framework-module defaults"
diff --git a/Android.bp b/Android.bp
index e3aed6b..9ffdd1d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -200,30 +200,6 @@
}
filegroup {
- name: "framework-wifi-sources",
- srcs: [
- "wifi/java/**/*.java",
- "wifi/java/**/*.aidl",
- ],
- exclude_srcs: [
- ":framework-wifi-non-updatable-sources"
- ],
- path: "wifi/java",
-}
-
-filegroup {
- name: "framework-wifi-non-updatable-sources",
- srcs: [
- // TODO(b/146011398) package android.net.wifi is now split amongst 2 jars: framework.jar and
- // framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache
- // to a separate package.
- "wifi/java/android/net/wifi/WifiNetworkScoreCache.java",
- "wifi/java/android/net/wifi/WifiCondManager.java",
- "wifi/java/android/net/wifi/wificond/*.java",
- ],
-}
-
-filegroup {
name: "framework-non-updatable-sources",
srcs: [
// Java/AIDL sources under frameworks/base
@@ -247,9 +223,8 @@
":framework-telecomm-sources",
":framework-telephony-common-sources",
":framework-telephony-sources",
- ":framework-wifi-sources",
+ ":framework-wifi-annotations",
":framework-wifi-non-updatable-sources",
- ":libwificond_ipc_aidl",
":PacProcessor-aidl-sources",
":ProxyHandler-aidl-sources",
@@ -288,7 +263,9 @@
name: "framework-updatable-sources",
srcs: [
":framework-sdkext-sources",
+ ":framework-statsd-sources",
":updatable-media-srcs",
+ ":framework-wifi-updatable-sources",
]
}
@@ -424,7 +401,7 @@
filegroup {
name: "framework-jarjar-rules",
- srcs: ["jarjar_rules_hidl.txt"],
+ srcs: ["framework-jarjar-rules.txt"],
}
filegroup {
@@ -449,6 +426,11 @@
name: "framework-minus-apex",
defaults: ["framework-defaults"],
srcs: [":framework-non-updatable-sources"],
+ libs: [
+ // TODO(b/146167933): Use framework-statsd-stubs
+ "framework-statsd",
+ "framework-wifi-stubs",
+ ],
installable: true,
javac_shard_size: 150,
required: [
@@ -484,7 +466,12 @@
installable: false, // this lib is a build-only library
static_libs: [
"framework-minus-apex",
- // TODO(jiyong): add stubs for APEXes here
+ "framework-sdkext-stubs-systemapi",
+ // TODO(b/146167933): Use framework-statsd-stubs instead.
+ "framework-statsd",
+ // TODO(b/140299412): should be framework-wifi-stubs
+ "framework-wifi",
+ // TODO(jiyong): add more stubs for APEXes here
],
sdk_version: "core_platform",
apex_available: ["//apex_available:platform"],
diff --git a/apex/sdkext/Android.bp b/apex/sdkext/Android.bp
index aaf25b1..b9071f8 100644
--- a/apex/sdkext/Android.bp
+++ b/apex/sdkext/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_visibility: [":__subpackages__"],
+}
+
apex {
name: "com.android.sdkext",
manifest: "manifest.json",
@@ -25,6 +29,11 @@
certificate: ":com.android.sdkext.certificate",
}
+sdk {
+ name: "sdkext-sdk",
+ java_libs: [ "framework-sdkext-stubs-systemapi" ],
+}
+
apex_key {
name: "com.android.sdkext.key",
public_key: "com.android.sdkext.avbpubkey",
diff --git a/apex/sdkext/framework/Android.bp b/apex/sdkext/framework/Android.bp
index b17f0f8..a50dc3d 100644
--- a/apex/sdkext/framework/Android.bp
+++ b/apex/sdkext/framework/Android.bp
@@ -12,12 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_visibility: [ ":__pkg__" ]
+}
+
filegroup {
name: "framework-sdkext-sources",
srcs: [
"java/**/*.java",
],
path: "java",
+ visibility: [ "//frameworks/base:__pkg__" ] // For the "global" stubs.
}
java_library {
@@ -27,4 +32,40 @@
libs: [ "framework-annotations-lib" ],
permitted_packages: [ "android.os.ext" ],
installable: true,
+ visibility: [ "//frameworks/base/apex/sdkext:__pkg__" ],
+}
+
+droidstubs {
+ name: "framework-sdkext-droidstubs-publicapi",
+ defaults: [
+ "framework-sdkext-stubs-defaults",
+ "framework-module-stubs-defaults-publicapi",
+ ]
+}
+
+droidstubs {
+ name: "framework-sdkext-droidstubs-systemapi",
+ defaults: [
+ "framework-sdkext-stubs-defaults",
+ "framework-module-stubs-defaults-systemapi",
+ ]
+}
+
+stubs_defaults {
+ name: "framework-sdkext-stubs-defaults",
+ srcs: [
+ ":framework-sdkext-sources",
+ ":framework-annotations",
+ ],
+ sdk_version: "system_current",
+}
+
+java_library {
+ name: "framework-sdkext-stubs-systemapi",
+ srcs: [":framework-sdkext-droidstubs-systemapi"],
+ sdk_version: "system_current",
+ visibility: [
+ "//frameworks/base:__pkg__", // Framework
+ "//frameworks/base/apex/sdkext:__pkg__", // sdkext SDK
+ ]
}
diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp
index 5c46e1f..09ca1d2 100644
--- a/apex/statsd/Android.bp
+++ b/apex/statsd/Android.bp
@@ -22,7 +22,10 @@
// libc.so and libcutils.so are included in the apex
// native_shared_libs: ["libc", "libcutils"],
// binaries: ["vold"],
- java_libs: ["service-statsd"],
+ java_libs: [
+ "framework-statsd",
+ "service-statsd",
+ ],
// prebuilts: ["my_prebuilt"],
name: "com.android.os.statsd-defaults",
key: "com.android.os.statsd.key",
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
new file mode 100644
index 0000000..37b07a6
--- /dev/null
+++ b/apex/statsd/framework/Android.bp
@@ -0,0 +1,67 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+filegroup {
+ name: "framework-statsd-sources",
+ srcs: [
+ "java/**/*.java",
+ ],
+ path: "java",
+}
+
+java_library {
+ name: "framework-statsd",
+ installable: true,
+ // TODO(b/146209659): Use system_current instead.
+ sdk_version: "core_platform",
+ srcs: [
+ ":framework-statsd-sources",
+ ],
+ permitted_packages: [
+ "android.app",
+ "android.util",
+ ],
+ libs: [
+ "framework-annotations-lib",
+ // TODO(b/146230220): Use framework-system-stubs instead.
+ "android_system_stubs_current",
+ ],
+ // TODO:(b/146210774): Add apex_available field.
+}
+
+droidstubs {
+ name: "framework-statsd-stubs-docs",
+ defaults: [
+ "framework-module-stubs-defaults-publicapi"
+ ],
+ srcs: [
+ ":framework-statsd-sources",
+ ],
+ libs: [
+ "framework-all",
+ ],
+ sdk_version: "core_platform",
+}
+
+// TODO(b/146167933): Use these stubs in frameworks/base/Android.bp
+java_library {
+ name: "framework-statsd-stubs",
+ srcs: [
+ ":framework-statsd-stubs-docs",
+ ],
+ libs: [
+ "framework-all",
+ ],
+ sdk_version: "core_platform",
+}
diff --git a/core/java/android/util/StatsEvent.java b/apex/statsd/framework/java/android/util/StatsEvent.java
similarity index 100%
rename from core/java/android/util/StatsEvent.java
rename to apex/statsd/framework/java/android/util/StatsEvent.java
diff --git a/api/current.txt b/api/current.txt
index b61917d..1254a85 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6732,6 +6732,7 @@
method @Deprecated @Nullable public String getApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName);
method public boolean getAutoTime(@NonNull android.content.ComponentName);
method @Deprecated public boolean getAutoTimeRequired();
+ method public boolean getAutoTimeZone(@NonNull android.content.ComponentName);
method @NonNull public java.util.List<android.os.UserHandle> getBindDeviceAdminTargetUsers(@NonNull android.content.ComponentName);
method public boolean getBluetoothContactSharingDisabled(@NonNull android.content.ComponentName);
method public boolean getCameraDisabled(@Nullable android.content.ComponentName);
@@ -6850,6 +6851,7 @@
method @Deprecated public void setApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName, @Nullable String) throws android.content.pm.PackageManager.NameNotFoundException;
method public void setAutoTime(@NonNull android.content.ComponentName, boolean);
method @Deprecated public void setAutoTimeRequired(@NonNull android.content.ComponentName, boolean);
+ method public void setAutoTimeZone(@NonNull android.content.ComponentName, boolean);
method public void setBackupServiceEnabled(@NonNull android.content.ComponentName, boolean);
method public void setBluetoothContactSharingDisabled(@NonNull android.content.ComponentName, boolean);
method public void setCameraDisabled(@NonNull android.content.ComponentName, boolean);
@@ -16778,16 +16780,29 @@
package android.hardware.biometrics {
public class BiometricManager {
- method @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public int canAuthenticate();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public int canAuthenticate();
+ method @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public int canAuthenticate(int);
field public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1; // 0x1
field public static final int BIOMETRIC_ERROR_NONE_ENROLLED = 11; // 0xb
field public static final int BIOMETRIC_ERROR_NO_HARDWARE = 12; // 0xc
field public static final int BIOMETRIC_SUCCESS = 0; // 0x0
}
+ public static interface BiometricManager.Authenticators {
+ field public static final int BIOMETRIC_STRONG = 15; // 0xf
+ field public static final int BIOMETRIC_WEAK = 255; // 0xff
+ field public static final int DEVICE_CREDENTIAL = 32768; // 0x8000
+ }
+
public class BiometricPrompt {
method @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public void authenticate(@NonNull android.hardware.biometrics.BiometricPrompt.CryptoObject, @NonNull android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.biometrics.BiometricPrompt.AuthenticationCallback);
method @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public void authenticate(@NonNull android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.biometrics.BiometricPrompt.AuthenticationCallback);
+ method @Nullable public int getAllowedAuthenticators();
+ method @Nullable public CharSequence getDescription();
+ method @Nullable public CharSequence getNegativeButtonText();
+ method @Nullable public CharSequence getSubtitle();
+ method @NonNull public CharSequence getTitle();
+ method public boolean isConfirmationRequired();
field public static final int BIOMETRIC_ACQUIRED_GOOD = 0; // 0x0
field public static final int BIOMETRIC_ACQUIRED_IMAGER_DIRTY = 3; // 0x3
field public static final int BIOMETRIC_ACQUIRED_INSUFFICIENT = 2; // 0x2
@@ -16823,9 +16838,10 @@
public static class BiometricPrompt.Builder {
ctor public BiometricPrompt.Builder(android.content.Context);
method @NonNull public android.hardware.biometrics.BiometricPrompt build();
+ method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setAllowedAuthenticators(int);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setConfirmationRequired(boolean);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setDescription(@NonNull CharSequence);
- method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setDeviceCredentialAllowed(boolean);
+ method @Deprecated @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setDeviceCredentialAllowed(boolean);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setNegativeButton(@NonNull CharSequence, @NonNull java.util.concurrent.Executor, @NonNull android.content.DialogInterface.OnClickListener);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setSubtitle(@NonNull CharSequence);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setTitle(@NonNull CharSequence);
@@ -38382,11 +38398,11 @@
protected static interface ContactsContract.RawContactsColumns {
field public static final String ACCOUNT_TYPE_AND_DATA_SET = "account_type_and_data_set";
field public static final String AGGREGATION_MODE = "aggregation_mode";
- field public static final String BACKUP_ID = "backup_id";
+ field @Deprecated public static final String BACKUP_ID = "backup_id";
field public static final String CONTACT_ID = "contact_id";
field public static final String DATA_SET = "data_set";
field public static final String DELETED = "deleted";
- field public static final String METADATA_DIRTY = "metadata_dirty";
+ field @Deprecated public static final String METADATA_DIRTY = "metadata_dirty";
field public static final String RAW_CONTACT_IS_READ_ONLY = "raw_contact_is_read_only";
field public static final String RAW_CONTACT_IS_USER_PROFILE = "raw_contact_is_user_profile";
}
diff --git a/api/system-current.txt b/api/system-current.txt
index b449b2e..ebcda3d 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1497,6 +1497,11 @@
field public static final int REMOTE_PANU_ROLE = 2; // 0x2
}
+ public class BluetoothPbap implements android.bluetooth.BluetoothProfile {
+ method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
+ field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
+ }
+
public interface BluetoothProfile {
field public static final int CONNECTION_POLICY_ALLOWED = 100; // 0x64
field public static final int CONNECTION_POLICY_FORBIDDEN = 0; // 0x0
@@ -1628,6 +1633,7 @@
field public static final String STATS_MANAGER = "stats";
field public static final String STATUS_BAR_SERVICE = "statusbar";
field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
+ field public static final String TELEPHONY_IMS_SERVICE = "telephony_ims";
field public static final String TELEPHONY_REGISTRY_SERVICE = "telephony_registry";
field public static final String VR_SERVICE = "vrmanager";
field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager";
@@ -2277,6 +2283,15 @@
}
+package android.hardware.biometrics {
+
+ public static interface BiometricManager.Authenticators {
+ field public static final int BIOMETRIC_CONVENIENCE = 4095; // 0xfff
+ field public static final int EMPTY_SET = 0; // 0x0
+ }
+
+}
+
package android.hardware.camera2 {
public abstract class CameraDevice implements java.lang.AutoCloseable {
@@ -7078,34 +7093,34 @@
field public static final int STATUS_NOT_BLOCKED = 0; // 0x0
}
- public static final class ContactsContract.MetadataSync implements android.provider.BaseColumns android.provider.ContactsContract.MetadataSyncColumns {
- field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_metadata";
- field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_metadata";
- field public static final android.net.Uri CONTENT_URI;
- field public static final String METADATA_AUTHORITY = "com.android.contacts.metadata";
- field public static final android.net.Uri METADATA_AUTHORITY_URI;
+ @Deprecated public static final class ContactsContract.MetadataSync implements android.provider.BaseColumns android.provider.ContactsContract.MetadataSyncColumns {
+ field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_metadata";
+ field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_metadata";
+ field @Deprecated public static final android.net.Uri CONTENT_URI;
+ field @Deprecated public static final String METADATA_AUTHORITY = "com.android.contacts.metadata";
+ field @Deprecated public static final android.net.Uri METADATA_AUTHORITY_URI;
}
- protected static interface ContactsContract.MetadataSyncColumns {
- field public static final String ACCOUNT_NAME = "account_name";
- field public static final String ACCOUNT_TYPE = "account_type";
- field public static final String DATA = "data";
- field public static final String DATA_SET = "data_set";
- field public static final String DELETED = "deleted";
- field public static final String RAW_CONTACT_BACKUP_ID = "raw_contact_backup_id";
+ @Deprecated protected static interface ContactsContract.MetadataSyncColumns {
+ field @Deprecated public static final String ACCOUNT_NAME = "account_name";
+ field @Deprecated public static final String ACCOUNT_TYPE = "account_type";
+ field @Deprecated public static final String DATA = "data";
+ field @Deprecated public static final String DATA_SET = "data_set";
+ field @Deprecated public static final String DELETED = "deleted";
+ field @Deprecated public static final String RAW_CONTACT_BACKUP_ID = "raw_contact_backup_id";
}
- public static final class ContactsContract.MetadataSyncState implements android.provider.BaseColumns android.provider.ContactsContract.MetadataSyncStateColumns {
- field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_metadata_sync_state";
- field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_metadata_sync_state";
- field public static final android.net.Uri CONTENT_URI;
+ @Deprecated public static final class ContactsContract.MetadataSyncState implements android.provider.BaseColumns android.provider.ContactsContract.MetadataSyncStateColumns {
+ field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_metadata_sync_state";
+ field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_metadata_sync_state";
+ field @Deprecated public static final android.net.Uri CONTENT_URI;
}
- protected static interface ContactsContract.MetadataSyncStateColumns {
- field public static final String ACCOUNT_NAME = "account_name";
- field public static final String ACCOUNT_TYPE = "account_type";
- field public static final String DATA_SET = "data_set";
- field public static final String STATE = "state";
+ @Deprecated protected static interface ContactsContract.MetadataSyncStateColumns {
+ field @Deprecated public static final String ACCOUNT_NAME = "account_name";
+ field @Deprecated public static final String ACCOUNT_TYPE = "account_type";
+ field @Deprecated public static final String DATA_SET = "data_set";
+ field @Deprecated public static final String STATE = "state";
}
public final class DeviceConfig {
@@ -7126,6 +7141,7 @@
field public static final String NAMESPACE_APP_COMPAT = "app_compat";
field public static final String NAMESPACE_ATTENTION_MANAGER_SERVICE = "attention_manager_service";
field public static final String NAMESPACE_AUTOFILL = "autofill";
+ field public static final String NAMESPACE_BIOMETRICS = "biometrics";
field public static final String NAMESPACE_CONNECTIVITY = "connectivity";
field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
field @Deprecated public static final String NAMESPACE_DEX_BOOT = "dex_boot";
@@ -7312,6 +7328,7 @@
public final class Settings {
field public static final String ACTION_ACCESSIBILITY_DETAILS_SETTINGS = "android.settings.ACCESSIBILITY_DETAILS_SETTINGS";
+ field public static final String ACTION_BUGREPORT_HANDLER_SETTINGS = "android.settings.BUGREPORT_HANDLER_SETTINGS";
field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
field public static final String ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS = "android.settings.LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS";
field public static final String ACTION_MANAGE_APP_OVERLAY_PERMISSION = "android.settings.MANAGE_APP_OVERLAY_PERMISSION";
@@ -10322,6 +10339,11 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
}
+ public class ImsManager {
+ method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
+ method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
+ }
+
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
method @NonNull public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
@@ -10365,6 +10387,22 @@
ctor @Deprecated public ImsMmTelManager.RegistrationCallback();
}
+ public class ImsRcsManager implements android.telephony.ims.RegistrationManager {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(int) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(int, int) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerRcsAvailabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterRcsAvailabilityCallback(@NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException;
+ }
+
+ public static class ImsRcsManager.AvailabilityCallback {
+ ctor public ImsRcsManager.AvailabilityCallback();
+ method public void onAvailabilityChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
+ }
+
public final class ImsReasonInfo implements android.os.Parcelable {
field public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED = "Forbidden. Not Authorized for Service";
}
@@ -10726,9 +10764,22 @@
public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
ctor public RcsFeature();
- method public void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+ method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+ method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
method public void onFeatureReady();
method public void onFeatureRemoved();
+ method public boolean queryCapabilityConfiguration(int, int);
+ method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus();
+ }
+
+ public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
+ ctor public RcsFeature.RcsImsCapabilities(int);
+ method public void addCapabilities(int);
+ method public boolean isCapable(int);
+ method public void removeCapabilities(int);
+ field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
+ field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
+ field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
}
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 7cfc218..3bdd479 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -711,6 +711,7 @@
field public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
field public static final String ROLLBACK_SERVICE = "rollback";
field public static final String STATUS_BAR_SERVICE = "statusbar";
+ field public static final String TELEPHONY_IMS_SERVICE = "telephony_ims";
field public static final String TEST_NETWORK_SERVICE = "test_network";
}
@@ -2433,6 +2434,7 @@
method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(@NonNull String, @NonNull String, @Nullable String, boolean);
field public static final String NAMESPACE_ANDROID = "android";
field public static final String NAMESPACE_AUTOFILL = "autofill";
+ field public static final String NAMESPACE_BIOMETRICS = "biometrics";
field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
field public static final String NAMESPACE_PERMISSIONS = "permissions";
field public static final String NAMESPACE_PRIVACY = "privacy";
@@ -3426,6 +3428,11 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
}
+ public class ImsManager {
+ method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
+ method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
+ }
+
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
method @NonNull public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
@@ -3469,6 +3476,22 @@
ctor @Deprecated public ImsMmTelManager.RegistrationCallback();
}
+ public class ImsRcsManager implements android.telephony.ims.RegistrationManager {
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isAvailable(int) throws android.telephony.ims.ImsException;
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isCapable(int, int) throws android.telephony.ims.ImsException;
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerRcsAvailabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterRcsAvailabilityCallback(@NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException;
+ }
+
+ public static class ImsRcsManager.AvailabilityCallback {
+ ctor public ImsRcsManager.AvailabilityCallback();
+ method public void onAvailabilityChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
+ }
+
public class ImsService extends android.app.Service {
ctor public ImsService();
method public android.telephony.ims.feature.MmTelFeature createMmTelFeature(int);
@@ -3826,9 +3849,22 @@
public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
ctor public RcsFeature();
- method public void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+ method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+ method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
method public void onFeatureReady();
method public void onFeatureRemoved();
+ method public boolean queryCapabilityConfiguration(int, int);
+ method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus();
+ }
+
+ public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
+ ctor public RcsFeature.RcsImsCapabilities(int);
+ method public void addCapabilities(int);
+ method public boolean isCapable(int);
+ method public void removeCapabilities(int);
+ field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
+ field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
+ field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
}
}
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp
index f3871d7..f56dd6e 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.cpp
+++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp
@@ -27,12 +27,13 @@
#include <cstring>
#include <memory>
+#include <android/log.h>
+#include <android/looper.h>
#include <jni.h>
#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
-#include <android/looper.h>
-#include <android/log.h>
#include <android-base/stringprintf.h>
@@ -49,6 +50,7 @@
static struct {
jmethodID onDeviceOpen;
jmethodID onDeviceGetReport;
+ jmethodID onDeviceOutput;
jmethodID onDeviceError;
} gDeviceCallbackClassInfo;
@@ -64,6 +66,18 @@
}
}
+static ScopedLocalRef<jbyteArray> toJbyteArray(JNIEnv* env, const std::vector<uint8_t>& vector) {
+ ScopedLocalRef<jbyteArray> array(env, env->NewByteArray(vector.size()));
+ if (array.get() == nullptr) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", nullptr);
+ return array;
+ }
+ static_assert(sizeof(char) == sizeof(uint8_t));
+ env->SetByteArrayRegion(array.get(), 0, vector.size(),
+ reinterpret_cast<const signed char*>(vector.data()));
+ return array;
+}
+
static std::string toString(const std::vector<uint8_t>& data) {
std::string s = "";
for (uint8_t b : data) {
@@ -101,6 +115,13 @@
checkAndClearException(env, "onDeviceGetReport");
}
+void DeviceCallback::onDeviceOutput(const std::vector<uint8_t>& data) {
+ JNIEnv* env = getJNIEnv();
+ env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOutput,
+ toJbyteArray(env, data).get());
+ checkAndClearException(env, "onDeviceOutput");
+}
+
JNIEnv* DeviceCallback::getJNIEnv() {
JNIEnv* env;
mJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
@@ -240,6 +261,12 @@
set_report.rnum, toString(data).c_str());
break;
}
+ case UHID_OUTPUT: {
+ struct uhid_output_req& output = ev.u.output;
+ std::vector<uint8_t> data(output.data, output.data + output.size);
+ mDeviceCallback->onDeviceOutput(data);
+ break;
+ }
default: {
LOGI("Unhandled event type: %" PRIu32, ev.type);
break;
@@ -332,6 +359,8 @@
env->GetMethodID(clazz, "onDeviceOpen", "()V");
uhid::gDeviceCallbackClassInfo.onDeviceGetReport =
env->GetMethodID(clazz, "onDeviceGetReport", "(II)V");
+ uhid::gDeviceCallbackClassInfo.onDeviceOutput =
+ env->GetMethodID(clazz, "onDeviceOutput", "([B)V");
uhid::gDeviceCallbackClassInfo.onDeviceError =
env->GetMethodID(clazz, "onDeviceError", "()V");
if (uhid::gDeviceCallbackClassInfo.onDeviceOpen == NULL ||
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h
index b0471ed..93ea881 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.h
+++ b/cmds/hid/jni/com_android_commands_hid_Device.h
@@ -31,6 +31,7 @@
void onDeviceOpen();
void onDeviceGetReport(uint32_t requestId, uint8_t reportId);
+ void onDeviceOutput(const std::vector<uint8_t>& data);
void onDeviceError();
private:
diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java
index 616d411..874604c 100644
--- a/cmds/hid/src/com/android/commands/hid/Device.java
+++ b/cmds/hid/src/com/android/commands/hid/Device.java
@@ -20,13 +20,16 @@
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
-import android.os.MessageQueue;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.os.SomeArgs;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Map;
+
public class Device {
private static final String TAG = "HidDevice";
@@ -40,6 +43,7 @@
private final DeviceHandler mHandler;
// mFeatureReports is limited to 256 entries, because the report number is 8-bit
private final SparseArray<byte[]> mFeatureReports;
+ private final Map<ByteBuffer, byte[]> mOutputs;
private long mTimeToSend;
private final Object mCond = new Object();
@@ -55,12 +59,13 @@
private static native void nativeCloseDevice(long ptr);
public Device(int id, String name, int vid, int pid, byte[] descriptor,
- byte[] report, SparseArray<byte[]> featureReports) {
+ byte[] report, SparseArray<byte[]> featureReports, Map<ByteBuffer, byte[]> outputs) {
mId = id;
mThread = new HandlerThread("HidDeviceHandler");
mThread.start();
mHandler = new DeviceHandler(mThread.getLooper());
mFeatureReports = featureReports;
+ mOutputs = outputs;
SomeArgs args = SomeArgs.obtain();
args.argi1 = id;
args.argi2 = vid;
@@ -160,6 +165,11 @@
}
public void onDeviceGetReport(int requestId, int reportId) {
+ if (mFeatureReports == null) {
+ Log.e(TAG, "Received GET_REPORT request for reportId=" + reportId
+ + ", but 'feature_reports' section is not found");
+ return;
+ }
byte[] report = mFeatureReports.get(reportId);
if (report == null) {
@@ -176,6 +186,29 @@
mHandler.sendMessageAtTime(msg, mTimeToSend);
}
+ // native callback
+ public void onDeviceOutput(byte[] data) {
+ if (mOutputs == null) {
+ Log.e(TAG, "Received OUTPUT request, but 'outputs' section is not found");
+ return;
+ }
+ byte[] response = mOutputs.get(ByteBuffer.wrap(data));
+ if (response == null) {
+ Log.i(TAG,
+ "Requested response for output " + Arrays.toString(data) + " is not found");
+ return;
+ }
+
+ Message msg;
+ msg = mHandler.obtainMessage(MSG_SEND_REPORT, response);
+
+ // Message is set to asynchronous so it won't be blocked by synchronization
+ // barrier during UHID_OPEN. This is necessary for drivers that do
+ // UHID_OUTPUT requests during probe, and expect a response right away.
+ msg.setAsynchronous(true);
+ mHandler.sendMessageAtTime(msg, mTimeToSend);
+ }
+
public void onDeviceError() {
Log.e(TAG, "Device error occurred, closing /dev/uhid");
Message msg = mHandler.obtainMessage(MSG_CLOSE_DEVICE);
diff --git a/cmds/hid/src/com/android/commands/hid/Event.java b/cmds/hid/src/com/android/commands/hid/Event.java
index 746e372..62587a7 100644
--- a/cmds/hid/src/com/android/commands/hid/Event.java
+++ b/cmds/hid/src/com/android/commands/hid/Event.java
@@ -21,10 +21,13 @@
import android.util.Log;
import android.util.SparseArray;
-import java.io.InputStreamReader;
import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
public class Event {
private static final String TAG = "HidEvent";
@@ -41,6 +44,7 @@
private int mPid;
private byte[] mReport;
private SparseArray<byte[]> mFeatureReports;
+ private Map<ByteBuffer, byte[]> mOutputs;
private int mDuration;
public int getId() {
@@ -75,6 +79,10 @@
return mFeatureReports;
}
+ public Map<ByteBuffer, byte[]> getOutputs() {
+ return mOutputs;
+ }
+
public int getDuration() {
return mDuration;
}
@@ -88,6 +96,7 @@
+ ", pid=" + mPid
+ ", report=" + Arrays.toString(mReport)
+ ", feature_reports=" + mFeatureReports.toString()
+ + ", outputs=" + mOutputs.toString()
+ ", duration=" + mDuration
+ "}";
}
@@ -123,6 +132,10 @@
mEvent.mFeatureReports = reports;
}
+ public void setOutputs(Map<ByteBuffer, byte[]> outputs) {
+ mEvent.mOutputs = outputs;
+ }
+
public void setVid(int vid) {
mEvent.mVid = vid;
}
@@ -199,6 +212,9 @@
case "feature_reports":
eb.setFeatureReports(readFeatureReports());
break;
+ case "outputs":
+ eb.setOutputs(readOutputs());
+ break;
case "duration":
eb.setDuration(readInt());
break;
@@ -250,7 +266,7 @@
private SparseArray<byte[]> readFeatureReports()
throws IllegalStateException, IOException {
- SparseArray<byte[]> featureReports = new SparseArray();
+ SparseArray<byte[]> featureReports = new SparseArray<>();
try {
mReader.beginArray();
while (mReader.hasNext()) {
@@ -276,17 +292,60 @@
}
}
mReader.endObject();
- if (data != null)
+ if (data != null) {
featureReports.put(id, data);
+ }
}
mReader.endArray();
- } catch (IllegalStateException|NumberFormatException e) {
+ } catch (IllegalStateException | NumberFormatException e) {
consumeRemainingElements();
mReader.endArray();
throw new IllegalStateException("Encountered malformed data.", e);
- } finally {
- return featureReports;
}
+ return featureReports;
+ }
+
+ private Map<ByteBuffer, byte[]> readOutputs()
+ throws IllegalStateException, IOException {
+ Map<ByteBuffer, byte[]> outputs = new HashMap<>();
+
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ byte[] output = null;
+ byte[] response = null;
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "description":
+ // Description is only used to keep track of the output responses
+ mReader.nextString();
+ break;
+ case "output":
+ output = readData();
+ break;
+ case "response":
+ response = readData();
+ break;
+ default:
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException("Invalid key in outputs: " + name);
+ }
+ }
+ mReader.endObject();
+ if (output != null) {
+ outputs.put(ByteBuffer.wrap(output), response);
+ }
+ }
+ mReader.endArray();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return outputs;
}
private void consumeRemainingElements() throws IOException {
@@ -296,10 +355,6 @@
}
}
- private static void error(String msg) {
- error(msg, null);
- }
-
private static void error(String msg, Exception e) {
System.out.println(msg);
Log.e(TAG, msg);
diff --git a/cmds/hid/src/com/android/commands/hid/Hid.java b/cmds/hid/src/com/android/commands/hid/Hid.java
index 54ac1b0..0ee2cc4 100644
--- a/cmds/hid/src/com/android/commands/hid/Hid.java
+++ b/cmds/hid/src/com/android/commands/hid/Hid.java
@@ -16,22 +16,17 @@
package com.android.commands.hid;
-import android.util.JsonReader;
-import android.util.JsonToken;
import android.util.Log;
import android.util.SparseArray;
import libcore.io.IoUtils;
-import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
+import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.io.IOException;
import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
public class Hid {
private static final String TAG = "HID";
@@ -119,7 +114,7 @@
}
int id = e.getId();
Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(),
- e.getDescriptor(), e.getReport(), e.getFeatureReports());
+ e.getDescriptor(), e.getReport(), e.getFeatureReports(), e.getOutputs());
mDevices.append(id, d);
}
diff --git a/cmds/incident/main.cpp b/cmds/incident/main.cpp
index 6c3d197..eb2b98a 100644
--- a/cmds/incident/main.cpp
+++ b/cmds/incident/main.cpp
@@ -375,7 +375,7 @@
if (destination == DEST_STDOUT) {
// Call into the service
sp<StatusListener> listener(new StatusListener());
- status = service->reportIncidentToStream(args, listener, writeEnd);
+ status = service->reportIncidentToStream(args, listener, std::move(writeEnd));
if (!status.isOk()) {
fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
@@ -388,7 +388,7 @@
} else if (destination == DEST_DUMPSTATE) {
// Call into the service
sp<StatusListener> listener(new StatusListener());
- status = service->reportIncidentToDumpstate(writeEnd, listener);
+ status = service->reportIncidentToDumpstate(std::move(writeEnd), listener);
if (!status.isOk()) {
fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
return 1;
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index 999936b..cfd77c2 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -279,7 +279,7 @@
Status IncidentService::reportIncidentToStream(const IncidentReportArgs& args,
const sp<IIncidentReportStatusListener>& listener,
- const unique_fd& stream) {
+ unique_fd stream) {
IncidentReportArgs argsCopy(args);
// Streaming reports can not also be broadcast.
@@ -306,7 +306,7 @@
return Status::ok();
}
-Status IncidentService::reportIncidentToDumpstate(const unique_fd& stream,
+Status IncidentService::reportIncidentToDumpstate(unique_fd stream,
const sp<IIncidentReportStatusListener>& listener) {
uid_t caller = IPCThreadState::self()->getCallingUid();
if (caller != AID_ROOT && caller != AID_SHELL) {
diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h
index fb013d0..b2c7f23 100644
--- a/cmds/incidentd/src/IncidentService.h
+++ b/cmds/incidentd/src/IncidentService.h
@@ -121,9 +121,9 @@
virtual Status reportIncidentToStream(const IncidentReportArgs& args,
const sp<IIncidentReportStatusListener>& listener,
- const unique_fd& stream);
+ unique_fd stream);
- virtual Status reportIncidentToDumpstate(const unique_fd& stream,
+ virtual Status reportIncidentToDumpstate(unique_fd stream,
const sp<IIncidentReportStatusListener>& listener);
virtual Status systemRunning();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 577272e..1e3b950 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -766,9 +766,19 @@
private static final String REQUEST_PERMISSIONS_WHO_PREFIX = "@android:requestPermissions:";
private static final String AUTO_FILL_AUTH_WHO_PREFIX = "@android:autoFillAuth:";
-
private static final String KEYBOARD_SHORTCUTS_RECEIVER_PKG_NAME = "com.android.systemui";
+ private static final int LOG_AM_ON_CREATE_CALLED = 30057;
+ private static final int LOG_AM_ON_START_CALLED = 30059;
+ private static final int LOG_AM_ON_RESUME_CALLED = 30022;
+ private static final int LOG_AM_ON_PAUSE_CALLED = 30021;
+ private static final int LOG_AM_ON_STOP_CALLED = 30049;
+ private static final int LOG_AM_ON_RESTART_CALLED = 30058;
+ private static final int LOG_AM_ON_DESTROY_CALLED = 30060;
+ private static final int LOG_AM_ON_ACTIVITY_RESULT_CALLED = 30062;
+ private static final int LOG_AM_ON_TOP_RESUMED_GAINED_CALLED = 30064;
+ private static final int LOG_AM_ON_TOP_RESUMED_LOST_CALLED = 30065;
+
private static class ManagedDialog {
Dialog mDialog;
Bundle mArgs;
@@ -2439,8 +2449,11 @@
* {@link #onProvideKeyboardShortcuts} to retrieve the shortcuts for the foreground activity.
*/
public final void requestShowKeyboardShortcuts() {
+ final ComponentName sysuiComponent = ComponentName.unflattenFromString(
+ getResources().getString(
+ com.android.internal.R.string.config_systemUIServiceComponent));
Intent intent = new Intent(Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS);
- intent.setPackage(KEYBOARD_SHORTCUTS_RECEIVER_PKG_NAME);
+ intent.setPackage(sysuiComponent.getPackageName());
sendBroadcastAsUser(intent, Process.myUserHandle());
}
@@ -2448,8 +2461,11 @@
* Dismiss the Keyboard Shortcuts screen.
*/
public final void dismissKeyboardShortcutsHelper() {
+ final ComponentName sysuiComponent = ComponentName.unflattenFromString(
+ getResources().getString(
+ com.android.internal.R.string.config_systemUIServiceComponent));
Intent intent = new Intent(Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS);
- intent.setPackage(KEYBOARD_SHORTCUTS_RECEIVER_PKG_NAME);
+ intent.setPackage(sysuiComponent.getPackageName());
sendBroadcastAsUser(intent, Process.myUserHandle());
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 590b3db..795f51a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4417,4 +4417,18 @@
}
}
}
+
+ /**
+ * Get packages of bugreport-whitelisted apps to handle a bug report.
+ *
+ * @return packages of bugreport-whitelisted apps to handle a bug report.
+ * @hide
+ */
+ public List<String> getBugreportWhitelistedPackages() {
+ try {
+ return getService().getBugreportWhitelistedPackages();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index aa8a302..112bd30 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -388,6 +388,7 @@
void requestFullBugReport();
void requestRemoteBugReport();
boolean launchBugReportHandlerApp();
+ List<String> getBugreportWhitelistedPackages();
@UnsupportedAppUsage
Intent getIntentForIntentSender(in IIntentSender sender);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index bd948ec5..a1305da 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1145,6 +1145,14 @@
return new TimeZoneDetector();
}});
+ registerService(Context.TELEPHONY_IMS_SERVICE, android.telephony.ims.ImsManager.class,
+ new CachedServiceFetcher<android.telephony.ims.ImsManager>() {
+ @Override
+ public android.telephony.ims.ImsManager createService(ContextImpl ctx) {
+ return new android.telephony.ims.ImsManager(ctx.getOuterContext());
+ }
+ });
+
registerService(Context.PERMISSION_SERVICE, PermissionManager.class,
new CachedServiceFetcher<PermissionManager>() {
@Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ec981b2..6e7ead1 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5810,6 +5810,49 @@
}
/**
+ * Called by a device owner, a profile owner for the primary user or a profile
+ * owner of an organization-owned managed profile to turn auto time zone on and off.
+ * Callers are recommended to use {@link UserManager#DISALLOW_CONFIG_DATE_TIME}
+ * to prevent the user from changing this setting.
+ * <p>
+ * If user restriction {@link UserManager#DISALLOW_CONFIG_DATE_TIME} is used,
+ * no user will be able set the date and time zone. Instead, the network date
+ * and time zone will be used.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param enabled Whether time zone should be obtained automatically from the network or not.
+ * @throws SecurityException if caller is not a device owner, a profile owner for the
+ * primary user, or a profile owner of an organization-owned managed profile.
+ */
+ public void setAutoTimeZone(@NonNull ComponentName admin, boolean enabled) {
+ throwIfParentInstance("setAutoTimeZone");
+ if (mService != null) {
+ try {
+ mService.setAutoTimeZone(admin, enabled);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * @return true if auto time zone is enabled on the device.
+ * @throws SecurityException if caller is not a device owner, a profile owner for the
+ * primary user, or a profile owner of an organization-owned managed profile.
+ */
+ public boolean getAutoTimeZone(@NonNull ComponentName admin) {
+ throwIfParentInstance("getAutoTimeZone");
+ if (mService != null) {
+ try {
+ return mService.getAutoTimeZone(admin);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
* Called by a device owner to set whether all users created on the device should be ephemeral.
* <p>
* The system user is exempt from this policy - it is never ephemeral.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index ff1ecd5..949e8ab 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -298,6 +298,9 @@
void setAutoTime(in ComponentName who, boolean enabled);
boolean getAutoTime(in ComponentName who);
+ void setAutoTimeZone(in ComponentName who, boolean enabled);
+ boolean getAutoTimeZone(in ComponentName who);
+
void setForceEphemeralUsers(in ComponentName who, boolean forceEpehemeralUsers);
boolean getForceEphemeralUsers(in ComponentName who);
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 5e530ee..bd1eea5 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -515,8 +515,8 @@
public static PendingIntent createPermissionIntent(Context context, Uri sliceUri,
String callingPackage) {
Intent intent = new Intent(SliceManager.ACTION_REQUEST_SLICE_PERMISSION);
- intent.setComponent(new ComponentName("com.android.systemui",
- "com.android.systemui.SlicePermissionActivity"));
+ intent.setComponent(ComponentName.unflattenFromString(context.getResources().getString(
+ com.android.internal.R.string.config_slicePermissionComponent)));
intent.putExtra(EXTRA_BIND_URI, sliceUri);
intent.putExtra(EXTRA_PKG, callingPackage);
intent.putExtra(EXTRA_PROVIDER_PKG, context.getPackageName());
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index d94c657..df02896 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -16,7 +16,10 @@
package android.bluetooth;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -54,6 +57,7 @@
*
* @hide
*/
+@SystemApi
public class BluetoothPbap implements BluetoothProfile {
private static final String TAG = "BluetoothPbap";
@@ -75,7 +79,11 @@
* {@link BluetoothProfile#STATE_DISCONNECTING}.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
* receive.
+ *
+ * @hide
*/
+ @SuppressLint("ActionValue")
+ @SystemApi
@SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
@@ -85,33 +93,16 @@
private ServiceListener mServiceListener;
private BluetoothAdapter mAdapter;
+ /** @hide */
public static final int RESULT_FAILURE = 0;
+ /** @hide */
public static final int RESULT_SUCCESS = 1;
- /** Connection canceled before completion. */
- public static final int RESULT_CANCELED = 2;
-
/**
- * An interface for notifying Bluetooth PCE IPC clients when they have
- * been connected to the BluetoothPbap service.
+ * Connection canceled before completion.
+ *
+ * @hide
*/
- public interface ServiceListener {
- /**
- * Called to notify the client when this proxy object has been
- * connected to the BluetoothPbap service. Clients must wait for
- * this callback before making IPC calls on the BluetoothPbap
- * service.
- */
- public void onServiceConnected(BluetoothPbap proxy);
-
- /**
- * Called to notify the client that this proxy object has been
- * disconnected from the BluetoothPbap service. Clients must not
- * make IPC calls on the BluetoothPbap service after this callback.
- * This callback will currently only occur if the application hosting
- * the BluetoothPbap service, but may be called more often in future.
- */
- public void onServiceDisconnected();
- }
+ public static final int RESULT_CANCELED = 2;
private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
new IBluetoothStateChangeCallback.Stub() {
@@ -127,6 +118,8 @@
/**
* Create a BluetoothPbap proxy object.
+ *
+ * @hide
*/
public BluetoothPbap(Context context, ServiceListener l) {
mContext = context;
@@ -181,6 +174,7 @@
}
}
+ /** @hide */
protected void finalize() throws Throwable {
try {
close();
@@ -194,6 +188,8 @@
* Other public functions of BluetoothPbap will return default error
* results once close() has been called. Multiple invocations of close()
* are ok.
+ *
+ * @hide
*/
public synchronized void close() {
IBluetoothManager mgr = mAdapter.getBluetoothManager();
@@ -210,6 +206,8 @@
/**
* {@inheritDoc}
+ *
+ * @hide
*/
@Override
public List<BluetoothDevice> getConnectedDevices() {
@@ -229,17 +227,22 @@
/**
* {@inheritDoc}
+ *
+ * @hide
*/
+ @SystemApi
@Override
- public int getConnectionState(BluetoothDevice device) {
+ public int getConnectionState(@Nullable BluetoothDevice device) {
log("getConnectionState: device=" + device);
- final IBluetoothPbap service = mService;
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
- }
try {
- return service.getConnectionState(device);
+ final IBluetoothPbap service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ return service.getConnectionState(device);
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return BluetoothProfile.STATE_DISCONNECTED;
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -248,6 +251,8 @@
/**
* {@inheritDoc}
+ *
+ * @hide
*/
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
@@ -266,22 +271,12 @@
}
/**
- * Returns true if the specified Bluetooth device is connected (does not
- * include connecting). Returns false if not connected, or if this proxy
- * object is not currently connected to the Pbap service.
- */
- // TODO: This is currently being used by SettingsLib and internal app.
- public boolean isConnected(BluetoothDevice device) {
- return getConnectionState(device) == BluetoothAdapter.STATE_CONNECTED;
- }
-
- /**
* Disconnects the current Pbap client (PCE). Currently this call blocks,
* it may soon be made asynchronous. Returns false if this proxy object is
* not currently connected to the Pbap service.
+ *
+ * @hide
*/
- // TODO: This is currently being used by SettingsLib and will be used in the future.
- // TODO: Must specify target device. Implement this in the service.
@UnsupportedAppUsage
public boolean disconnect(BluetoothDevice device) {
log("disconnect()");
@@ -304,7 +299,7 @@
log("Proxy object connected");
mService = IBluetoothPbap.Stub.asInterface(service);
if (mServiceListener != null) {
- mServiceListener.onServiceConnected(BluetoothPbap.this);
+ mServiceListener.onServiceConnected(BluetoothProfile.PBAP, BluetoothPbap.this);
}
}
@@ -312,11 +307,23 @@
log("Proxy object disconnected");
doUnbind();
if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected();
+ mServiceListener.onServiceDisconnected(BluetoothProfile.PBAP);
}
}
};
+ private boolean isEnabled() {
+ if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
+ return false;
+ }
+
+ private boolean isValidDevice(BluetoothDevice device) {
+ if (device == null) return false;
+
+ if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+ return false;
+ }
+
private static void log(String msg) {
if (DBG) {
Log.d(TAG, msg);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 85027d9..808f2160 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4916,6 +4916,8 @@
* {@link android.telephony.ims.ImsManager}.
* @hide
*/
+ @SystemApi
+ @TestApi
public static final String TELEPHONY_IMS_SERVICE = "telephony_ims";
/**
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index 0b3c765..e954635 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -39,6 +39,9 @@
void transfer(in String packageName, in IntentSender statusReceiver);
void abandon();
+ void addFile(String name, long lengthBytes, in byte[] metadata);
+ void removeFile(String name);
+
boolean isMultiPackage();
int[] getChildSessionIds();
void addChildSessionId(in int sessionId);
@@ -46,5 +49,4 @@
int getParentSessionId();
boolean isStaged();
- void addFile(in String name, long size, in byte[] metadata);
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 8f51435..898631e 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -231,6 +231,15 @@
public static final String EXTRA_CALLBACK = "android.content.pm.extra.CALLBACK";
/**
+ * Streaming installation pending.
+ * Caller should make sure DataLoader is able to prepare image and reinitiate the operation.
+ *
+ * @see #EXTRA_SESSION_ID
+ * {@hide}
+ */
+ public static final int STATUS_PENDING_STREAMING = -2;
+
+ /**
* User action is currently required to proceed. You can launch the intent
* activity described by {@link Intent#EXTRA_INTENT} to involve the user and
* continue.
@@ -1059,13 +1068,56 @@
}
}
+
+ /**
+ * Adds a file to session. On commit this file will be pulled from dataLoader.
+ *
+ * @param name arbitrary, unique name of your choosing to identify the
+ * APK being written. You can open a file again for
+ * additional writes (such as after a reboot) by using the
+ * same name. This name is only meaningful within the context
+ * of a single install session.
+ * @param lengthBytes total size of the file being written.
+ * The system may clear various caches as needed to allocate
+ * this space.
+ * @param metadata additional info use by dataLoader to pull data for the file.
+ * @throws SecurityException if called after the session has been
+ * sealed or abandoned
+ * @throws IllegalStateException if called for non-callback session
+ * {@hide}
+ */
+ public void addFile(@NonNull String name, long lengthBytes, @NonNull byte[] metadata) {
+ try {
+ mSession.addFile(name, lengthBytes, metadata);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes a file.
+ *
+ * @param name name of a file, e.g. split.
+ * @throws SecurityException if called after the session has been
+ * sealed or abandoned
+ * @throws IllegalStateException if called for non-callback session
+ * {@hide}
+ */
+ public void removeFile(@NonNull String name) {
+ try {
+ mSession.removeFile(name);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Attempt to commit everything staged in this session. This may require
* user intervention, and so it may not happen immediately. The final
* result of the commit will be reported through the given callback.
* <p>
- * Once this method is called, the session is sealed and no additional
- * mutations may be performed on the session. If the device reboots
+ * Once this method is called, the session is sealed and no additional mutations may be
+ * performed on the session. In case of device reboot or data loader transient failure
* before the session has been finalized, you may commit the session again.
* <p>
* If the installer is the device owner or the affiliated profile owner, there will be no
@@ -1220,27 +1272,6 @@
}
/**
- * Configure files for an installation session.
- *
- * Currently only for Incremental installation session. Once this method is called,
- * the files and their paths, as specified in the parameters, will be created and properly
- * configured in the Incremental File System.
- *
- * TODO(b/136132412): update this and InstallationFile class with latest API design.
- *
- * @throws IllegalStateException if {@link SessionParams#incrementalParams} is null.
- *
- * @hide
- */
- public void addFile(@NonNull String name, long size, @NonNull byte[] metadata) {
- try {
- mSession.addFile(name, size, metadata);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Release this session object. You can open the session again if it
* hasn't been finalized.
*/
@@ -1429,6 +1460,9 @@
public long requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
/** {@hide} */
public IncrementalDataLoaderParams incrementalParams;
+ /** TODO(b/146080380): add a class name to make it fully compatible with ComponentName.
+ * {@hide} */
+ public String dataLoaderPackageName;
/**
* Construct parameters for a new package install session.
@@ -1468,6 +1502,7 @@
incrementalParams = new IncrementalDataLoaderParams(
dataLoaderParamsParcel);
}
+ dataLoaderPackageName = source.readString();
}
/** {@hide} */
@@ -1492,6 +1527,7 @@
ret.isStaged = isStaged;
ret.requiredInstalledVersionCode = requiredInstalledVersionCode;
ret.incrementalParams = incrementalParams;
+ ret.dataLoaderPackageName = dataLoaderPackageName;
return ret;
}
@@ -1831,6 +1867,20 @@
this.incrementalParams = incrementalParams;
}
+ /**
+ * Set the data provider params for the session.
+ * This also switches installation into callback mode and disallow direct writes into
+ * staging folder.
+ * TODO(b/146080380): unify dataprovider params with Incremental.
+ *
+ * @param dataLoaderPackageName name of the dataLoader package
+ * {@hide}
+ */
+ @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
+ public void setDataLoaderPackageName(String dataLoaderPackageName) {
+ this.dataLoaderPackageName = dataLoaderPackageName;
+ }
+
/** {@hide} */
public void dump(IndentingPrintWriter pw) {
pw.printPair("mode", mode);
@@ -1851,6 +1901,7 @@
pw.printPair("isMultiPackage", isMultiPackage);
pw.printPair("isStaged", isStaged);
pw.printPair("requiredInstalledVersionCode", requiredInstalledVersionCode);
+ pw.printPair("dataLoaderPackageName", dataLoaderPackageName);
pw.println();
}
@@ -1885,6 +1936,7 @@
} else {
dest.writeParcelable(null, flags);
}
+ dest.writeString(dataLoaderPackageName);
}
public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java
index edbf73a0..0deb2ab 100644
--- a/core/java/android/content/pm/parsing/ApkParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkParseUtils.java
@@ -2321,7 +2321,7 @@
parsingPackage.setProfileableByShell(true);
}
XmlUtils.skipCurrentTag(parser);
-
+ break;
default:
if (!PackageParser.RIGID_PARSER) {
Slog.w(TAG, "Unknown element under <application>: " + tagName
diff --git a/core/java/android/hardware/biometrics/Authenticator.java b/core/java/android/hardware/biometrics/Authenticator.java
deleted file mode 100644
index 6d7e748..0000000
--- a/core/java/android/hardware/biometrics/Authenticator.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.biometrics;
-
-/**
- * Type of authenticators defined on a granularity that the BiometricManager / BiometricPrompt
- * supports.
- * @hide
- */
-public class Authenticator {
-
- /**
- * Device credential, e.g. Pin/Pattern/Password.
- */
- public static final int TYPE_CREDENTIAL = 1 << 0;
- /**
- * Encompasses all biometrics on the device, e.g. Fingerprint/Iris/Face.
- */
- public static final int TYPE_BIOMETRIC = 1 << 1;
-
-}
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 191516b..d28b7c5 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -16,8 +16,9 @@
package android.hardware.biometrics;
+import static android.hardware.biometrics.BiometricManager.Authenticators;
+
import android.annotation.UnsupportedAppUsage;
-import android.app.KeyguardManager;
/**
@@ -126,8 +127,8 @@
/**
* The device does not have pin, pattern, or password set up. See
- * {@link BiometricPrompt.Builder#setDeviceCredentialAllowed(boolean)} and
- * {@link KeyguardManager#isDeviceSecure()}
+ * {@link BiometricPrompt.Builder#setAllowedAuthenticators(int)},
+ * {@link Authenticators#DEVICE_CREDENTIAL}, and {@link BiometricManager#canAuthenticate(int)}.
*/
int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14;
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index f17b3ae..7e1506f 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -65,6 +66,77 @@
BIOMETRIC_ERROR_NO_HARDWARE})
@interface BiometricError {}
+ /**
+ * Types of authenticators, defined at a level of granularity supported by
+ * {@link BiometricManager} and {@link BiometricPrompt}.
+ *
+ * <p>Types may combined via bitwise OR into a single integer representing multiple
+ * authenticators (e.g. <code>DEVICE_CREDENTIAL | BIOMETRIC_WEAK</code>).
+ */
+ public interface Authenticators {
+ /**
+ * An {@link IntDef} representing valid combinations of authenticator types.
+ * @hide
+ */
+ @IntDef(flag = true, value = {
+ BIOMETRIC_STRONG,
+ BIOMETRIC_WEAK,
+ DEVICE_CREDENTIAL,
+ })
+ @interface Types {}
+
+ /**
+ * Empty set with no authenticators specified.
+ * @hide
+ */
+ @SystemApi
+ int EMPTY_SET = 0x0;
+
+ /**
+ * Placeholder for the theoretical strongest biometric security tier.
+ * @hide
+ */
+ int BIOMETRIC_MAX_STRENGTH = 0x001;
+
+ /**
+ * Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
+ * requirements for <strong>Strong</strong>, as defined by the Android CDD.
+ */
+ int BIOMETRIC_STRONG = 0x00F;
+
+ /**
+ * Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
+ * requirements for <strong>Weak</strong>, as defined by the Android CDD.
+ *
+ * <p>Note that this is a superset of {@link #BIOMETRIC_STRONG} and is defined such that
+ * <code>BIOMETRIC_STRONG | BIOMETRIC_WEAK == BIOMETRIC_WEAK</code>.
+ */
+ int BIOMETRIC_WEAK = 0x0FF;
+
+ /**
+ * Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
+ * requirements for <strong>Convenience</strong>, as defined by the Android CDD. This
+ * is not a valid parameter to any of the {@link android.hardware.biometrics} APIs, since
+ * the CDD allows only {@link #BIOMETRIC_WEAK} and stronger authenticators to participate.
+ * @hide
+ */
+ @SystemApi
+ int BIOMETRIC_CONVENIENCE = 0xFFF;
+
+ /**
+ * Placeholder for the theoretical weakest biometric security tier.
+ * @hide
+ */
+ int BIOMETRIC_MIN_STRENGTH = 0x7FFF;
+
+ /**
+ * The non-biometric credential used to secure the device (i.e., PIN, pattern, or password).
+ * This should typically only be used in combination with a biometric auth type, such as
+ * {@link #BIOMETRIC_WEAK}.
+ */
+ int DEVICE_CREDENTIAL = 1 << 15;
+ }
+
private final Context mContext;
private final IAuthService mService;
private final boolean mHasHardware;
@@ -94,27 +166,64 @@
}
/**
- * Determine if biometrics can be used. In other words, determine if {@link BiometricPrompt}
- * can be expected to be shown (hardware available, templates enrolled, user-enabled).
+ * Determine if biometrics can be used. In other words, determine if
+ * {@link BiometricPrompt} can be expected to be shown (hardware available, templates enrolled,
+ * user-enabled). This is the equivalent of {@link #canAuthenticate(int)} with
+ * {@link Authenticators#BIOMETRIC_WEAK}
*
- * @return Returns {@link #BIOMETRIC_ERROR_NONE_ENROLLED} if the user does not have any
- * enrolled, or {@link #BIOMETRIC_ERROR_HW_UNAVAILABLE} if none are currently
- * supported/enabled. Returns {@link #BIOMETRIC_SUCCESS} if a biometric can currently be
- * used (enrolled and available).
+ * @return {@link #BIOMETRIC_ERROR_NONE_ENROLLED} if the user does not have any strong
+ * biometrics enrolled, or {@link #BIOMETRIC_ERROR_HW_UNAVAILABLE} if none are currently
+ * supported/enabled. Returns {@link #BIOMETRIC_SUCCESS} if a strong biometric can currently
+ * be used (enrolled and available).
+ *
+ * @deprecated See {@link #canAuthenticate(int)}.
*/
+ @Deprecated
@RequiresPermission(USE_BIOMETRIC)
public @BiometricError int canAuthenticate() {
- return canAuthenticate(mContext.getUserId());
+ return canAuthenticate(Authenticators.BIOMETRIC_WEAK);
+ }
+
+ /**
+ * Determine if any of the provided authenticators can be used. In other words, determine if
+ * {@link BiometricPrompt} can be expected to be shown (hardware available, templates enrolled,
+ * user-enabled).
+ *
+ * For biometric authenticators, determine if the device can currently authenticate with at
+ * least the requested strength. For example, invoking this API with
+ * {@link Authenticators#BIOMETRIC_WEAK} on a device that currently only has
+ * {@link Authenticators#BIOMETRIC_STRONG} enrolled will return {@link #BIOMETRIC_SUCCESS}.
+ *
+ * Invoking this API with {@link Authenticators#DEVICE_CREDENTIAL} can be used to determine
+ * if the user has a PIN/Pattern/Password set up.
+ *
+ * @param authenticators bit field consisting of constants defined in {@link Authenticators}.
+ * If multiple authenticators are queried, a logical OR will be applied.
+ * For example, if {@link Authenticators#DEVICE_CREDENTIAL} |
+ * {@link Authenticators#BIOMETRIC_STRONG} is queried and only
+ * {@link Authenticators#DEVICE_CREDENTIAL} is set up, this API will
+ * return {@link #BIOMETRIC_SUCCESS}
+ *
+ * @return {@link #BIOMETRIC_ERROR_NONE_ENROLLED} if the user does not have any of the
+ * requested authenticators enrolled, or {@link #BIOMETRIC_ERROR_HW_UNAVAILABLE} if none are
+ * currently supported/enabled. Returns {@link #BIOMETRIC_SUCCESS} if one of the requested
+ * authenticators can currently be used (enrolled and available).
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ public @BiometricError int canAuthenticate(@Authenticators.Types int authenticators) {
+ return canAuthenticate(mContext.getUserId(), authenticators);
}
/**
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
- public @BiometricError int canAuthenticate(int userId) {
+ public @BiometricError int canAuthenticate(int userId,
+ @Authenticators.Types int authenticators) {
if (mService != null) {
try {
- return mService.canAuthenticate(mContext.getOpPackageName(), userId);
+ final String opPackageName = mContext.getOpPackageName();
+ return mService.canAuthenticate(opPackageName, userId, authenticators);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 9c51b52..6f9c9e6 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
+import static android.hardware.biometrics.BiometricManager.Authenticators;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
@@ -149,7 +150,7 @@
/**
* A builder that collects arguments to be shown on the system-provided biometric dialog.
- **/
+ */
public static class Builder {
private final Bundle mBundle;
private ButtonInfo mPositiveButtonInfo;
@@ -157,8 +158,8 @@
private Context mContext;
/**
- * Creates a builder for a biometric dialog.
- * @param context
+ * Creates a builder for a {@link BiometricPrompt} dialog.
+ * @param context The {@link Context} that will be used to build the prompt.
*/
public Builder(Context context) {
mBundle = new Bundle();
@@ -166,58 +167,67 @@
}
/**
- * Required: Set the title to display.
- * @param title
- * @return
+ * Required: Sets the title that will be shown on the prompt.
+ * @param title The title to display.
+ * @return This builder.
*/
- @NonNull public Builder setTitle(@NonNull CharSequence title) {
+ @NonNull
+ public Builder setTitle(@NonNull CharSequence title) {
mBundle.putCharSequence(KEY_TITLE, title);
return this;
}
/**
- * For internal use currently. Only takes effect if title is null/empty. Shows a default
- * modality-specific title.
+ * Shows a default, modality-specific title for the prompt if the title would otherwise be
+ * null or empty. Currently for internal use only.
+ * @return This builder.
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
- @NonNull public Builder setUseDefaultTitle() {
+ @NonNull
+ public Builder setUseDefaultTitle() {
mBundle.putBoolean(KEY_USE_DEFAULT_TITLE, true);
return this;
}
/**
- * Optional: Set the subtitle to display.
- * @param subtitle
- * @return
+ * Optional: Sets a subtitle that will be shown on the prompt.
+ * @param subtitle The subtitle to display.
+ * @return This builder.
*/
- @NonNull public Builder setSubtitle(@NonNull CharSequence subtitle) {
+ @NonNull
+ public Builder setSubtitle(@NonNull CharSequence subtitle) {
mBundle.putCharSequence(KEY_SUBTITLE, subtitle);
return this;
}
/**
- * Optional: Set the description to display.
- * @param description
- * @return
+ * Optional: Sets a description that will be shown on the prompt.
+ * @param description The description to display.
+ * @return This builder.
*/
- @NonNull public Builder setDescription(@NonNull CharSequence description) {
+ @NonNull
+ public Builder setDescription(@NonNull CharSequence description) {
mBundle.putCharSequence(KEY_DESCRIPTION, description);
return this;
}
/**
- * Required: Set the text for the negative button. This would typically be used as a
- * "Cancel" button, but may be also used to show an alternative method for authentication,
- * such as screen that asks for a backup password.
+ * Required: Sets the text, executor, and click listener for the negative button on the
+ * prompt. This is typically a cancel button, but may be also used to show an alternative
+ * method for authentication, such as a screen that asks for a backup password.
*
- * Note that this should not be set if {@link #setDeviceCredentialAllowed(boolean)}(boolean)
- * is set to true.
+ * <p>Note that this setting is not required, and in fact is explicitly disallowed, if
+ * device credential authentication is enabled via {@link #setAllowedAuthenticators(int)} or
+ * {@link #setDeviceCredentialAllowed(boolean)}.
*
- * @param text
- * @return
+ * @param text Text to be shown on the negative button for the prompt.
+ * @param executor Executor that will be used to run the on click callback.
+ * @param listener Listener containing a callback to be run when the button is pressed.
+ * @return This builder.
*/
- @NonNull public Builder setNegativeButton(@NonNull CharSequence text,
+ @NonNull
+ public Builder setNegativeButton(@NonNull CharSequence text,
@NonNull @CallbackExecutor Executor executor,
@NonNull DialogInterface.OnClickListener listener) {
if (TextUtils.isEmpty(text)) {
@@ -235,70 +245,112 @@
}
/**
- * Optional: A hint to the system to require user confirmation after a biometric has been
- * authenticated. For example, implicit modalities like Face and Iris authentication are
- * passive, meaning they don't require an explicit user action to complete. When set to
- * 'false', the user action (e.g. pressing a button) will not be required. BiometricPrompt
- * will require confirmation by default.
+ * Optional: Sets a hint to the system for whether to require user confirmation after
+ * authentication. For example, implicit modalities like face and iris are passive, meaning
+ * they don't require an explicit user action to complete authentication. If set to true,
+ * these modalities should require the user to take some action (e.g. press a button)
+ * before {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is
+ * called. Defaults to true.
*
- * A typical use case for not requiring confirmation would be for low-risk transactions,
+ * <p>A typical use case for not requiring confirmation would be for low-risk transactions,
* such as re-authenticating a recently authenticated application. A typical use case for
* requiring confirmation would be for authorizing a purchase.
*
- * Note that this is a hint to the system. The system may choose to ignore the flag. For
- * example, if the user disables implicit authentication in Settings, or if it does not
- * apply to a modality (e.g. Fingerprint). When ignored, the system will default to
- * requiring confirmation.
+ * <p>Note that this just passes a hint to the system, which the system may then ignore. For
+ * example, a value of false may be ignored if the user has disabled implicit authentication
+ * in Settings, or if it does not apply to a particular modality (e.g. fingerprint).
*
- * @param requireConfirmation
+ * @param requireConfirmation true if explicit user confirmation should be required, or
+ * false otherwise.
+ * @return This builder.
*/
- @NonNull public Builder setConfirmationRequired(boolean requireConfirmation) {
+ @NonNull
+ public Builder setConfirmationRequired(boolean requireConfirmation) {
mBundle.putBoolean(KEY_REQUIRE_CONFIRMATION, requireConfirmation);
return this;
}
/**
- * The user will first be prompted to authenticate with biometrics, but also given the
- * option to authenticate with their device PIN, pattern, or password. Developers should
- * first check {@link KeyguardManager#isDeviceSecure()} before enabling this. If the device
- * is not secure, {@link BiometricPrompt#BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL} will be
- * returned in {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}}.
+ * Optional: If enabled, the user will first be prompted to authenticate with biometrics,
+ * but also given the option to authenticate with their device PIN, pattern, or password.
+ * Developers should first check {@link KeyguardManager#isDeviceSecure()} before enabling.
+ * If the device is not secure, {@link BiometricPrompt#BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL}
+ * will be given to {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
* Defaults to false.
*
- * Note that {@link #setNegativeButton(CharSequence, Executor,
- * DialogInterface.OnClickListener)} should not be set if this is set to true.
+ * <p>Note that enabling this option replaces the negative button on the prompt with one
+ * that allows the user to authenticate with their device credential, making it an error to
+ * call {@link #setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.
*
- * @param allowed When true, the prompt will fall back to ask for the user's device
- * credentials (PIN, pattern, or password).
- * @return
+ * @param allowed true if the prompt should fall back to asking for the user's device
+ * credential (PIN/pattern/password), or false otherwise.
+ * @return This builder.
+ *
+ * @deprecated Replaced by {@link #setAllowedAuthenticators(int)}.
*/
- @NonNull public Builder setDeviceCredentialAllowed(boolean allowed) {
+ @Deprecated
+ @NonNull
+ public Builder setDeviceCredentialAllowed(boolean allowed) {
mBundle.putBoolean(KEY_ALLOW_DEVICE_CREDENTIAL, allowed);
return this;
}
/**
- * Creates a {@link BiometricPrompt}.
- * @return a {@link BiometricPrompt}
- * @throws IllegalArgumentException if any of the required fields are not set.
+ * Optional: Specifies the type(s) of authenticators that may be invoked by
+ * {@link BiometricPrompt} to authenticate the user. Available authenticator types are
+ * defined in {@link Authenticators} and can be combined via bitwise OR. Defaults to:
+ * <ul>
+ * <li>{@link Authenticators#BIOMETRIC_WEAK} for non-crypto authentication, or</li>
+ * <li>{@link Authenticators#BIOMETRIC_STRONG} for crypto-based authentication.</li>
+ * </ul>
+ *
+ * <p>If this method is used and no authenticator of any of the specified types is available
+ * at the time <code>BiometricPrompt#authenticate(...)</code> is called, authentication will
+ * be canceled and {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}
+ * will be invoked with an appropriate error code.
+ *
+ * <p>This method should be preferred over {@link #setDeviceCredentialAllowed(boolean)} and
+ * overrides the latter if both are used. Using this method to enable device credential
+ * authentication (with {@link Authenticators#DEVICE_CREDENTIAL}) will replace the negative
+ * button on the prompt, making it an error to also call
+ * {@link #setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.
+ *
+ * @param authenticators A bit field representing all valid authenticator types that may be
+ * invoked by the prompt.
+ * @return This builder.
*/
- @NonNull public BiometricPrompt build() {
+ @NonNull
+ public Builder setAllowedAuthenticators(@Authenticators.Types int authenticators) {
+ mBundle.putInt(KEY_AUTHENTICATORS_ALLOWED, authenticators);
+ return this;
+ }
+
+ /**
+ * Creates a {@link BiometricPrompt}.
+ *
+ * @return An instance of {@link BiometricPrompt}.
+ *
+ * @throws IllegalArgumentException If any required fields are unset, or if given any
+ * invalid combination of field values.
+ */
+ @NonNull
+ public BiometricPrompt build() {
final CharSequence title = mBundle.getCharSequence(KEY_TITLE);
final CharSequence negative = mBundle.getCharSequence(KEY_NEGATIVE_TEXT);
- final boolean useDefaultTitle = mBundle.getBoolean(KEY_USE_DEFAULT_TITLE);
- final boolean allowCredential = mBundle.getBoolean(KEY_ALLOW_DEVICE_CREDENTIAL);
- final Object authenticatorsAllowed = mBundle.get(KEY_AUTHENTICATORS_ALLOWED);
+ final boolean useDefaultTitle = mBundle.getBoolean(KEY_USE_DEFAULT_TITLE, false);
+ final boolean deviceCredentialAllowed = mBundle.getBoolean(KEY_ALLOW_DEVICE_CREDENTIAL);
+ final @Authenticators.Types int authenticators =
+ mBundle.getInt(KEY_AUTHENTICATORS_ALLOWED, 0);
+ final boolean willShowDeviceCredentialButton = deviceCredentialAllowed
+ || (authenticators & Authenticators.DEVICE_CREDENTIAL) != 0;
if (TextUtils.isEmpty(title) && !useDefaultTitle) {
throw new IllegalArgumentException("Title must be set and non-empty");
- } else if (TextUtils.isEmpty(negative) && !allowCredential) {
+ } else if (TextUtils.isEmpty(negative) && !willShowDeviceCredentialButton) {
throw new IllegalArgumentException("Negative text must be set and non-empty");
- } else if (!TextUtils.isEmpty(negative) && allowCredential) {
+ } else if (!TextUtils.isEmpty(negative) && willShowDeviceCredentialButton) {
throw new IllegalArgumentException("Can't have both negative button behavior"
+ " and device credential enabled");
- } else if (authenticatorsAllowed != null && allowCredential) {
- throw new IllegalArgumentException("setAuthenticatorsAllowed and"
- + " setDeviceCredentialAllowed should not be used simultaneously");
}
return new BiometricPrompt(mContext, mBundle, mPositiveButtonInfo, mNegativeButtonInfo);
}
@@ -394,6 +446,75 @@
}
/**
+ * Gets the title for the prompt, as set by {@link Builder#setTitle(CharSequence)}.
+ * @return The title of the prompt, which is guaranteed to be non-null.
+ */
+ @NonNull
+ public CharSequence getTitle() {
+ return mBundle.getCharSequence(KEY_TITLE, "");
+ }
+
+ /**
+ * Whether to use a default modality-specific title. For internal use only.
+ * @return See {@link Builder#setUseDefaultTitle()}.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public boolean shouldUseDefaultTitle() {
+ return mBundle.getBoolean(KEY_USE_DEFAULT_TITLE, false);
+ }
+
+ /**
+ * Gets the subtitle for the prompt, as set by {@link Builder#setSubtitle(CharSequence)}.
+ * @return The subtitle for the prompt, or null if the prompt has no subtitle.
+ */
+ @Nullable
+ public CharSequence getSubtitle() {
+ return mBundle.getCharSequence(KEY_SUBTITLE);
+ }
+
+ /**
+ * Gets the description for the prompt, as set by {@link Builder#setDescription(CharSequence)}.
+ * @return The description for the prompt, or null if the prompt has no description.
+ */
+ @Nullable
+ public CharSequence getDescription() {
+ return mBundle.getCharSequence(KEY_DESCRIPTION);
+ }
+
+ /**
+ * Gets the negative button text for the prompt, as set by
+ * {@link Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.
+ * @return The negative button text for the prompt, or null if no negative button text was set.
+ */
+ @Nullable
+ public CharSequence getNegativeButtonText() {
+ return mBundle.getCharSequence(KEY_NEGATIVE_TEXT);
+ }
+
+ /**
+ * Determines if explicit user confirmation is required by the prompt, as set by
+ * {@link Builder#setConfirmationRequired(boolean)}.
+ *
+ * @return true if explicit user confirmation is required, or false otherwise.
+ */
+ public boolean isConfirmationRequired() {
+ return mBundle.getBoolean(KEY_REQUIRE_CONFIRMATION, true);
+ }
+
+ /**
+ * Gets the type(s) of authenticators that may be invoked by the prompt to authenticate the
+ * user, as set by {@link Builder#setAllowedAuthenticators(int)}.
+ *
+ * @return A bit field representing the type(s) of authenticators that may be invoked by the
+ * prompt (as defined by {@link Authenticators}), or 0 if this field was not set.
+ */
+ @Nullable
+ public int getAllowedAuthenticators() {
+ return mBundle.getInt(KEY_AUTHENTICATORS_ALLOWED, 0);
+ }
+
+ /**
* A wrapper class for the crypto objects supported by BiometricPrompt. Currently the framework
* supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
*/
@@ -509,10 +630,12 @@
/**
* Authenticates for the given user.
+ *
* @param cancel An object that can be used to cancel authentication
* @param executor An executor to handle callback events
* @param callback An object to receive authentication events
* @param userId The user to authenticate
+ *
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
@@ -542,23 +665,33 @@
* authentication errors through {@link AuthenticationCallback}, and button events through the
* corresponding callback set in {@link Builder#setNegativeButton(CharSequence, Executor,
* DialogInterface.OnClickListener)}. It is safe to reuse the {@link BiometricPrompt} object,
- * and calling {@link BiometricPrompt#authenticate( CancellationSignal, Executor,
+ * and calling {@link BiometricPrompt#authenticate(CancellationSignal, Executor,
* AuthenticationCallback)} while an existing authentication attempt is occurring will stop the
* previous client and start a new authentication. The interrupted client will receive a
* cancelled notification through {@link AuthenticationCallback#onAuthenticationError(int,
* CharSequence)}.
*
- * Note: Applications generally should not cancel and start authentication in quick succession.
- * For example, to properly handle authentication across configuration changes, it's recommended
- * to use BiometricPrompt in a fragment with setRetainInstance(true). By doing so, the
- * application will not need to cancel/restart authentication during the configuration change.
+ * <p>Note: Applications generally should not cancel and start authentication in quick
+ * succession. For example, to properly handle authentication across configuration changes, it's
+ * recommended to use BiometricPrompt in a fragment with setRetainInstance(true). By doing so,
+ * the application will not need to cancel/restart authentication during the configuration
+ * change.
*
- * @throws IllegalArgumentException If any of the arguments are null
+ * <p>Per the Android CDD, only biometric authenticators that meet or exceed the requirements
+ * for <strong>Strong</strong> are permitted to integrate with Keystore to perform related
+ * cryptographic operations. Therefore, it is an error to call this method after explicitly
+ * calling {@link Builder#setAllowedAuthenticators(int)} with any value other than
+ * {@link Authenticators#BIOMETRIC_STRONG}.
*
- * @param crypto Object associated with the call
- * @param cancel An object that can be used to cancel authentication
- * @param executor An executor to handle callback events
- * @param callback An object to receive authentication events
+ * @throws IllegalArgumentException If any of the arguments are null, if
+ * {@link Builder#setDeviceCredentialAllowed(boolean)} was explicitly set to true, or if
+ * {@link Builder#setAllowedAuthenticators(int)} was explicitly called with any value other than
+ * {@link Authenticators#BIOMETRIC_STRONG}.
+ *
+ * @param crypto A cryptographic operation to be unlocked after successful authentication.
+ * @param cancel An object that can be used to cancel authentication.
+ * @param executor An executor to handle callback events.
+ * @param callback An object to receive authentication events.
*/
@RequiresPermission(USE_BIOMETRIC)
public void authenticate(@NonNull CryptoObject crypto,
@@ -577,9 +710,20 @@
if (callback == null) {
throw new IllegalArgumentException("Must supply a callback");
}
- if (mBundle.getBoolean(KEY_ALLOW_DEVICE_CREDENTIAL)) {
+
+ @Authenticators.Types int authenticators = mBundle.getInt(
+ KEY_AUTHENTICATORS_ALLOWED, Authenticators.BIOMETRIC_STRONG);
+
+ if (mBundle.getBoolean(KEY_ALLOW_DEVICE_CREDENTIAL)
+ || (authenticators & Authenticators.DEVICE_CREDENTIAL) != 0) {
throw new IllegalArgumentException("Device credential not supported with crypto");
}
+
+ // Disallow any non-Strong biometric authenticator types.
+ if ((authenticators & ~Authenticators.BIOMETRIC_STRONG) != 0) {
+ throw new IllegalArgumentException("Only Strong biometrics supported with crypto");
+ }
+
authenticateInternal(crypto, cancel, executor, callback, mContext.getUserId());
}
@@ -598,16 +742,17 @@
* authentication. The interrupted client will receive a cancelled notification through {@link
* AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
*
- * Note: Applications generally should not cancel and start authentication in quick succession.
- * For example, to properly handle authentication across configuration changes, it's recommended
- * to use BiometricPrompt in a fragment with setRetainInstance(true). By doing so, the
- * application will not need to cancel/restart authentication during the configuration change.
+ * <p>Note: Applications generally should not cancel and start authentication in quick
+ * succession. For example, to properly handle authentication across configuration changes, it's
+ * recommended to use BiometricPrompt in a fragment with setRetainInstance(true). By doing so,
+ * the application will not need to cancel/restart authentication during the configuration
+ * change.
*
- * @throws IllegalArgumentException If any of the arguments are null
+ * @throws IllegalArgumentException If any of the arguments are null.
*
- * @param cancel An object that can be used to cancel authentication
- * @param executor An executor to handle callback events
- * @param callback An object to receive authentication events
+ * @param cancel An object that can be used to cancel authentication.
+ * @param executor An executor to handle callback events.
+ * @param callback An object to receive authentication events.
*/
@RequiresPermission(USE_BIOMETRIC)
public void authenticate(@NonNull CancellationSignal cancel,
@@ -653,8 +798,22 @@
mAuthenticationCallback = callback;
final long sessionId = crypto != null ? crypto.getOpId() : 0;
if (BiometricManager.hasBiometrics(mContext)) {
+ final Bundle bundle;
+ if (crypto != null) {
+ // Allowed authenticators should default to BIOMETRIC_STRONG for crypto auth.
+ // Note that we use a new bundle here so as to not overwrite the application's
+ // preference, since it is possible that the same prompt configuration be used
+ // without a crypto object later.
+ bundle = new Bundle(mBundle);
+ bundle.putInt(KEY_AUTHENTICATORS_ALLOWED,
+ mBundle.getInt(KEY_AUTHENTICATORS_ALLOWED,
+ Authenticators.BIOMETRIC_STRONG));
+ } else {
+ bundle = mBundle;
+ }
+
mService.authenticate(mToken, sessionId, userId, mBiometricServiceReceiver,
- mContext.getOpPackageName(), mBundle);
+ mContext.getOpPackageName(), bundle);
} else {
mExecutor.execute(() -> {
callback.onAuthenticationError(BiometricPrompt.BIOMETRIC_ERROR_HW_NOT_PRESENT,
diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl
index 516a25d..d482198 100644
--- a/core/java/android/hardware/biometrics/IAuthService.aidl
+++ b/core/java/android/hardware/biometrics/IAuthService.aidl
@@ -35,7 +35,7 @@
// TODO(b/141025588): Make userId the first arg to be consistent with hasEnrolledBiometrics.
// Checks if biometrics can be used.
- int canAuthenticate(String opPackageName, int userId);
+ int canAuthenticate(String opPackageName, int userId, int authenticators);
// Checks if any biometrics are enrolled.
boolean hasEnrolledBiometrics(int userId, String opPackageName);
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index ca02421..8a6be18 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -35,7 +35,7 @@
void cancelAuthentication(IBinder token, String opPackageName);
// Checks if biometrics can be used.
- int canAuthenticate(String opPackageName, int userId);
+ int canAuthenticate(String opPackageName, int userId, int authenticators);
// Checks if any biometrics are enrolled.
boolean hasEnrolledBiometrics(int userId, String opPackageName);
@@ -43,7 +43,8 @@
// Registers an authenticator (e.g. face, fingerprint, iris).
// Id must be unique, whereas strength and modality don't need to be.
// TODO(b/123321528): Turn strength and modality into enums.
- void registerAuthenticator(int id, int strength, int modality, IBiometricAuthenticator authenticator);
+ void registerAuthenticator(int id, int modality, int strength,
+ IBiometricAuthenticator authenticator);
// Register callback for when keyguard biometric eligibility changes.
void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback);
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index eb1684f..c86c83c 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -2381,7 +2381,11 @@
* This id is provided by its own data source, and can be used to backup metadata
* to the server.
* This should be unique within each set of account_name/account_type/data_set
+ *
+ * @deprecated This column is no longer supported as of Android version
+ * {@link android.os.Build.VERSION_CODES#R}.
*/
+ @Deprecated
public static final String BACKUP_ID = "backup_id";
/**
@@ -2445,7 +2449,11 @@
* Flag indicating that a raw contact's metadata has changed, and its metadata
* needs to be synchronized by the server.
* <P>Type: INTEGER (boolean)</P>
+ *
+ * @deprecated This column is no longer supported as of Android version
+ * {@link android.os.Build.VERSION_CODES#R}.
*/
+ @Deprecated
public static final String METADATA_DIRTY = "metadata_dirty";
}
@@ -4188,7 +4196,10 @@
* Hash id on the data fields, used for backup and restore.
*
* @hide
+ * @deprecated This column is no longer supported as of Android version
+ * {@link android.os.Build.VERSION_CODES#R}.
*/
+ @Deprecated
public static final String HASH_ID = "hash_id";
/**
@@ -9495,7 +9506,10 @@
/**
* @hide
+ * @deprecated These columns are no longer supported as of Android version
+ * {@link android.os.Build.VERSION_CODES#R}.
*/
+ @Deprecated
@SystemApi
protected interface MetadataSyncColumns {
@@ -9602,7 +9616,10 @@
* from server before it is merged into other CP2 tables.
*
* @hide
+ * @deprecated These columns are no longer supported as of Android version
+ * {@link android.os.Build.VERSION_CODES#R}.
*/
+ @Deprecated
@SystemApi
public static final class MetadataSync implements BaseColumns, MetadataSyncColumns {
@@ -9638,7 +9655,10 @@
/**
* @hide
+ * @deprecated These columns are no longer supported as of Android version
+ * {@link android.os.Build.VERSION_CODES#R}.
*/
+ @Deprecated
@SystemApi
protected interface MetadataSyncStateColumns {
@@ -9672,7 +9692,10 @@
* sync state for a set of accounts.
*
* @hide
+ * @deprecated These columns are no longer supported as of Android version
+ * {@link android.os.Build.VERSION_CODES#R}.
*/
+ @Deprecated
@SystemApi
public static final class MetadataSyncState implements BaseColumns, MetadataSyncStateColumns {
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index ef22d70..6650cf2 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -350,6 +350,15 @@
public static final String NAMESPACE_PRIVACY = "privacy";
/**
+ * Namespace for biometrics related features
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final String NAMESPACE_BIOMETRICS = "biometrics";
+
+ /**
* Permission related properties definitions.
*
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index bd1eb21..ee9aa09 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8410,6 +8410,12 @@
public static final String TAP_GESTURE = "tap_gesture";
/**
+ * Controls whether the people strip is enabled.
+ * @hide
+ */
+ public static final String PEOPLE_STRIP = "people_strip";
+
+ /**
* Keys we no longer back up under the current schema, but want to continue to
* process when restoring historical backup datasets.
*
@@ -14223,6 +14229,20 @@
*/
public static final int ADD_WIFI_RESULT_ALREADY_EXISTS = 2;
+ /**
+ * Activity Action: Allows user to select current bug report handler.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ *
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_BUGREPORT_HANDLER_SETTINGS =
+ "android.settings.BUGREPORT_HANDLER_SETTINGS";
+
private static final String[] PM_WRITE_SETTINGS = {
android.Manifest.permission.WRITE_SETTINGS
};
diff --git a/core/java/android/service/dreams/Sandman.java b/core/java/android/service/dreams/Sandman.java
index efb8923..f2cedbc 100644
--- a/core/java/android/service/dreams/Sandman.java
+++ b/core/java/android/service/dreams/Sandman.java
@@ -36,12 +36,6 @@
public final class Sandman {
private static final String TAG = "Sandman";
- // The component name of a special dock app that merely launches a dream.
- // We don't want to launch this app when docked because it causes an unnecessary
- // activity transition. We just want to start the dream.
- private static final ComponentName SOMNAMBULATOR_COMPONENT =
- new ComponentName("com.android.systemui", "com.android.systemui.Somnambulator");
-
// The sandman is eternal. No one instantiates him.
private Sandman() {
@@ -52,8 +46,11 @@
* False if we should dream instead, if appropriate.
*/
public static boolean shouldStartDockApp(Context context, Intent intent) {
+ final ComponentName somnambulatorComponent = ComponentName.unflattenFromString(
+ context.getResources().getString(
+ com.android.internal.R.string.config_somnambulatorComponent));
ComponentName name = intent.resolveActivity(context.getPackageManager());
- return name != null && !name.equals(SOMNAMBULATOR_COMPONENT);
+ return name != null && !name.equals(somnambulatorComponent);
}
/**
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index dd2586c..d0675ed 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -481,9 +481,12 @@
* as true on their TileService Manifest declaration, and will do nothing otherwise.
*/
public static final void requestListeningState(Context context, ComponentName component) {
+ final ComponentName sysuiComponent = ComponentName.unflattenFromString(
+ context.getResources().getString(
+ com.android.internal.R.string.config_systemUIServiceComponent));
Intent intent = new Intent(ACTION_REQUEST_LISTENING);
intent.putExtra(Intent.EXTRA_COMPONENT_NAME, component);
- intent.setPackage("com.android.systemui");
+ intent.setPackage(sysuiComponent.getPackageName());
context.sendBroadcast(intent, Manifest.permission.BIND_QUICK_SETTINGS_TILE);
}
}
diff --git a/core/java/android/util/LongArrayQueue.java b/core/java/android/util/LongArrayQueue.java
index d5f0484..5c701db 100644
--- a/core/java/android/util/LongArrayQueue.java
+++ b/core/java/android/util/LongArrayQueue.java
@@ -162,4 +162,24 @@
final int index = (mTail == 0) ? mValues.length - 1 : mTail - 1;
return mValues[index];
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ if (mSize <= 0) {
+ return "{}";
+ }
+
+ final StringBuilder buffer = new StringBuilder(mSize * 64);
+ buffer.append('{');
+ buffer.append(get(0));
+ for (int i = 1; i < mSize; i++) {
+ buffer.append(", ");
+ buffer.append(get(i));
+ }
+ buffer.append('}');
+ return buffer.toString();
+ }
}
diff --git a/core/java/android/util/SparseSetArray.java b/core/java/android/util/SparseSetArray.java
index c1873d7..9f0f246 100644
--- a/core/java/android/util/SparseSetArray.java
+++ b/core/java/android/util/SparseSetArray.java
@@ -27,8 +27,8 @@
}
/**
- * Add a value at index n.
- * @return FALSE when the value already existed at the given index, TRUE otherwise.
+ * Add a value for key n.
+ * @return FALSE when the value already existed for the given key, TRUE otherwise.
*/
public boolean add(int n, T value) {
ArraySet<T> set = mData.get(n);
@@ -51,7 +51,7 @@
}
/**
- * @return whether a value exists at index n.
+ * @return whether the value exists for the key n.
*/
public boolean contains(int n, T value) {
final ArraySet<T> set = mData.get(n);
@@ -62,15 +62,15 @@
}
/**
- * @return the set of items at index n
+ * @return the set of items of key n
*/
public ArraySet<T> get(int n) {
return mData.get(n);
}
/**
- * Remove a value from index n.
- * @return TRUE when the value existed at the given index and removed, FALSE otherwise.
+ * Remove a value for key n.
+ * @return TRUE when the value existed for the given key and removed, FALSE otherwise.
*/
public boolean remove(int n, T value) {
final ArraySet<T> set = mData.get(n);
@@ -85,7 +85,7 @@
}
/**
- * Remove all values from index n.
+ * Remove all values for key n.
*/
public void remove(int n) {
mData.remove(n);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 34092e2..3b6c55b 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1313,14 +1313,11 @@
* set for you by Window as described in {@link Window#setFlags}.*/
public static final int FLAG_LAYOUT_INSET_DECOR = 0x00010000;
- /** Window flag: invert the state of {@link #FLAG_NOT_FOCUSABLE} with
- * respect to how this window interacts with the current method. That
- * is, if FLAG_NOT_FOCUSABLE is set and this flag is set, then the
- * window will behave as if it needs to interact with the input method
- * and thus be placed behind/away from it; if FLAG_NOT_FOCUSABLE is
- * not set and this flag is set, then the window will behave as if it
- * doesn't need to interact with the input method and can be placed
- * to use more space and cover the input method.
+ /** Window flag: When set, input method can't interact with the focusable window
+ * and can be placed to use more space and cover the input method.
+ * Note: When combined with {@link #FLAG_NOT_FOCUSABLE}, this flag has no
+ * effect since input method cannot interact with windows having {@link #FLAG_NOT_FOCUSABLE}
+ * flag set.
*/
public static final int FLAG_ALT_FOCUSABLE_IM = 0x00020000;
@@ -1998,16 +1995,12 @@
*
* @param flags The current window manager flags.
*
- * @return Returns true if such a window should be behind/interact
- * with an input method, false if not.
+ * @return Returns {@code true} if such a window should be behind/interact
+ * with an input method, {@code false} if not.
*/
public static boolean mayUseInputMethod(int flags) {
- switch (flags&(FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM)) {
- case 0:
- case FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM:
- return true;
- }
- return false;
+ return (flags & FLAG_NOT_FOCUSABLE) != FLAG_NOT_FOCUSABLE
+ && (flags & FLAG_ALT_FOCUSABLE_IM) != FLAG_ALT_FOCUSABLE_IM;
}
/**
diff --git a/telephony/java/com/android/ims/internal/uce/common/CapInfo.aidl b/core/java/com/android/ims/internal/uce/common/CapInfo.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/common/CapInfo.aidl
rename to core/java/com/android/ims/internal/uce/common/CapInfo.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/common/CapInfo.java b/core/java/com/android/ims/internal/uce/common/CapInfo.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/common/CapInfo.java
rename to core/java/com/android/ims/internal/uce/common/CapInfo.java
diff --git a/telephony/java/com/android/ims/internal/uce/common/StatusCode.aidl b/core/java/com/android/ims/internal/uce/common/StatusCode.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/common/StatusCode.aidl
rename to core/java/com/android/ims/internal/uce/common/StatusCode.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/common/StatusCode.java b/core/java/com/android/ims/internal/uce/common/StatusCode.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/common/StatusCode.java
rename to core/java/com/android/ims/internal/uce/common/StatusCode.java
diff --git a/telephony/java/com/android/ims/internal/uce/common/UceLong.aidl b/core/java/com/android/ims/internal/uce/common/UceLong.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/common/UceLong.aidl
rename to core/java/com/android/ims/internal/uce/common/UceLong.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/common/UceLong.java b/core/java/com/android/ims/internal/uce/common/UceLong.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/common/UceLong.java
rename to core/java/com/android/ims/internal/uce/common/UceLong.java
diff --git a/telephony/java/com/android/ims/internal/uce/options/IOptionsListener.aidl b/core/java/com/android/ims/internal/uce/options/IOptionsListener.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/options/IOptionsListener.aidl
rename to core/java/com/android/ims/internal/uce/options/IOptionsListener.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/options/IOptionsService.aidl b/core/java/com/android/ims/internal/uce/options/IOptionsService.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/options/IOptionsService.aidl
rename to core/java/com/android/ims/internal/uce/options/IOptionsService.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsCapInfo.aidl b/core/java/com/android/ims/internal/uce/options/OptionsCapInfo.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/options/OptionsCapInfo.aidl
rename to core/java/com/android/ims/internal/uce/options/OptionsCapInfo.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsCapInfo.java b/core/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
rename to core/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsCmdId.aidl b/core/java/com/android/ims/internal/uce/options/OptionsCmdId.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/options/OptionsCmdId.aidl
rename to core/java/com/android/ims/internal/uce/options/OptionsCmdId.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsCmdId.java b/core/java/com/android/ims/internal/uce/options/OptionsCmdId.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/options/OptionsCmdId.java
rename to core/java/com/android/ims/internal/uce/options/OptionsCmdId.java
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsCmdStatus.aidl b/core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/options/OptionsCmdStatus.aidl
rename to core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java b/core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
rename to core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsSipResponse.aidl b/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/options/OptionsSipResponse.aidl
rename to core/java/com/android/ims/internal/uce/options/OptionsSipResponse.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsSipResponse.java b/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
rename to core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
diff --git a/telephony/java/com/android/ims/internal/uce/presence/IPresenceListener.aidl b/core/java/com/android/ims/internal/uce/presence/IPresenceListener.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/IPresenceListener.aidl
rename to core/java/com/android/ims/internal/uce/presence/IPresenceListener.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/presence/IPresenceService.aidl b/core/java/com/android/ims/internal/uce/presence/IPresenceService.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/IPresenceService.aidl
rename to core/java/com/android/ims/internal/uce/presence/IPresenceService.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresCapInfo.aidl b/core/java/com/android/ims/internal/uce/presence/PresCapInfo.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresCapInfo.aidl
rename to core/java/com/android/ims/internal/uce/presence/PresCapInfo.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresCapInfo.java b/core/java/com/android/ims/internal/uce/presence/PresCapInfo.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresCapInfo.java
rename to core/java/com/android/ims/internal/uce/presence/PresCapInfo.java
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresCmdId.aidl b/core/java/com/android/ims/internal/uce/presence/PresCmdId.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresCmdId.aidl
rename to core/java/com/android/ims/internal/uce/presence/PresCmdId.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresCmdId.java b/core/java/com/android/ims/internal/uce/presence/PresCmdId.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresCmdId.java
rename to core/java/com/android/ims/internal/uce/presence/PresCmdId.java
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresCmdStatus.aidl b/core/java/com/android/ims/internal/uce/presence/PresCmdStatus.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresCmdStatus.aidl
rename to core/java/com/android/ims/internal/uce/presence/PresCmdStatus.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresCmdStatus.java b/core/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
rename to core/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.aidl b/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.aidl
rename to core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java b/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
rename to core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresResInfo.aidl b/core/java/com/android/ims/internal/uce/presence/PresResInfo.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresResInfo.aidl
rename to core/java/com/android/ims/internal/uce/presence/PresResInfo.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresResInfo.java b/core/java/com/android/ims/internal/uce/presence/PresResInfo.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresResInfo.java
rename to core/java/com/android/ims/internal/uce/presence/PresResInfo.java
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.aidl b/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.aidl
rename to core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java b/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
rename to core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresRlmiInfo.aidl b/core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresRlmiInfo.aidl
rename to core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java b/core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
rename to core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresServiceInfo.aidl b/core/java/com/android/ims/internal/uce/presence/PresServiceInfo.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresServiceInfo.aidl
rename to core/java/com/android/ims/internal/uce/presence/PresServiceInfo.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresServiceInfo.java b/core/java/com/android/ims/internal/uce/presence/PresServiceInfo.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresServiceInfo.java
rename to core/java/com/android/ims/internal/uce/presence/PresServiceInfo.java
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresSipResponse.aidl b/core/java/com/android/ims/internal/uce/presence/PresSipResponse.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresSipResponse.aidl
rename to core/java/com/android/ims/internal/uce/presence/PresSipResponse.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresSipResponse.java b/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresSipResponse.java
rename to core/java/com/android/ims/internal/uce/presence/PresSipResponse.java
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresSubscriptionState.aidl b/core/java/com/android/ims/internal/uce/presence/PresSubscriptionState.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresSubscriptionState.aidl
rename to core/java/com/android/ims/internal/uce/presence/PresSubscriptionState.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java b/core/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java
rename to core/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresTupleInfo.aidl b/core/java/com/android/ims/internal/uce/presence/PresTupleInfo.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresTupleInfo.aidl
rename to core/java/com/android/ims/internal/uce/presence/PresTupleInfo.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresTupleInfo.java b/core/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
rename to core/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
diff --git a/telephony/java/com/android/ims/internal/uce/uceservice/IUceListener.aidl b/core/java/com/android/ims/internal/uce/uceservice/IUceListener.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/uceservice/IUceListener.aidl
rename to core/java/com/android/ims/internal/uce/uceservice/IUceListener.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl b/core/java/com/android/ims/internal/uce/uceservice/IUceService.aidl
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl
rename to core/java/com/android/ims/internal/uce/uceservice/IUceService.aidl
diff --git a/telephony/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java b/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java
rename to core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java
diff --git a/telephony/java/com/android/ims/internal/uce/uceservice/UceServiceBase.java b/core/java/com/android/ims/internal/uce/uceservice/UceServiceBase.java
similarity index 100%
rename from telephony/java/com/android/ims/internal/uce/uceservice/UceServiceBase.java
rename to core/java/com/android/ims/internal/uce/uceservice/UceServiceBase.java
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index f5fae19..22fe31e 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -444,7 +444,7 @@
return sum;
}
- /**
+ /**
* Configure the native library files managed by Incremental Service. Makes sure Incremental
* Service will create native library directories and set up native library binary files in the
* same structure as they are in non-incremental installations.
@@ -458,8 +458,20 @@
*/
public static int configureNativeBinariesForSupportedAbi(AndroidPackage pkg, Handle handle,
File libraryRoot, String[] abiList, boolean useIsaSubdir) {
- // TODO(b/136132412): Implement this.
- return -1;
+ int abi = findSupportedAbi(handle, abiList);
+ if (abi < 0) {
+ Slog.e(TAG, "Failed to find find matching ABI.");
+ return abi;
+ }
+
+ // Currently only support installations that have pre-configured native library files
+ // TODO(b/136132412): implement this after incfs supports file mapping
+ if (!libraryRoot.exists()) {
+ Slog.e(TAG, "Incremental installation currently does not configure native libs");
+ return INSTALL_FAILED_NO_MATCHING_ABIS;
+ }
+
+ return abi;
}
// We don't care about the other return values for now.
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 2f34aa0..d8e8d67 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -60,15 +60,10 @@
@UnsupportedAppUsage(maxTargetSdk = 28)
void notifyDataActivity(int state);
void notifyDataActivityForSubscriber(in int subId, int state);
- void notifyDataConnection(int state, boolean isDataConnectivityPossible,
- String apn, String apnType, in LinkProperties linkProperties,
- in NetworkCapabilities networkCapabilities, int networkType, boolean roaming);
void notifyDataConnectionForSubscriber(int phoneId, int subId, int state,
boolean isDataConnectivityPossible,
String apn, String apnType, in LinkProperties linkProperties,
in NetworkCapabilities networkCapabilities, int networkType, boolean roaming);
- @UnsupportedAppUsage
- void notifyDataConnectionFailed(String apnType);
void notifyDataConnectionFailedForSubscriber(int phoneId, int subId, String apnType);
@UnsupportedAppUsage(maxTargetSdk = 28)
void notifyCellLocation(in Bundle cellLocation);
diff --git a/core/java/com/android/internal/util/ScreenRecordHelper.java b/core/java/com/android/internal/util/ScreenRecordHelper.java
index 64d0898..ec7ed4e 100644
--- a/core/java/com/android/internal/util/ScreenRecordHelper.java
+++ b/core/java/com/android/internal/util/ScreenRecordHelper.java
@@ -24,10 +24,6 @@
* Helper class to initiate a screen recording
*/
public class ScreenRecordHelper {
- private static final String SYSUI_PACKAGE = "com.android.systemui";
- private static final String SYSUI_SCREENRECORD_LAUNCHER =
- "com.android.systemui.screenrecord.ScreenRecordDialog";
-
private final Context mContext;
/**
@@ -42,8 +38,9 @@
* Show dialog of screen recording options to user.
*/
public void launchRecordPrompt() {
- final ComponentName launcherComponent = new ComponentName(SYSUI_PACKAGE,
- SYSUI_SCREENRECORD_LAUNCHER);
+ final ComponentName launcherComponent = ComponentName.unflattenFromString(
+ mContext.getResources().getString(
+ com.android.internal.R.string.config_screenRecorderComponent));
final Intent intent = new Intent();
intent.setComponent(launcherComponent);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index f6f187f..8cad5a0 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -20,12 +20,6 @@
public class ScreenshotHelper {
private static final String TAG = "ScreenshotHelper";
- private static final String SYSUI_PACKAGE = "com.android.systemui";
- private static final String SYSUI_SCREENSHOT_SERVICE =
- "com.android.systemui.screenshot.TakeScreenshotService";
- private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER =
- "com.android.systemui.screenshot.ScreenshotServiceErrorReceiver";
-
// Time until we give up on the screenshot & show an error instead.
private final int SCREENSHOT_TIMEOUT_MS = 10000;
@@ -94,8 +88,9 @@
if (mScreenshotConnection != null) {
return;
}
- final ComponentName serviceComponent = new ComponentName(SYSUI_PACKAGE,
- SYSUI_SCREENSHOT_SERVICE);
+ final ComponentName serviceComponent = ComponentName.unflattenFromString(
+ mContext.getResources().getString(
+ com.android.internal.R.string.config_screenshotServiceComponent));
final Intent serviceIntent = new Intent();
final Runnable mScreenshotTimeout = new Runnable() {
@@ -181,8 +176,9 @@
*/
private void notifyScreenshotError() {
// If the service process is killed, then ask it to clean up after itself
- final ComponentName errorComponent = new ComponentName(SYSUI_PACKAGE,
- SYSUI_SCREENSHOT_ERROR_RECEIVER);
+ final ComponentName errorComponent = ComponentName.unflattenFromString(
+ mContext.getResources().getString(
+ com.android.internal.R.string.config_screenshotErrorReceiverComponent));
// Broadcast needs to have a valid action. We'll just pick
// a generic one, since the receiver here doesn't care.
Intent errorIntent = new Intent(Intent.ACTION_USER_PRESENT);
diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp
index 1290026..6c2a5a3 100644
--- a/core/jni/android/graphics/AnimatedImageDrawable.cpp
+++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp
@@ -25,6 +25,7 @@
#include <SkPicture.h>
#include <SkPictureRecorder.h>
#include <hwui/AnimatedImageDrawable.h>
+#include <hwui/ImageDecoder.h>
#include <hwui/Canvas.h>
#include <utils/Looper.h>
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index 4d907f6..627f8f5 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -20,10 +20,12 @@
#include "CreateJavaOutputStreamAdaptor.h"
#include "GraphicsJNI.h"
#include "ImageDecoder.h"
+#include "NinePatchPeeker.h"
#include "Utils.h"
#include "core_jni_helpers.h"
#include <hwui/Bitmap.h>
+#include <hwui/ImageDecoder.h>
#include <HardwareBitmapUploader.h>
#include <SkAndroidCodec.h>
@@ -49,6 +51,28 @@
static jmethodID gCanvas_constructorMethodID;
static jmethodID gCanvas_releaseMethodID;
+// These need to stay in sync with ImageDecoder.java's Allocator constants.
+enum Allocator {
+ kDefault_Allocator = 0,
+ kSoftware_Allocator = 1,
+ kSharedMemory_Allocator = 2,
+ kHardware_Allocator = 3,
+};
+
+// These need to stay in sync with ImageDecoder.java's Error constants.
+enum Error {
+ kSourceException = 1,
+ kSourceIncomplete = 2,
+ kSourceMalformedData = 3,
+};
+
+// These need to stay in sync with PixelFormat.java's Format constants.
+enum PixelFormat {
+ kUnknown = 0,
+ kTranslucent = -3,
+ kOpaque = -1,
+};
+
// Clear and return any pending exception for handling other than throwing directly.
static jthrowable get_and_clear_exception(JNIEnv* env) {
jthrowable jexception = env->ExceptionOccurred();
@@ -59,7 +83,7 @@
}
// Throw a new ImageDecoder.DecodeException. Returns null for convenience.
-static jobject throw_exception(JNIEnv* env, ImageDecoder::Error error, const char* msg,
+static jobject throw_exception(JNIEnv* env, Error error, const char* msg,
jthrowable cause, jobject source) {
jstring jstr = nullptr;
if (msg) {
@@ -81,27 +105,27 @@
static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream,
jobject source, jboolean preferAnimation) {
if (!stream.get()) {
- return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to create a stream",
+ return throw_exception(env, kSourceMalformedData, "Failed to create a stream",
nullptr, source);
}
- std::unique_ptr<ImageDecoder> decoder(new ImageDecoder);
+ sk_sp<NinePatchPeeker> peeker(new NinePatchPeeker);
SkCodec::Result result;
auto codec = SkCodec::MakeFromStream(
- std::move(stream), &result, decoder->mPeeker.get(),
+ std::move(stream), &result, peeker.get(),
preferAnimation ? SkCodec::SelectionPolicy::kPreferAnimation
: SkCodec::SelectionPolicy::kPreferStillImage);
if (jthrowable jexception = get_and_clear_exception(env)) {
- return throw_exception(env, ImageDecoder::kSourceException, "", jexception, source);
+ return throw_exception(env, kSourceException, "", jexception, source);
}
if (!codec) {
switch (result) {
case SkCodec::kIncompleteInput:
- return throw_exception(env, ImageDecoder::kSourceIncomplete, "", nullptr, source);
+ return throw_exception(env, kSourceIncomplete, "", nullptr, source);
default:
SkString msg;
msg.printf("Failed to create image decoder with message '%s'",
SkCodec::ResultToString(result));
- return throw_exception(env, ImageDecoder::kSourceMalformedData, msg.c_str(),
+ return throw_exception(env, kSourceMalformedData, msg.c_str(),
nullptr, source);
}
@@ -109,21 +133,22 @@
const bool animated = codec->getFrameCount() > 1;
if (jthrowable jexception = get_and_clear_exception(env)) {
- return throw_exception(env, ImageDecoder::kSourceException, "", jexception, source);
+ return throw_exception(env, kSourceException, "", jexception, source);
}
- decoder->mCodec = SkAndroidCodec::MakeFromCodec(std::move(codec),
+ auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec),
SkAndroidCodec::ExifOrientationBehavior::kRespect);
- if (!decoder->mCodec.get()) {
- return throw_exception(env, ImageDecoder::kSourceMalformedData, "", nullptr, source);
+ if (!androidCodec.get()) {
+ return throw_exception(env, kSourceMalformedData, "", nullptr, source);
}
- const auto& info = decoder->mCodec->getInfo();
+ const auto& info = androidCodec->getInfo();
const int width = info.width();
const int height = info.height();
- const bool isNinePatch = decoder->mPeeker->mPatch != nullptr;
+ const bool isNinePatch = peeker->mPatch != nullptr;
+ ImageDecoder* decoder = new ImageDecoder(std::move(androidCodec), std::move(peeker));
return env->NewObject(gImageDecoder_class, gImageDecoder_constructorMethodID,
- reinterpret_cast<jlong>(decoder.release()), width, height,
+ reinterpret_cast<jlong>(decoder), width, height,
animated, isNinePatch);
}
@@ -133,7 +158,7 @@
struct stat fdStat;
if (fstat(descriptor, &fdStat) == -1) {
- return throw_exception(env, ImageDecoder::kSourceMalformedData,
+ return throw_exception(env, kSourceMalformedData,
"broken file descriptor; fstat returned -1", nullptr, source);
}
@@ -141,7 +166,7 @@
FILE* file = fdopen(dupDescriptor, "r");
if (file == NULL) {
close(dupDescriptor);
- return throw_exception(env, ImageDecoder::kSourceMalformedData, "Could not open file",
+ return throw_exception(env, kSourceMalformedData, "Could not open file",
nullptr, source);
}
@@ -154,7 +179,7 @@
std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage, false));
if (!stream.get()) {
- return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to create a stream",
+ return throw_exception(env, kSourceMalformedData, "Failed to create a stream",
nullptr, source);
}
@@ -177,7 +202,7 @@
std::unique_ptr<SkStream> stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer,
initialPosition, limit);
if (!stream) {
- return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to read ByteBuffer",
+ return throw_exception(env, kSourceMalformedData, "Failed to read ByteBuffer",
nullptr, source);
}
return native_create(env, std::move(stream), source, preferAnimation);
@@ -195,7 +220,7 @@
reinterpret_cast<jlong>(canvas.get()));
if (!jcanvas) {
doThrowOOME(env, "Failed to create Java Canvas for PostProcess!");
- return ImageDecoder::kUnknown;
+ return kUnknown;
}
// jcanvas now owns canvas.
@@ -206,43 +231,23 @@
static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
jobject jdecoder, jboolean jpostProcess,
- jint desiredWidth, jint desiredHeight, jobject jsubset,
+ jint targetWidth, jint targetHeight, jobject jsubset,
jboolean requireMutable, jint allocator,
jboolean requireUnpremul, jboolean preferRamOverQuality,
jboolean asAlphaMask, jlong colorSpaceHandle,
jboolean extended) {
auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
- SkAndroidCodec* codec = decoder->mCodec.get();
- const SkISize desiredSize = SkISize::Make(desiredWidth, desiredHeight);
- SkISize decodeSize = desiredSize;
- const int sampleSize = codec->computeSampleSize(&decodeSize);
- const bool scale = desiredSize != decodeSize;
- SkImageInfo decodeInfo = codec->getInfo().makeWH(decodeSize.width(), decodeSize.height());
- if (scale && requireUnpremul && kOpaque_SkAlphaType != decodeInfo.alphaType()) {
+ if (!decoder->setTargetSize(targetWidth, targetHeight)) {
+ doThrowISE(env, "Could not scale to target size!");
+ return nullptr;
+ }
+ if (requireUnpremul && !decoder->setOutAlphaType(kUnpremul_SkAlphaType)) {
doThrowISE(env, "Cannot scale unpremultiplied pixels!");
return nullptr;
}
- switch (decodeInfo.alphaType()) {
- case kUnpremul_SkAlphaType:
- if (!requireUnpremul) {
- decodeInfo = decodeInfo.makeAlphaType(kPremul_SkAlphaType);
- }
- break;
- case kPremul_SkAlphaType:
- if (requireUnpremul) {
- decodeInfo = decodeInfo.makeAlphaType(kUnpremul_SkAlphaType);
- }
- break;
- case kOpaque_SkAlphaType:
- break;
- case kUnknown_SkAlphaType:
- doThrowIOE(env, "Unknown alpha type");
- return nullptr;
- }
-
SkColorType colorType = kN32_SkColorType;
- if (asAlphaMask && decodeInfo.colorType() == kGray_8_SkColorType) {
+ if (asAlphaMask && decoder->gray()) {
// We have to trick Skia to decode this to a single channel.
colorType = kGray_8_SkColorType;
} else if (preferRamOverQuality) {
@@ -250,12 +255,12 @@
// result incorrect. If we call the postProcess before now and record
// to a picture, we can know whether alpha was added, and if not, we
// can still use 565.
- if (decodeInfo.alphaType() == kOpaque_SkAlphaType && !jpostProcess) {
+ if (decoder->opaque() && !jpostProcess) {
// If the final result will be hardware, decoding to 565 and then
// uploading to the gpu as 8888 will not save memory. This still
// may save us from using F16, but do not go down to 565.
- if (allocator != ImageDecoder::kHardware_Allocator &&
- (allocator != ImageDecoder::kDefault_Allocator || requireMutable)) {
+ if (allocator != kHardware_Allocator &&
+ (allocator != kDefault_Allocator || requireMutable)) {
colorType = kRGB_565_SkColorType;
}
}
@@ -263,12 +268,12 @@
} else if (extended) {
colorType = kRGBA_F16_SkColorType;
} else {
- colorType = codec->computeOutputColorType(colorType);
+ colorType = decoder->mCodec->computeOutputColorType(colorType);
}
const bool isHardware = !requireMutable
- && (allocator == ImageDecoder::kDefault_Allocator ||
- allocator == ImageDecoder::kHardware_Allocator)
+ && (allocator == kDefault_Allocator ||
+ allocator == kHardware_Allocator)
&& colorType != kGray_8_SkColorType;
if (colorType == kRGBA_F16_SkColorType && isHardware &&
@@ -276,12 +281,28 @@
colorType = kN32_SkColorType;
}
- sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
- colorSpace = codec->computeOutputColorSpace(colorType, colorSpace);
- decodeInfo = decodeInfo.makeColorType(colorType).makeColorSpace(colorSpace);
+ if (!decoder->setOutColorType(colorType)) {
+ doThrowISE(env, "Failed to set out color type!");
+ return nullptr;
+ }
+
+ {
+ sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
+ colorSpace = decoder->mCodec->computeOutputColorSpace(colorType, colorSpace);
+ decoder->setOutColorSpace(std::move(colorSpace));
+ }
+
+ if (jsubset) {
+ SkIRect subset;
+ GraphicsJNI::jrect_to_irect(env, jsubset, &subset);
+ if (!decoder->setCropRect(&subset)) {
+ doThrowISE(env, "Invalid crop rect!");
+ return nullptr;
+ }
+ }
SkBitmap bm;
- auto bitmapInfo = decodeInfo;
+ SkImageInfo bitmapInfo = decoder->getOutputInfo();
if (asAlphaMask && colorType == kGray_8_SkColorType) {
bitmapInfo = bitmapInfo.makeColorType(kAlpha_8_SkColorType);
}
@@ -291,10 +312,7 @@
}
sk_sp<Bitmap> nativeBitmap;
- // If we are going to scale or subset, we will create a new bitmap later on,
- // so use the heap for the temporary.
- // FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380.
- if (allocator == ImageDecoder::kSharedMemory_Allocator && !scale && !jsubset) {
+ if (allocator == kSharedMemory_Allocator) {
nativeBitmap = Bitmap::allocateAshmemBitmap(&bm);
} else {
nativeBitmap = Bitmap::allocateHeapBitmap(&bm);
@@ -302,16 +320,14 @@
if (!nativeBitmap) {
SkString msg;
msg.printf("OOM allocating Bitmap with dimensions %i x %i",
- decodeInfo.width(), decodeInfo.height());
+ bitmapInfo.width(), bitmapInfo.height());
doThrowOOME(env, msg.c_str());
return nullptr;
}
- SkAndroidCodec::AndroidOptions options;
- options.fSampleSize = sampleSize;
- auto result = codec->getAndroidPixels(decodeInfo, bm.getPixels(), bm.rowBytes(), &options);
+ SkCodec::Result result = decoder->decode(bm.getPixels(), bm.rowBytes());
jthrowable jexception = get_and_clear_exception(env);
- int onPartialImageError = jexception ? ImageDecoder::kSourceException
+ int onPartialImageError = jexception ? kSourceException
: 0; // No error.
switch (result) {
case SkCodec::kSuccess:
@@ -321,12 +337,12 @@
break;
case SkCodec::kIncompleteInput:
if (!jexception) {
- onPartialImageError = ImageDecoder::kSourceIncomplete;
+ onPartialImageError = kSourceIncomplete;
}
break;
case SkCodec::kErrorInInput:
if (!jexception) {
- onPartialImageError = ImageDecoder::kSourceMalformedData;
+ onPartialImageError = kSourceMalformedData;
}
break;
default:
@@ -350,20 +366,21 @@
// Ignore ninepatch when post-processing.
if (!jpostProcess) {
// FIXME: Share more code with BitmapFactory.cpp.
- if (decoder->mPeeker->mPatch != nullptr) {
- size_t ninePatchArraySize = decoder->mPeeker->mPatch->serializedSize();
+ auto* peeker = reinterpret_cast<NinePatchPeeker*>(decoder->mPeeker.get());
+ if (peeker->mPatch != nullptr) {
+ size_t ninePatchArraySize = peeker->mPatch->serializedSize();
ninePatchChunk = env->NewByteArray(ninePatchArraySize);
if (ninePatchChunk == nullptr) {
doThrowOOME(env, "Failed to allocate nine patch chunk.");
return nullptr;
}
- env->SetByteArrayRegion(ninePatchChunk, 0, decoder->mPeeker->mPatchSize,
- reinterpret_cast<jbyte*>(decoder->mPeeker->mPatch));
+ env->SetByteArrayRegion(ninePatchChunk, 0, peeker->mPatchSize,
+ reinterpret_cast<jbyte*>(peeker->mPatch));
}
- if (decoder->mPeeker->mHasInsets) {
- ninePatchInsets = decoder->mPeeker->createNinePatchInsets(env, 1.0f);
+ if (peeker->mHasInsets) {
+ ninePatchInsets = peeker->createNinePatchInsets(env, 1.0f);
if (ninePatchInsets == nullptr) {
doThrowOOME(env, "Failed to allocate nine patch insets.");
return nullptr;
@@ -371,58 +388,6 @@
}
}
- if (scale || jsubset) {
- int translateX = 0;
- int translateY = 0;
- SkImageInfo scaledInfo;
- if (jsubset) {
- SkIRect subset;
- GraphicsJNI::jrect_to_irect(env, jsubset, &subset);
-
- translateX = -subset.fLeft;
- translateY = -subset.fTop;
- scaledInfo = bitmapInfo.makeWH(subset.width(), subset.height());
- } else {
- scaledInfo = bitmapInfo.makeWH(desiredWidth, desiredHeight);
- }
- SkBitmap scaledBm;
- if (!scaledBm.setInfo(scaledInfo)) {
- doThrowIOE(env, "Failed scaled setInfo");
- return nullptr;
- }
-
- sk_sp<Bitmap> scaledPixelRef;
- if (allocator == ImageDecoder::kSharedMemory_Allocator) {
- scaledPixelRef = Bitmap::allocateAshmemBitmap(&scaledBm);
- } else {
- scaledPixelRef = Bitmap::allocateHeapBitmap(&scaledBm);
- }
- if (!scaledPixelRef) {
- SkString msg;
- msg.printf("OOM allocating scaled Bitmap with dimensions %i x %i",
- desiredWidth, desiredHeight);
- doThrowOOME(env, msg.c_str());
- return nullptr;
- }
-
- SkPaint paint;
- paint.setBlendMode(SkBlendMode::kSrc);
- paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering
-
- SkCanvas canvas(scaledBm, SkCanvas::ColorBehavior::kLegacy);
- canvas.translate(translateX, translateY);
- if (scale) {
- float scaleX = (float) desiredWidth / decodeInfo.width();
- float scaleY = (float) desiredHeight / decodeInfo.height();
- canvas.scale(scaleX, scaleY);
- }
-
- canvas.drawBitmap(bm, 0.0f, 0.0f, &paint);
-
- bm.swap(scaledBm);
- nativeBitmap = std::move(scaledPixelRef);
- }
-
if (jpostProcess) {
std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm));
@@ -433,12 +398,12 @@
SkAlphaType newAlphaType = bm.alphaType();
switch (pixelFormat) {
- case ImageDecoder::kUnknown:
+ case kUnknown:
break;
- case ImageDecoder::kTranslucent:
+ case kTranslucent:
newAlphaType = kPremul_SkAlphaType;
break;
- case ImageDecoder::kOpaque:
+ case kOpaque:
newAlphaType = kOpaque_SkAlphaType;
break;
default:
@@ -477,7 +442,7 @@
return bitmap::createBitmap(env, hwBitmap.release(), bitmapCreateFlags,
ninePatchChunk, ninePatchInsets);
}
- if (allocator == ImageDecoder::kHardware_Allocator) {
+ if (allocator == kHardware_Allocator) {
doThrowOOME(env, "failed to allocate hardware Bitmap!");
return nullptr;
}
@@ -501,7 +466,7 @@
static void ImageDecoder_nGetPadding(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
jobject outPadding) {
auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
- decoder->mPeeker->getPadding(env, outPadding);
+ reinterpret_cast<NinePatchPeeker*>(decoder->mPeeker.get())->getPadding(env, outPadding);
}
static void ImageDecoder_nClose(JNIEnv* /*env*/, jobject /*clazz*/, jlong nativePtr) {
diff --git a/core/jni/android/graphics/ImageDecoder.h b/core/jni/android/graphics/ImageDecoder.h
index fd9827b..8a7fa79 100644
--- a/core/jni/android/graphics/ImageDecoder.h
+++ b/core/jni/android/graphics/ImageDecoder.h
@@ -14,48 +14,12 @@
* limitations under the License.
*/
-#include "NinePatchPeeker.h"
-
#include <hwui/Canvas.h>
#include <jni.h>
-class SkAndroidCodec;
-
-using namespace android;
-
-struct ImageDecoder {
- // These need to stay in sync with ImageDecoder.java's Allocator constants.
- enum Allocator {
- kDefault_Allocator = 0,
- kSoftware_Allocator = 1,
- kSharedMemory_Allocator = 2,
- kHardware_Allocator = 3,
- };
-
- // These need to stay in sync with ImageDecoder.java's Error constants.
- enum Error {
- kSourceException = 1,
- kSourceIncomplete = 2,
- kSourceMalformedData = 3,
- };
-
- // These need to stay in sync with PixelFormat.java's Format constants.
- enum PixelFormat {
- kUnknown = 0,
- kTranslucent = -3,
- kOpaque = -1,
- };
-
- std::unique_ptr<SkAndroidCodec> mCodec;
- sk_sp<NinePatchPeeker> mPeeker;
-
- ImageDecoder()
- :mPeeker(new NinePatchPeeker)
- {}
-};
-
// Creates a Java Canvas object from canvas, calls jimageDecoder's PostProcess on it, and then
// releases the Canvas.
// Caller needs to check for exceptions.
-jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas);
+jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder,
+ std::unique_ptr<android::Canvas> canvas);
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index f28c422..c4ac89a 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -74,6 +74,7 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <bionic/malloc.h>
+#include <bionic/page.h>
#include <cutils/fs.h>
#include <cutils/multiuser.h>
#include <cutils/sockets.h>
@@ -1389,9 +1390,14 @@
void* data [[maybe_unused]]) {
// Search for any execute-only segments and mark them read+execute.
for (int i = 0; i < info->dlpi_phnum; i++) {
- if ((info->dlpi_phdr[i].p_type == PT_LOAD) && (info->dlpi_phdr[i].p_flags == PF_X)) {
- mprotect(reinterpret_cast<void*>(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr),
- info->dlpi_phdr[i].p_memsz, PROT_READ | PROT_EXEC);
+ const auto& phdr = info->dlpi_phdr[i];
+ if ((phdr.p_type == PT_LOAD) && (phdr.p_flags == PF_X)) {
+ auto addr = reinterpret_cast<void*>(info->dlpi_addr + PAGE_START(phdr.p_vaddr));
+ size_t len = PAGE_OFFSET(phdr.p_vaddr) + phdr.p_memsz;
+ if (mprotect(addr, len, PROT_READ | PROT_EXEC) == -1) {
+ ALOGE("mprotect(%p, %zu, PROT_READ | PROT_EXEC) failed: %m", addr, len);
+ return -1;
+ }
}
}
// Return non-zero to exit dl_iterate_phdr.
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 0c21076..bef0ee4 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -36,9 +36,11 @@
"/apex/com.android.conscrypt/javalib/conscrypt.jar",
"/apex/com.android.ipsec/javalib/ike.jar",
"/apex/com.android.media/javalib/updatable-media.jar",
+ "/apex/com.android.os.statsd/javalib/framework-statsd.jar",
"/apex/com.android.sdkext/javalib/framework-sdkext.jar",
"/apex/com.android.telephony/javalib/telephony-common.jar",
"/apex/com.android.telephony/javalib/ims-common.jar",
+ "/apex/com.android.wifi/javalib/framework-wifi.jar",
"/dev/null",
"/dev/socket/zygote",
"/dev/socket/zygote_secondary",
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index ab97fdd..0c2129f 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2467,4 +2467,9 @@
// CATEGORY: SETTINGS
// OS: R
ACCOUNT_WORK = 1807;
+
+ // OPEN: Settings > Developer Options > Bug report handler
+ // CATEGORY: SETTINGS
+ // OS: R
+ SETTINGS_BUGREPORT_HANDLER = 1808;
}
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index f8c304c..ee5144c 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -152,4 +152,5 @@
CROSS_PROFILE_APPS_GET_TARGET_USER_PROFILES = 125;
CROSS_PROFILE_APPS_START_ACTIVITY_AS_USER = 126;
SET_AUTO_TIME = 127;
+ SET_AUTO_TIME_ZONE = 128;
}
diff --git a/core/res/res/values-mcc510-mnc08/config.xml b/core/res/res/values-mcc510-mnc08/config.xml
index 7b27554..58fbb9e 100644
--- a/core/res/res/values-mcc510-mnc08/config.xml
+++ b/core/res/res/values-mcc510-mnc08/config.xml
@@ -23,4 +23,6 @@
and "333" is used for other purpose -->
<string-array translatable="false" name="config_callBarringMMI">
</string-array>
+ <string-array translatable="false" name="config_callBarringMMI_for_ims">
+ </string-array>
</resources>
diff --git a/core/res/res/values-mcc510-mnc89/config.xml b/core/res/res/values-mcc510-mnc89/config.xml
index 82efecf..c262247 100644
--- a/core/res/res/values-mcc510-mnc89/config.xml
+++ b/core/res/res/values-mcc510-mnc89/config.xml
@@ -23,4 +23,6 @@
and "333" is used for other purpose -->
<string-array translatable="false" name="config_callBarringMMI">
</string-array>
+ <string-array translatable="false" name="config_callBarringMMI_for_ims">
+ </string-array>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 89c913b..d6d5c57 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2544,6 +2544,28 @@
<string name="config_customAdbPublicKeyConfirmationSecondaryUserComponent"
>com.android.systemui/com.android.systemui.usb.UsbDebuggingSecondaryUserActivity</string>
+ <!-- Component name of the activity that shows the usb containment status. -->
+ <string name="config_usbContaminantActivity" translatable="false"
+ >com.android.systemui/com.android.systemui.usb.UsbContaminantActivity</string>
+
+ <!-- Component name of the activity that shows the request for access to a usb device. -->
+ <string name="config_usbPermissionActivity" translatable="false"
+ >com.android.systemui/com.android.systemui.usb.UsbPermissionActivity</string>
+
+ <!-- Component name of the activity that shows more information about a usb accessory. -->
+ <string name="config_usbAccessoryUriActivity" translatable="false"
+ >com.android.systemui/com.android.systemui.usb.UsbAccessoryUriActivity</string>
+
+ <!-- Component name of the activity that confirms the activity to start when a usb device is
+ plugged in. -->
+ <string name="config_usbConfirmActivity" translatable="false"
+ >com.android.systemui/com.android.systemui.usb.UsbConfirmActivity</string>
+
+ <!-- Component name of the activity to select the activity to start when a usb device is plugged
+ in. -->
+ <string name="config_usbResolverActivity" translatable="false"
+ >com.android.systemui/com.android.systemui.usb.UsbResolverActivity</string>
+
<!-- Name of the dialog that is used to request the user's consent to VPN connection -->
<string name="config_customVpnConfirmDialogComponent" translatable="false"
>com.android.vpndialogs/com.android.vpndialogs.ConfirmDialog</string>
@@ -2630,6 +2652,18 @@
<item>353</item>
</string-array>
+ <!-- Ims supported call barring MMI code -->
+ <string-array translatable="false" name="config_callBarringMMI_for_ims">
+ <item>33</item>
+ <item>331</item>
+ <item>332</item>
+ <item>35</item>
+ <item>351</item>
+ <item>330</item>
+ <item>333</item>
+ <item>353</item>
+ </string-array>
+
<!-- Override the default detection behavior for the framework method
android.view.ViewConfiguration#hasPermanentMenuKey().
Valid settings are:
@@ -2662,11 +2696,45 @@
property. If this is false, then the following recents config flags are ignored. -->
<bool name="config_hasRecents">true</bool>
- <!-- Component name for the activity that will be presenting the Recents UI, which will receive special permissions for API related
- to fetching and presenting recent tasks. The default configuration uses Launcehr3QuickStep as default launcher and points to
- the corresponding recents component. When using a different default launcher, change this appropriately or use the default
- systemui implementation: com.android.systemui/.recents.RecentsActivity -->
- <string name="config_recentsComponentName" translatable="false">com.android.launcher3/com.android.quickstep.RecentsActivity</string>
+ <!-- Component name for the activity that will be presenting the Recents UI, which will receive
+ special permissions for API related to fetching and presenting recent tasks. The default
+ configuration uses Launcehr3QuickStep as default launcher and points to the corresponding
+ recents component. When using a different default launcher, change this appropriately or
+ use the default systemui implementation: com.android.systemui/.recents.RecentsActivity -->
+ <string name="config_recentsComponentName" translatable="false"
+ >com.android.launcher3/com.android.quickstep.RecentsActivity</string>
+
+ <!-- SystemUi service component -->
+ <string name="config_systemUIServiceComponent" translatable="false"
+ >com.android.systemui/com.android.systemui.SystemUIService</string>
+
+ <!-- Keyguard component -->
+ <string name="config_keyguardComponent" translatable="false"
+ >com.android.systemui/com.android.systemui.keyguard.KeyguardService</string>
+
+ <!-- Screen record dialog component -->
+ <string name="config_screenRecorderComponent" translatable="false"
+ >com.android.systemui/com.android.systemui.screenrecord.ScreenRecordDialog</string>
+
+ <!-- The component name of a special dock app that merely launches a dream.
+ We don't want to launch this app when docked because it causes an unnecessary
+ activity transition. We just want to start the dream. -->
+ <string name="config_somnambulatorComponent" translatable="false"
+ >com.android.systemui/com.android.systemui.Somnambulator</string>
+
+ <!-- The component name of a special dock app that merely launches a dream.
+ We don't want to launch this app when docked because it causes an unnecessary
+ activity transition. We just want to start the dream.. -->
+ <string name="config_screenshotServiceComponent" translatable="false"
+ >com.android.systemui/com.android.systemui.screenshot.TakeScreenshotService</string>
+
+ <!-- The component notified when there is an error while taking a screenshot. -->
+ <string name="config_screenshotErrorReceiverComponent" translatable="false"
+ >com.android.systemui/com.android.systemui.screenshot.ScreenshotServiceErrorReceiver</string>
+
+ <!-- The component for the activity shown to grant permissions for a slice. -->
+ <string name="config_slicePermissionComponent" translatable="false"
+ >com.android.systemui/com.android.systemui.SlicePermissionActivity</string>
<!-- The minimum number of visible recent tasks to be presented to the user through the
SystemUI. Can be -1 if there is no minimum limit. -->
@@ -3020,9 +3088,6 @@
<!-- Specifies the maximum burn-in offset vertically. -->
<integer name="config_burnInProtectionMaxVerticalOffset">0</integer>
- <!-- Keyguard component -->
- <string name="config_keyguardComponent" translatable="false">com.android.systemui/com.android.systemui.keyguard.KeyguardService</string>
-
<!-- Limit for the number of face templates per user -->
<integer name="config_faceMaxTemplatesPerUser">1</integer>
@@ -4095,6 +4160,14 @@
<!-- Which binder services to include in incident reports containing restricted images. -->
<string-array name="config_restrictedImagesServices" translatable="false"/>
+ <!-- List of biometric sensors on the device, in decreasing strength. Consumed by AuthService
+ when registering authenticators with BiometricService. Format must be ID:Modality:Strength,
+ where: IDs are unique per device, Modality as defined in BiometricAuthenticator.java,
+ and Strength as defined in Authenticators.java -->
+ <string-array name="config_biometric_sensors" translatable="false" >
+ <item>0:2:15</item> <!-- ID0:Fingerprint:Strong -->
+ </string-array>
+
<!-- Messages that should not be shown to the user during face auth enrollment. This should be
used to hide messages that may be too chatty or messages that the user can't do much about.
Entries are defined in android.hardware.biometrics.face@1.0 types.hal -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6956c39..011dc39 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -360,6 +360,17 @@
<java-symbol type="bool" name="config_use16BitTaskSnapshotPixelFormat" />
<java-symbol type="bool" name="config_hasRecents" />
<java-symbol type="string" name="config_recentsComponentName" />
+ <java-symbol type="string" name="config_systemUIServiceComponent" />
+ <java-symbol type="string" name="config_screenRecorderComponent" />
+ <java-symbol type="string" name="config_somnambulatorComponent" />
+ <java-symbol type="string" name="config_screenshotServiceComponent" />
+ <java-symbol type="string" name="config_screenshotErrorReceiverComponent" />
+ <java-symbol type="string" name="config_slicePermissionComponent" />
+ <java-symbol type="string" name="config_usbContaminantActivity" />
+ <java-symbol type="string" name="config_usbPermissionActivity" />
+ <java-symbol type="string" name="config_usbAccessoryUriActivity" />
+ <java-symbol type="string" name="config_usbConfirmActivity" />
+ <java-symbol type="string" name="config_usbResolverActivity" />
<java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" />
<java-symbol type="integer" name="config_maxNumVisibleRecentTasks_lowRam" />
<java-symbol type="integer" name="config_minNumVisibleRecentTasks_grid" />
@@ -1231,6 +1242,7 @@
<java-symbol type="array" name="config_cdma_dun_supported_types" />
<java-symbol type="array" name="config_disabledUntilUsedPreinstalledImes" />
<java-symbol type="array" name="config_callBarringMMI" />
+ <java-symbol type="array" name="config_callBarringMMI_for_ims" />
<java-symbol type="array" name="config_globalActionsList" />
<java-symbol type="array" name="config_telephonyEuiccDeviceCapabilities" />
<java-symbol type="array" name="config_telephonyHardware" />
@@ -2471,6 +2483,8 @@
<java-symbol type="string" name="face_authenticated_no_confirmation_required" />
<java-symbol type="string" name="face_authenticated_confirmation_required" />
+ <java-symbol type="array" name="config_biometric_sensors" />
+
<java-symbol type="array" name="config_face_acquire_enroll_ignorelist" />
<java-symbol type="array" name="config_face_acquire_vendor_enroll_ignorelist" />
<java-symbol type="array" name="config_face_acquire_keyguard_ignorelist" />
diff --git a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
index e16d1ca..1472b90 100644
--- a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
+++ b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
@@ -24,8 +24,13 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import android.content.Context;
+import android.content.res.Resources;
import android.os.Handler;
import android.os.Looper;
@@ -54,9 +59,15 @@
// This raises a `SecurityException` if the device is locked. Calling either `Context`
// method results in a broadcast of `android.intent.action. USER_PRESENT`. Only the system
// process is allowed to broadcast that `Intent`.
+ Resources res = mock(Resources.class);
mContext = Mockito.spy(Context.class);
- Mockito.doNothing().when(mContext).sendBroadcastAsUser(any(), any());
- Mockito.doReturn(true).when(mContext).bindServiceAsUser(any(), any(), anyInt(), any());
+ doNothing().when(mContext).sendBroadcastAsUser(any(), any());
+ doReturn(true).when(mContext).bindServiceAsUser(any(), any(), anyInt(), any());
+ doReturn(res).when(mContext).getResources();
+ doReturn("com.android.systemui/.Service").when(res).getString(
+ eq(com.android.internal.R.string.config_screenshotServiceComponent));
+ doReturn("com.android.systemui/.ErrorReceiver").when(res).getString(
+ eq(com.android.internal.R.string.config_screenshotErrorReceiverComponent));
mHandler = new Handler(Looper.getMainLooper());
mScreenshotHelper = new ScreenshotHelper(mContext);
diff --git a/framework-jarjar-rules.txt b/framework-jarjar-rules.txt
new file mode 100644
index 0000000..d8af726
--- /dev/null
+++ b/framework-jarjar-rules.txt
@@ -0,0 +1,2 @@
+rule android.hidl.** android.internal.hidl.@1
+rule android.net.wifi.WifiAnnotations* android.internal.wifi.WifiAnnotations@1
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index 2648008..5ad93f4 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -17,6 +17,7 @@
package android.graphics;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
import android.graphics.fonts.FontVariationAxis;
import android.text.TextUtils;
@@ -57,6 +58,7 @@
*
* This cannot be deleted because it's in use by AndroidX.
*/
+ @UnsupportedAppUsage(trackingBug = 123768928)
public long mNativePtr;
// Points native font family builder. Must be zero after freezing this family.
@@ -65,6 +67,7 @@
/**
* This cannot be deleted because it's in use by AndroidX.
*/
+ @UnsupportedAppUsage(trackingBug = 123768928)
public FontFamily() {
mBuilderPtr = nInitBuilder(null, 0);
mNativeBuilderCleaner = sBuilderRegistry.registerNativeAllocation(this, mBuilderPtr);
@@ -73,6 +76,7 @@
/**
* This cannot be deleted because it's in use by AndroidX.
*/
+ @UnsupportedAppUsage(trackingBug = 123768928)
public FontFamily(@Nullable String[] langs, int variant) {
final String langsString;
if (langs == null || langs.length == 0) {
@@ -94,6 +98,7 @@
*
* This cannot be deleted because it's in use by AndroidX.
*/
+ @UnsupportedAppUsage(trackingBug = 123768928)
public boolean freeze() {
if (mBuilderPtr == 0) {
throw new IllegalStateException("This FontFamily is already frozen");
@@ -110,6 +115,7 @@
/**
* This cannot be deleted because it's in use by AndroidX.
*/
+ @UnsupportedAppUsage(trackingBug = 123768928)
public void abortCreation() {
if (mBuilderPtr == 0) {
throw new IllegalStateException("This FontFamily is already frozen or abandoned");
@@ -121,6 +127,7 @@
/**
* This cannot be deleted because it's in use by AndroidX.
*/
+ @UnsupportedAppUsage(trackingBug = 123768928)
public boolean addFont(String path, int ttcIndex, FontVariationAxis[] axes, int weight,
int italic) {
if (mBuilderPtr == 0) {
@@ -144,6 +151,7 @@
/**
* This cannot be deleted because it's in use by AndroidX.
*/
+ @UnsupportedAppUsage(trackingBug = 123768928)
public boolean addFontFromBuffer(ByteBuffer font, int ttcIndex, FontVariationAxis[] axes,
int weight, int italic) {
if (mBuilderPtr == 0) {
@@ -171,6 +179,7 @@
*
* This cannot be deleted because it's in use by AndroidX.
*/
+ @UnsupportedAppUsage(trackingBug = 123768928)
public boolean addFontFromAssetManager(AssetManager mgr, String path, int cookie,
boolean isAsset, int ttcIndex, int weight, int isItalic,
FontVariationAxis[] axes) {
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 45b2de5..c6586ec 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -784,9 +784,7 @@
mFillPaint.setDither(st.mDither);
mFillPaint.setColorFilter(colorFilter);
if (colorFilter != null && st.mSolidColors == null) {
- // If we don't have a solid color and we don't have a gradient,
- // the app is stroking the shape, set the color to transparent
- mFillPaint.setColor(st.mGradientColors != null ? mAlpha << 24 : 0);
+ mFillPaint.setColor(mAlpha << 24);
}
if (haveStroke) {
mStrokePaint.setAlpha(currStrokeAlpha);
diff --git a/jarjar_rules_hidl.txt b/jarjar_rules_hidl.txt
deleted file mode 100644
index 4b2331d..0000000
--- a/jarjar_rules_hidl.txt
+++ /dev/null
@@ -1 +0,0 @@
-rule android.hidl.** android.internal.hidl.@1
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index f670cf9..d945fc4 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -176,6 +176,7 @@
"hwui/AnimatedImageThread.cpp",
"hwui/Bitmap.cpp",
"hwui/Canvas.cpp",
+ "hwui/ImageDecoder.cpp",
"hwui/MinikinSkia.cpp",
"hwui/MinikinUtils.cpp",
"hwui/PaintImpl.cpp",
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 2ba6fbe..f4149b9 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -44,9 +44,7 @@
namespace android {
-// returns true if rowBytes * height can be represented by a positive int32_t value
-// and places that value in size.
-static bool computeAllocationSize(size_t rowBytes, int height, size_t* size) {
+bool Bitmap::computeAllocationSize(size_t rowBytes, int height, size_t* size) {
return 0 <= height && height <= std::numeric_limits<size_t>::max() &&
!__builtin_mul_overflow(rowBytes, (size_t)height, size) &&
*size <= std::numeric_limits<int32_t>::max();
@@ -66,7 +64,7 @@
// we must respect the rowBytes value already set on the bitmap instead of
// attempting to compute our own.
const size_t rowBytes = bitmap->rowBytes();
- if (!computeAllocationSize(rowBytes, bitmap->height(), &size)) {
+ if (!Bitmap::computeAllocationSize(rowBytes, bitmap->height(), &size)) {
return nullptr;
}
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 00733c6..1cda046 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -138,6 +138,10 @@
return mPalette;
}
+ // returns true if rowBytes * height can be represented by a positive int32_t value
+ // and places that value in size.
+ static bool computeAllocationSize(size_t rowBytes, int height, size_t* size);
+
private:
static sk_sp<Bitmap> allocateAshmemBitmap(size_t size, const SkImageInfo& i, size_t rowBytes);
static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& i, size_t rowBytes);
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
new file mode 100644
index 0000000..4f2027d
--- /dev/null
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ImageDecoder.h"
+
+#include <hwui/Bitmap.h>
+
+#include <SkAndroidCodec.h>
+#include <SkCanvas.h>
+#include <SkPaint.h>
+
+using namespace android;
+
+ImageDecoder::ImageDecoder(std::unique_ptr<SkAndroidCodec> codec, sk_sp<SkPngChunkReader> peeker)
+ : mCodec(std::move(codec))
+ , mPeeker(std::move(peeker))
+ , mTargetSize(mCodec->getInfo().dimensions())
+ , mDecodeSize(mTargetSize)
+ , mOutColorType(mCodec->computeOutputColorType(kN32_SkColorType))
+ , mOutAlphaType(mCodec->getInfo().isOpaque() ?
+ kOpaque_SkAlphaType : kPremul_SkAlphaType)
+ , mOutColorSpace(mCodec->getInfo().refColorSpace())
+ , mSampleSize(1)
+{
+}
+
+bool ImageDecoder::setTargetSize(int width, int height) {
+ if (width <= 0 || height <= 0) {
+ return false;
+ }
+
+ auto info = SkImageInfo::Make(width, height, mOutColorType, mOutAlphaType);
+ size_t rowBytes = info.minRowBytes();
+ if (rowBytes == 0) {
+ // This would have overflowed.
+ return false;
+ }
+
+ size_t pixelMemorySize;
+ if (!Bitmap::computeAllocationSize(rowBytes, height, &pixelMemorySize)) {
+ return false;
+ }
+
+ if (mCropRect) {
+ if (mCropRect->right() > width || mCropRect->bottom() > height) {
+ return false;
+ }
+ }
+
+ SkISize targetSize = { width, height }, decodeSize = targetSize;
+ int sampleSize = mCodec->computeSampleSize(&decodeSize);
+
+ if (decodeSize != targetSize && mOutAlphaType == kUnpremul_SkAlphaType
+ && !mCodec->getInfo().isOpaque()) {
+ return false;
+ }
+
+ mTargetSize = targetSize;
+ mDecodeSize = decodeSize;
+ mSampleSize = sampleSize;
+ return true;
+}
+
+bool ImageDecoder::setCropRect(const SkIRect* crop) {
+ if (!crop) {
+ mCropRect.reset();
+ return true;
+ }
+
+ if (crop->left() >= crop->right() || crop->top() >= crop->bottom()) {
+ return false;
+ }
+
+ const auto& size = mTargetSize;
+ if (crop->left() < 0 || crop->top() < 0
+ || crop->right() > size.width() || crop->bottom() > size.height()) {
+ return false;
+ }
+
+ mCropRect.emplace(*crop);
+ return true;
+}
+
+bool ImageDecoder::setOutColorType(SkColorType colorType) {
+ switch (colorType) {
+ case kRGB_565_SkColorType:
+ if (!opaque()) {
+ return false;
+ }
+ break;
+ case kGray_8_SkColorType:
+ if (!gray()) {
+ return false;
+ }
+ mOutColorSpace = nullptr;
+ break;
+ case kN32_SkColorType:
+ break;
+ case kRGBA_F16_SkColorType:
+ break;
+ default:
+ return false;
+ }
+
+ mOutColorType = colorType;
+ return true;
+}
+
+bool ImageDecoder::setOutAlphaType(SkAlphaType alpha) {
+ switch (alpha) {
+ case kOpaque_SkAlphaType:
+ return opaque();
+ case kPremul_SkAlphaType:
+ if (opaque()) {
+ // Opaque can be treated as premul.
+ return true;
+ }
+ break;
+ case kUnpremul_SkAlphaType:
+ if (opaque()) {
+ // Opaque can be treated as unpremul.
+ return true;
+ }
+ if (mDecodeSize != mTargetSize) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ mOutAlphaType = alpha;
+ return true;
+}
+
+void ImageDecoder::setOutColorSpace(sk_sp<SkColorSpace> colorSpace) {
+ mOutColorSpace = std::move(colorSpace);
+}
+
+SkImageInfo ImageDecoder::getOutputInfo() const {
+ SkISize size = mCropRect ? mCropRect->size() : mTargetSize;
+ return SkImageInfo::Make(size, mOutColorType, mOutAlphaType, mOutColorSpace);
+}
+
+bool ImageDecoder::opaque() const {
+ return mOutAlphaType == kOpaque_SkAlphaType;
+}
+
+bool ImageDecoder::gray() const {
+ return mCodec->getInfo().colorType() == kGray_8_SkColorType;
+}
+
+SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) {
+ void* decodePixels = pixels;
+ size_t decodeRowBytes = rowBytes;
+ auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, mOutAlphaType, mOutColorSpace);
+ // Used if we need a temporary before scaling or subsetting.
+ // FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380.
+ SkBitmap tmp;
+ const bool scale = mDecodeSize != mTargetSize;
+ if (scale || mCropRect) {
+ if (!tmp.setInfo(decodeInfo)) {
+ return SkCodec::kInternalError;
+ }
+ if (!Bitmap::allocateHeapBitmap(&tmp)) {
+ return SkCodec::kInternalError;
+ }
+ decodePixels = tmp.getPixels();
+ decodeRowBytes = tmp.rowBytes();
+ }
+
+ SkAndroidCodec::AndroidOptions options;
+ options.fSampleSize = mSampleSize;
+ auto result = mCodec->getAndroidPixels(decodeInfo, decodePixels, decodeRowBytes, &options);
+
+ if (scale || mCropRect) {
+ SkBitmap scaledBm;
+ if (!scaledBm.installPixels(getOutputInfo(), pixels, rowBytes)) {
+ return SkCodec::kInternalError;
+ }
+
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kSrc);
+ paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering
+
+ SkCanvas canvas(scaledBm, SkCanvas::ColorBehavior::kLegacy);
+ if (mCropRect) {
+ canvas.translate(-mCropRect->fLeft, -mCropRect->fTop);
+ }
+ if (scale) {
+ float scaleX = (float) mTargetSize.width() / mDecodeSize.width();
+ float scaleY = (float) mTargetSize.height() / mDecodeSize.height();
+ canvas.scale(scaleX, scaleY);
+ }
+
+ canvas.drawBitmap(tmp, 0.0f, 0.0f, &paint);
+ }
+
+ return result;
+}
+
diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h
new file mode 100644
index 0000000..b956f4a
--- /dev/null
+++ b/libs/hwui/hwui/ImageDecoder.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <SkCodec.h>
+#include <SkImageInfo.h>
+#include <SkPngChunkReader.h>
+#include <SkRect.h>
+#include <SkSize.h>
+#include <cutils/compiler.h>
+
+#include <optional>
+
+class SkAndroidCodec;
+
+namespace android {
+
+class ANDROID_API ImageDecoder {
+public:
+ std::unique_ptr<SkAndroidCodec> mCodec;
+ sk_sp<SkPngChunkReader> mPeeker;
+
+ ImageDecoder(std::unique_ptr<SkAndroidCodec> codec,
+ sk_sp<SkPngChunkReader> peeker = nullptr);
+
+ bool setTargetSize(int width, int height);
+ bool setCropRect(const SkIRect*);
+
+ bool setOutColorType(SkColorType outColorType);
+
+ bool setOutAlphaType(SkAlphaType outAlphaType);
+
+ void setOutColorSpace(sk_sp<SkColorSpace> cs);
+
+ // The size is the final size after scaling and cropping.
+ SkImageInfo getOutputInfo() const;
+ SkColorType getOutColorType() const { return mOutColorType; }
+ SkAlphaType getOutAlphaType() const { return mOutAlphaType; }
+
+ bool opaque() const;
+ bool gray() const;
+
+ SkCodec::Result decode(void* pixels, size_t rowBytes);
+
+private:
+ SkISize mTargetSize;
+ SkISize mDecodeSize;
+ SkColorType mOutColorType;
+ SkAlphaType mOutAlphaType;
+ sk_sp<SkColorSpace> mOutColorSpace;
+ int mSampleSize;
+ std::optional<SkIRect> mCropRect;
+
+ ImageDecoder(const ImageDecoder&) = delete;
+ ImageDecoder& operator=(const ImageDecoder&) = delete;
+};
+
+} // namespace android
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index df799fd..33ec46a 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -903,6 +903,18 @@
}
}
+ /**
+ * Returns a human readable name for a given device type
+ * @param device a native device type, NOT an AudioDeviceInfo type
+ * @return a string describing the device type
+ */
+ public static @NonNull String getDeviceName(int device) {
+ if ((device & DEVICE_BIT_IN) != 0) {
+ return getInputDeviceName(device);
+ }
+ return getOutputDeviceName(device);
+ }
+
// phone state, match audio_mode???
public static final int PHONE_STATE_OFFCALL = 0;
public static final int PHONE_STATE_RINGING = 1;
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index 291cdd5..3d74868 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -131,10 +131,7 @@
private void showDialogForInitialUser() {
int initialUser = mCarUserManagerHelper.getInitialUser();
UserInfo initialUserInfo = mUserManager.getUserInfo(initialUser);
- mSelectedUser = new UserRecord(initialUserInfo,
- /* isStartGuestSession= */ false,
- /* isAddUser= */ false,
- /* isForeground= */ true);
+ mSelectedUser = new UserRecord(initialUserInfo, UserRecord.FOREGROUND_USER);
// If the initial user has screen lock and trusted device, display the unlock dialog on the
// keyguard.
@@ -180,12 +177,14 @@
*/
private void onUserSelected(UserGridRecyclerView.UserRecord record) {
mSelectedUser = record;
- if (hasScreenLock(record.mInfo.id) && hasTrustedDevice(record.mInfo.id)) {
- mUnlockDialogHelper.showUnlockDialog(record.mInfo.id, mOnHideListener);
- return;
- }
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "no trusted device enrolled for uid: " + record.mInfo.id);
+ if (record.mInfo != null) {
+ if (hasScreenLock(record.mInfo.id) && hasTrustedDevice(record.mInfo.id)) {
+ mUnlockDialogHelper.showUnlockDialog(record.mInfo.id, mOnHideListener);
+ return;
+ }
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "no trusted device enrolled for uid: " + record.mInfo.id);
+ }
}
dismissUserSwitcher();
}
@@ -195,7 +194,7 @@
Log.e(TAG, "Request to dismiss user switcher, but no user selected");
return;
}
- if (mSelectedUser.mIsForeground) {
+ if (mSelectedUser.mType == UserRecord.FOREGROUND_USER) {
hide();
mStatusBar.dismissKeyguard();
return;
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index 0a5f80f..cdabeeb 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -21,6 +21,7 @@
import static android.os.UserManager.DISALLOW_ADD_USER;
import static android.os.UserManager.SWITCHABILITY_STATUS_OK;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AlertDialog;
@@ -54,6 +55,8 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@@ -142,8 +145,8 @@
}
boolean isForeground = fgUserId == userInfo.id;
- UserRecord record = new UserRecord(userInfo, false /* isStartGuestSession */,
- false /* isAddUser */, isForeground);
+ UserRecord record = new UserRecord(userInfo,
+ isForeground ? UserRecord.FOREGROUND_USER : UserRecord.BACKGROUND_USER);
userRecords.add(record);
}
@@ -160,27 +163,21 @@
private UserRecord createForegroundUserRecord() {
return new UserRecord(mUserManager.getUserInfo(ActivityManager.getCurrentUser()),
- false /* isStartGuestSession */, false /* isAddUser */, true /* isForeground */);
+ UserRecord.FOREGROUND_USER);
}
/**
* Create guest user record
*/
private UserRecord createStartGuestUserRecord() {
- UserInfo userInfo = new UserInfo();
- userInfo.name = mContext.getString(R.string.start_guest_session);
- return new UserRecord(userInfo, true /* isStartGuestSession */, false /* isAddUser */,
- false /* isForeground */);
+ return new UserRecord(null /* userInfo */, UserRecord.START_GUEST);
}
/**
* Create add user record
*/
private UserRecord createAddUserRecord() {
- UserInfo userInfo = new UserInfo();
- userInfo.name = mContext.getString(R.string.car_add_user);
- return new UserRecord(userInfo, false /* isStartGuestSession */,
- true /* isAddUser */, false /* isForeground */);
+ return new UserRecord(null /* userInfo */, UserRecord.ADD_USER);
}
public void setUserSelectionListener(UserSelectionListener userSelectionListener) {
@@ -258,35 +255,36 @@
UserRecord userRecord = mUsers.get(position);
RoundedBitmapDrawable circleIcon = getCircularUserRecordIcon(userRecord);
holder.mUserAvatarImageView.setImageDrawable(circleIcon);
- holder.mUserNameTextView.setText(userRecord.mInfo.name);
+ holder.mUserNameTextView.setText(getUserRecordName(userRecord));
holder.mView.setOnClickListener(v -> {
if (userRecord == null) {
return;
}
- if (userRecord.mIsStartGuestSession) {
- notifyUserSelected(userRecord);
- UserInfo guest = createNewOrFindExistingGuest(mContext);
- if (guest != null) {
- mCarUserManagerHelper.switchToUser(guest);
- }
- return;
- }
+ switch (userRecord.mType) {
+ case UserRecord.START_GUEST:
+ notifyUserSelected(userRecord);
+ UserInfo guest = createNewOrFindExistingGuest(mContext);
+ if (guest != null) {
+ mCarUserManagerHelper.switchToUser(guest);
+ }
+ break;
+ case UserRecord.ADD_USER:
+ // If the user wants to add a user, show dialog to confirm adding a user
+ // Disable button so it cannot be clicked multiple times
+ mAddUserView = holder.mView;
+ mAddUserView.setEnabled(false);
+ mAddUserRecord = userRecord;
- // If the user wants to add a user, show dialog to confirm adding a user
- if (userRecord.mIsAddUser) {
- // Disable button so it cannot be clicked multiple times
- mAddUserView = holder.mView;
- mAddUserView.setEnabled(false);
- mAddUserRecord = userRecord;
-
- handleAddUserClicked();
- return;
+ handleAddUserClicked();
+ break;
+ default:
+ // If the user doesn't want to be a guest or add a user, switch to the user
+ // selected
+ notifyUserSelected(userRecord);
+ mCarUserManagerHelper.switchToUser(userRecord.mInfo);
}
- // If the user doesn't want to be a guest or add a user, switch to the user selected
- notifyUserSelected(userRecord);
- mCarUserManagerHelper.switchToUser(userRecord.mInfo);
});
}
@@ -372,19 +370,44 @@
private RoundedBitmapDrawable getCircularUserRecordIcon(UserRecord userRecord) {
Resources resources = mContext.getResources();
RoundedBitmapDrawable circleIcon;
- if (userRecord.mIsStartGuestSession) {
- circleIcon = mUserIconProvider.getRoundedGuestDefaultIcon(resources);
- } else if (userRecord.mIsAddUser) {
- circleIcon = RoundedBitmapDrawableFactory.create(mRes, UserIcons.convertToBitmap(
- mContext.getDrawable(R.drawable.car_add_circle_round)));
- circleIcon.setCircular(true);
- } else {
- circleIcon = mUserIconProvider.getRoundedUserIcon(userRecord.mInfo, mContext);
+ switch (userRecord.mType) {
+ case UserRecord.START_GUEST:
+ circleIcon = mUserIconProvider.getRoundedGuestDefaultIcon(resources);
+ break;
+ case UserRecord.ADD_USER:
+ circleIcon = getCircularAddUserIcon();
+ break;
+ default:
+ circleIcon = mUserIconProvider.getRoundedUserIcon(userRecord.mInfo, mContext);
+ break;
}
-
return circleIcon;
}
+ private RoundedBitmapDrawable getCircularAddUserIcon() {
+ RoundedBitmapDrawable circleIcon =
+ RoundedBitmapDrawableFactory.create(mRes, UserIcons.convertToBitmap(
+ mContext.getDrawable(R.drawable.car_add_circle_round)));
+ circleIcon.setCircular(true);
+ return circleIcon;
+ }
+
+ private String getUserRecordName(UserRecord userRecord) {
+ String recordName;
+ switch (userRecord.mType) {
+ case UserRecord.START_GUEST:
+ recordName = mContext.getString(R.string.start_guest_session);
+ break;
+ case UserRecord.ADD_USER:
+ recordName = mContext.getString(R.string.car_add_user);
+ break;
+ default:
+ recordName = userRecord.mInfo.name;
+ break;
+ }
+ return recordName;
+ }
+
/**
* Finds the existing Guest user, or creates one if it doesn't exist.
* @param context App context
@@ -468,18 +491,21 @@
* guest profile, add user profile, or the foreground user.
*/
public static final class UserRecord {
-
public final UserInfo mInfo;
- public final boolean mIsStartGuestSession;
- public final boolean mIsAddUser;
- public final boolean mIsForeground;
+ public final @UserRecordType int mType;
- public UserRecord(UserInfo userInfo, boolean isStartGuestSession, boolean isAddUser,
- boolean isForeground) {
+ public static final int START_GUEST = 0;
+ public static final int ADD_USER = 1;
+ public static final int FOREGROUND_USER = 2;
+ public static final int BACKGROUND_USER = 3;
+
+ @IntDef({START_GUEST, ADD_USER, FOREGROUND_USER, BACKGROUND_USER})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UserRecordType{}
+
+ public UserRecord(@Nullable UserInfo userInfo, @UserRecordType int recordType) {
mInfo = userInfo;
- mIsStartGuestSession = isStartGuestSession;
- mIsAddUser = isAddUser;
- mIsForeground = isForeground;
+ mType = recordType;
}
}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index ade292f..f40105d 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -598,6 +598,8 @@
<string name="bluetooth_show_devices_without_names">Show Bluetooth devices without names</string>
<!-- Setting Checkbox title for disabling Bluetooth absolute volume -->
<string name="bluetooth_disable_absolute_volume">Disable absolute volume</string>
+ <!-- Setting Checkbox title for enabling Bluetooth Gabeldorsche. [CHAR LIMIT=40] -->
+ <string name="bluetooth_enable_gabeldorsche">Enable Gabeldorsche</string>
<!-- UI debug setting: Select Bluetooth AVRCP Version -->
<string name="bluetooth_select_avrcp_version_string">Bluetooth AVRCP Version</string>
@@ -696,6 +698,8 @@
<string name="bluetooth_show_devices_without_names_summary">Bluetooth devices without names (MAC addresses only) will be displayed</string>
<!-- Summary of checkbox for disabling Bluetooth absolute volume -->
<string name="bluetooth_disable_absolute_volume_summary">Disables the Bluetooth absolute volume feature in case of volume issues with remote devices such as unacceptably loud volume or lack of control.</string>
+ <!-- Summary of checkbox for enabling Bluetooth Gabeldorche features [CHAR LIMIT=none] -->
+ <string name="bluetooth_enable_gabeldorsche_summary">Enables the Bluetooth Gabeldorche feature stack.</string>
<!-- Title of checkbox setting that enables the terminal app. [CHAR LIMIT=32] -->
<string name="enable_terminal_title">Local terminal</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
index d91226e..3f920a8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -16,6 +16,7 @@
package com.android.settingslib.bluetooth;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothPbap;
@@ -52,14 +53,16 @@
// These callbacks run on the main thread.
private final class PbapServiceListener
- implements BluetoothPbap.ServiceListener {
+ implements BluetoothProfile.ServiceListener {
- public void onServiceConnected(BluetoothPbap proxy) {
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
mService = (BluetoothPbap) proxy;
mIsProfileReady=true;
}
- public void onServiceDisconnected() {
+ @Override
+ public void onServiceDisconnected(int profile) {
mIsProfileReady=false;
}
}
@@ -74,7 +77,8 @@
}
PbapServerProfile(Context context) {
- BluetoothPbap pbap = new BluetoothPbap(context, new PbapServiceListener());
+ BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, new PbapServiceListener(),
+ BluetoothProfile.PBAP);
}
public boolean accessProfileEnabled() {
@@ -97,13 +101,8 @@
}
public int getConnectionStatus(BluetoothDevice device) {
- if (mService == null) {
- return BluetoothProfile.STATE_DISCONNECTED;
- }
- if (mService.isConnected(device))
- return BluetoothProfile.STATE_CONNECTED;
- else
- return BluetoothProfile.STATE_DISCONNECTED;
+ if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
+ return mService.getConnectionState(device);
}
public boolean isPreferred(BluetoothDevice device) {
@@ -142,7 +141,8 @@
Log.d(TAG, "finalize()");
if (mService != null) {
try {
- mService.close();
+ BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.PBAP,
+ mService);
mService = null;
}catch (Throwable t) {
Log.w(TAG, "Error cleaning up PBAP proxy", t);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
index 22f47f1..440315f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
@@ -152,6 +152,14 @@
// TODO(b/70983952): Fill this method in
}
+ /**
+ * Result of the sign-in request indecated by the WifiEntry.SIGNIN_STATUS constants
+ */
+ public void onSignInResult(int status) {
+ // TODO(b/70983952): Fill this method in
+ }
+
+
private void updateIcon(int level) {
if (level == -1) {
setIcon(null);
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 21b3ba3..c9e1944b 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -224,7 +224,7 @@
<bool name="def_charging_sounds_enabled">true</bool>
<!-- Default for Settings.Secure.NOTIFICATION_BUBBLES -->
- <bool name="def_notification_bubbles">false</bool>
+ <bool name="def_notification_bubbles">true</bool>
<!-- Default for Settings.Secure.AWARE_ENABLED -->
<bool name="def_aware_enabled">false</bool>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index 1f68742..987e82e 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -72,5 +72,6 @@
Settings.Global.NOTIFICATION_BUBBLES,
Settings.Global.CUSTOM_BUGREPORT_HANDLER_APP,
Settings.Global.CUSTOM_BUGREPORT_HANDLER_USER,
+ Settings.Global.DEVELOPMENT_SETTINGS_ENABLED
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 3b929b9..76e7ab4 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -155,6 +155,7 @@
Settings.Secure.GLOBAL_ACTIONS_PANEL_ENABLED,
Settings.Secure.AWARE_LOCK_ENABLED,
Settings.Secure.AWARE_TAP_PAUSE_GESTURE_COUNT,
- Settings.Secure.AWARE_TAP_PAUSE_TOUCH_COUNT
+ Settings.Secure.AWARE_TAP_PAUSE_TOUCH_COUNT,
+ Settings.Secure.PEOPLE_STRIP,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 3d278db..72923a3 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -151,5 +151,6 @@
VALIDATORS.put(Global.NOTIFICATION_BUBBLES, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.CUSTOM_BUGREPORT_HANDLER_APP, ANY_STRING_VALIDATOR);
VALIDATORS.put(Global.CUSTOM_BUGREPORT_HANDLER_USER, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.DEVELOPMENT_SETTINGS_ENABLED, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 090af98..ae07598 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -234,5 +234,6 @@
VALIDATORS.put(Secure.AWARE_LOCK_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DISPLAY_DENSITY_FORCED, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.TAP_GESTURE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.PEOPLE_STRIP, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 93a1407..4309c80 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3264,7 +3264,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 184;
+ private static final int SETTINGS_VERSION = 185;
private final int mUserId;
@@ -4446,20 +4446,15 @@
}
if (currentVersion == 182) {
- // Remove secure bubble settings.
+ // Remove secure bubble settings; it's in global now.
getSecureSettingsLocked(userId).deleteSettingLocked("notification_bubbles");
- // Add global bubble settings.
- getGlobalSettingsLocked().insertSettingLocked(Global.NOTIFICATION_BUBBLES,
- getContext().getResources().getBoolean(
- R.bool.def_notification_bubbles) ? "1" : "0", null /* tag */,
- true /* makeDefault */, SettingsState.SYSTEM_PACKAGE_NAME);
-
+ // Removed. Updated NOTIFICATION_BUBBLES to be true by default, see 184.
currentVersion = 183;
}
if (currentVersion == 183) {
- // Version 184: Set default values for WIRELESS_CHARGING_STARTED_SOUND
+ // Version 183: Set default values for WIRELESS_CHARGING_STARTED_SOUND
// and CHARGING_STARTED_SOUND
final SettingsState globalSettings = getGlobalSettingsLocked();
@@ -4500,6 +4495,18 @@
currentVersion = 184;
}
+ if (currentVersion == 184) {
+ // Version 184: Reset the default for Global Settings: NOTIFICATION_BUBBLES
+ // This is originally set in version 182, however, the default value changed
+ // so this step is to ensure the value is updated to the correct default.
+ getGlobalSettingsLocked().insertSettingLocked(Global.NOTIFICATION_BUBBLES,
+ getContext().getResources().getBoolean(
+ R.bool.def_notification_bubbles) ? "1" : "0", null /* tag */,
+ true /* makeDefault */, SettingsState.SYSTEM_PACKAGE_NAME);
+
+ currentVersion = 185;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index d5a3254..4a10e85 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -223,7 +223,6 @@
Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES,
Settings.Global.DEVELOPMENT_FORCE_RTL,
Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM,
- Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,
Settings.Global.DEVICE_DEMO_MODE,
Settings.Global.DEVICE_IDLE_CONSTANTS,
Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 3514704..a8318d6 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -28,11 +28,6 @@
android:glEsVersion="0x00020000"
android:required="true" />
- <!-- SysUI must be the one to define this permission; its name is
- referenced by the core OS. -->
- <permission android:name="android.permission.systemui.IDENTITY"
- android:protectionLevel="signature" />
-
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- Used to read wallpaper -->
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockInfo.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockInfo.java
index 812f215..0210e08 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockInfo.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockInfo.java
@@ -25,12 +25,12 @@
final class ClockInfo {
private final String mName;
- private final String mTitle;
+ private final Supplier<String> mTitle;
private final String mId;
private final Supplier<Bitmap> mThumbnail;
private final Supplier<Bitmap> mPreview;
- private ClockInfo(String name, String title, String id,
+ private ClockInfo(String name, Supplier<String> title, String id,
Supplier<Bitmap> thumbnail, Supplier<Bitmap> preview) {
mName = name;
mTitle = title;
@@ -50,7 +50,7 @@
* Gets the name (title) of the clock face to be shown in the picker app.
*/
String getTitle() {
- return mTitle;
+ return mTitle.get();
}
/**
@@ -80,7 +80,7 @@
static class Builder {
private String mName;
- private String mTitle;
+ private Supplier<String> mTitle;
private String mId;
private Supplier<Bitmap> mThumbnail;
private Supplier<Bitmap> mPreview;
@@ -94,7 +94,7 @@
return this;
}
- public Builder setTitle(String title) {
+ public Builder setTitle(Supplier<String> title) {
mTitle = title;
return this;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index dfabe69..9cd4aec 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -244,11 +244,12 @@
mPreviewClocks.reloadCurrentClock();
mListeners.forEach((listener, clocks) -> {
clocks.reloadCurrentClock();
- ClockPlugin clock = clocks.getCurrentClock();
- if (clock instanceof DefaultClockController) {
- listener.onClockChanged(null);
+ final ClockPlugin clock = clocks.getCurrentClock();
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ listener.onClockChanged(clock instanceof DefaultClockController ? null : clock);
} else {
- listener.onClockChanged(clock);
+ mMainHandler.post(() -> listener.onClockChanged(
+ clock instanceof DefaultClockController ? null : clock));
}
});
}
@@ -323,7 +324,7 @@
mClocks.put(plugin.getClass().getName(), plugin);
mClockInfo.add(ClockInfo.builder()
.setName(plugin.getName())
- .setTitle(plugin.getTitle())
+ .setTitle(plugin::getTitle)
.setId(id)
.setThumbnail(plugin::getThumbnail)
.setPreview(() -> plugin.getPreview(mWidth, mHeight))
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 6b0d3c8..e0ca1ac 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -18,6 +18,7 @@
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+import static android.hardware.biometrics.BiometricManager.Authenticators;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -29,7 +30,6 @@
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
-import android.hardware.biometrics.Authenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricServiceReceiverInternal;
@@ -341,6 +341,10 @@
if (DEBUG) Log.d(TAG, "hideAuthenticationDialog");
mCurrentDialog.dismissFromSystemServer();
+
+ // BiometricService will have already sent the callback to the client in this case.
+ // This avoids a round trip to SystemUI. So, just dismiss the dialog and we're done.
+ mCurrentDialog = null;
}
private void showDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) {
@@ -416,7 +420,7 @@
// TODO: Clean this up
Bundle bundle = (Bundle) mCurrentDialogArgs.arg1;
bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED,
- Authenticator.TYPE_CREDENTIAL);
+ Authenticators.DEVICE_CREDENTIAL);
}
showDialog(mCurrentDialogArgs, true /* skipAnimation */, savedState);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
index d6f830d..7d237c4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
@@ -16,23 +16,21 @@
package com.android.systemui.biometrics;
+import static android.hardware.biometrics.BiometricManager.Authenticators;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
import android.annotation.IntDef;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
-import android.hardware.biometrics.Authenticator;
import android.hardware.biometrics.BiometricPrompt;
import android.os.Bundle;
import android.os.UserManager;
import android.util.DisplayMetrics;
-import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -71,12 +69,12 @@
static boolean isDeviceCredentialAllowed(Bundle biometricPromptBundle) {
final int authenticators = getAuthenticators(biometricPromptBundle);
- return (authenticators & Authenticator.TYPE_CREDENTIAL) != 0;
+ return (authenticators & Authenticators.DEVICE_CREDENTIAL) != 0;
}
static boolean isBiometricAllowed(Bundle biometricPromptBundle) {
final int authenticators = getAuthenticators(biometricPromptBundle);
- return (authenticators & Authenticator.TYPE_BIOMETRIC) != 0;
+ return (authenticators & Authenticators.BIOMETRIC_WEAK) != 0;
}
static int getAuthenticators(Bundle biometricPromptBundle) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
index 7f1b356..7aeb785 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
@@ -16,6 +16,7 @@
package com.android.systemui.doze;
+import android.annotation.Nullable;
import android.app.IWallpaperManager;
import android.os.RemoteException;
import android.util.Log;
@@ -34,6 +35,7 @@
private static final String TAG = "DozeWallpaperState";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ @Nullable
private final IWallpaperManager mWallpaperManagerService;
private final DozeParameters mDozeParameters;
private final BiometricUnlockController mBiometricUnlockController;
@@ -79,16 +81,18 @@
if (isAmbientMode != mIsAmbientMode) {
mIsAmbientMode = isAmbientMode;
- try {
- long duration = animated ? StackStateAnimator.ANIMATION_DURATION_WAKEUP : 0L;
- if (DEBUG) {
- Log.i(TAG, "AOD wallpaper state changed to: " + mIsAmbientMode
+ if (mWallpaperManagerService != null) {
+ try {
+ long duration = animated ? StackStateAnimator.ANIMATION_DURATION_WAKEUP : 0L;
+ if (DEBUG) {
+ Log.i(TAG, "AOD wallpaper state changed to: " + mIsAmbientMode
+ ", animationDuration: " + duration);
+ }
+ mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, duration);
+ } catch (RemoteException e) {
+ // Cannot notify wallpaper manager service, but it's fine, let's just skip it.
+ Log.w(TAG, "Cannot notify state to WallpaperManagerService: " + mIsAmbientMode);
}
- mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, duration);
- } catch (RemoteException e) {
- // Cannot notify wallpaper manager service, but it's fine, let's just skip it.
- Log.w(TAG, "Cannot notify state to WallpaperManagerService: " + mIsAmbientMode);
}
}
}
@@ -97,5 +101,6 @@
public void dump(PrintWriter pw) {
pw.println("DozeWallpaperState:");
pw.println(" isAmbientMode: " + mIsAmbientMode);
+ pw.println(" hasWallpaperService: " + (mWallpaperManagerService != null));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt
index 4f03003..efcef71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt
@@ -31,6 +31,11 @@
abstract fun peopleHubDataSource(impl: PeopleHubDataSourceImpl): DataSource<PeopleHubModel>
@Binds
+ abstract fun peopleHubSettingChangeDataSource(
+ impl: PeopleHubSettingChangeDataSourceImpl
+ ): DataSource<Boolean>
+
+ @Binds
abstract fun peopleHubViewModelFactoryDataSource(
impl: PeopleHubViewModelFactoryDataSourceImpl
): DataSource<PeopleHubViewModelFactory>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
index 5c35408..e9d6a0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
@@ -16,7 +16,14 @@
package com.android.systemui.statusbar.notification.people
+import android.content.Context
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
import android.view.View
+import com.android.systemui.dagger.qualifiers.MainHandler
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager
import javax.inject.Inject
@@ -90,29 +97,58 @@
@Singleton
class PeopleHubViewModelFactoryDataSourceImpl @Inject constructor(
private val activityStarter: ActivityStarter,
- private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubModel>
+ private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubModel>,
+ private val settingChangeSource: DataSource<@JvmSuppressWildcards Boolean>
) : DataSource<PeopleHubViewModelFactory> {
- override fun registerListener(listener: DataListener<PeopleHubViewModelFactory>) =
- dataSource.registerListener(PeopleHubModelListenerImpl(activityStarter, listener))
+ override fun registerListener(listener: DataListener<PeopleHubViewModelFactory>): Subscription {
+ var stripEnabled = false
+ var model: PeopleHubModel? = null
+
+ fun updateListener() {
+ // don't invoke listener until we've received our first model
+ model?.let { model ->
+ val factory =
+ if (stripEnabled) PeopleHubViewModelFactoryImpl(model, activityStarter)
+ else EmptyViewModelFactory
+ listener.onDataChanged(factory)
+ }
+ }
+
+ val settingSub = settingChangeSource.registerListener(object : DataListener<Boolean> {
+ override fun onDataChanged(data: Boolean) {
+ stripEnabled = data
+ updateListener()
+ }
+ })
+ val dataSub = dataSource.registerListener(object : DataListener<PeopleHubModel> {
+ override fun onDataChanged(data: PeopleHubModel) {
+ model = data
+ updateListener()
+ }
+ })
+ return object : Subscription {
+ override fun unsubscribe() {
+ settingSub.unsubscribe()
+ dataSub.unsubscribe()
+ }
+ }
+ }
}
-private class PeopleHubModelListenerImpl(
- private val activityStarter: ActivityStarter,
- private val dataListener: DataListener<PeopleHubViewModelFactory>
-) : DataListener<PeopleHubModel> {
-
- override fun onDataChanged(data: PeopleHubModel) =
- dataListener.onDataChanged(PeopleHubViewModelFactoryImpl(data, activityStarter))
+private object EmptyViewModelFactory : PeopleHubViewModelFactory {
+ override fun createWithAssociatedClickView(view: View): PeopleHubViewModel {
+ return PeopleHubViewModel(emptySequence(), false)
+ }
}
private class PeopleHubViewModelFactoryImpl(
- private val data: PeopleHubModel,
+ private val model: PeopleHubModel,
private val activityStarter: ActivityStarter
) : PeopleHubViewModelFactory {
override fun createWithAssociatedClickView(view: View): PeopleHubViewModel {
- val personViewModels = data.people.asSequence().map { personModel ->
+ val personViewModels = model.people.asSequence().map { personModel ->
val onClick = {
activityStarter.startPendingIntentDismissingKeyguard(
personModel.clickIntent,
@@ -122,7 +158,42 @@
}
PersonViewModel(personModel.name, personModel.avatar, onClick)
}
- return PeopleHubViewModel(personViewModels, data.people.isNotEmpty())
+ return PeopleHubViewModel(personViewModels, model.people.isNotEmpty())
+ }
+}
+
+@Singleton
+class PeopleHubSettingChangeDataSourceImpl @Inject constructor(
+ @MainHandler private val handler: Handler,
+ context: Context
+) : DataSource<Boolean> {
+
+ private val settingUri = Settings.Secure.getUriFor(Settings.Secure.PEOPLE_STRIP)
+ private val contentResolver = context.contentResolver
+
+ override fun registerListener(listener: DataListener<Boolean>): Subscription {
+ // Immediately report current value of setting
+ updateListener(listener)
+ val observer = object : ContentObserver(handler) {
+ override fun onChange(selfChange: Boolean, uri: Uri?, userId: Int) {
+ super.onChange(selfChange, uri, userId)
+ updateListener(listener)
+ }
+ }
+ contentResolver.registerContentObserver(settingUri, false, observer, UserHandle.USER_ALL)
+ return object : Subscription {
+ override fun unsubscribe() = contentResolver.unregisterContentObserver(observer)
+ }
+ }
+
+ private fun updateListener(listener: DataListener<Boolean>) {
+ val setting = Settings.Secure.getIntForUser(
+ contentResolver,
+ Settings.Secure.PEOPLE_STRIP,
+ 0,
+ UserHandle.USER_CURRENT
+ )
+ listener.onDataChanged(setting != 0)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index 8e9a051e..2761689 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -249,10 +249,8 @@
}
}
- if (adjustPeopleHubVisibilityAndPosition(lastPersonIndex)) {
- // make room for peopleHub
- firstGentleNotifIndex++;
- }
+ // make room for peopleHub
+ firstGentleNotifIndex += adjustPeopleHubVisibilityAndPosition(lastPersonIndex);
adjustGentleHeaderVisibilityAndPosition(firstGentleNotifIndex);
@@ -296,7 +294,7 @@
}
}
- private boolean adjustPeopleHubVisibilityAndPosition(int lastPersonIndex) {
+ private int adjustPeopleHubVisibilityAndPosition(int lastPersonIndex) {
final boolean showPeopleHeader = mPeopleHubVisible
&& mNumberOfSections > 2
&& mStatusBarStateController.getState() != StatusBarState.KEYGUARD;
@@ -307,6 +305,7 @@
if (!showPeopleHeader) {
if (currentlyVisible) {
mParent.removeView(mPeopleHubView);
+ return -1;
}
} else {
mPeopleHubView.unDismiss();
@@ -317,7 +316,7 @@
mPeopleHubView.setTransientContainer(null);
}
mParent.addView(mPeopleHubView, targetIndex);
- return true;
+ return 1;
} else if (currentHubIndex != targetIndex) {
if (currentHubIndex < targetIndex) {
targetIndex--;
@@ -325,7 +324,7 @@
mParent.changeViewPosition(mPeopleHubView, targetIndex);
}
}
- return false;
+ return 0;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 1454e25..b8aea9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -360,7 +360,8 @@
return false;
}
- if (mState == ScrimState.AOD && mDozeParameters.getAlwaysOn()) {
+ if (mState == ScrimState.AOD
+ && (mDozeParameters.getAlwaysOn() || mDockManager.isDocked())) {
return true;
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockInfoTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockInfoTest.java
index d2b2654..4c0890a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockInfoTest.java
@@ -57,7 +57,7 @@
@Test
public void testGetTitle() {
final String title = "title";
- ClockInfo info = ClockInfo.builder().setTitle(title).build();
+ ClockInfo info = ClockInfo.builder().setTitle(() -> title).build();
assertThat(info.getTitle()).isEqualTo(title);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockOptionsProviderTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockOptionsProviderTest.java
index 0cd6f9a..d2832fb9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockOptionsProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockOptionsProviderTest.java
@@ -117,12 +117,12 @@
public void testQuery_listOptions() {
mClocks.add(ClockInfo.builder()
.setName("name_a")
- .setTitle("title_a")
+ .setTitle(() -> "title_a")
.setId("id_a")
.build());
mClocks.add(ClockInfo.builder()
.setName("name_b")
- .setTitle("title_b")
+ .setTitle(() -> "title_b")
.setId("id_b")
.build());
Cursor cursor = mProvider.query(mListOptionsUri, null, null, null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
index df67637..25cc9a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.biometrics;
+import static android.hardware.biometrics.BiometricManager.Authenticators;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -24,7 +26,6 @@
import static org.mockito.Mockito.verify;
import android.content.Context;
-import android.hardware.biometrics.Authenticator;
import android.hardware.biometrics.BiometricPrompt;
import android.os.Bundle;
import android.test.suitebuilder.annotation.SmallTest;
@@ -292,9 +293,9 @@
private Bundle buildBiometricPromptBundle(boolean allowDeviceCredential) {
Bundle bundle = new Bundle();
bundle.putCharSequence(BiometricPrompt.KEY_TITLE, "Title");
- int authenticators = Authenticator.TYPE_BIOMETRIC;
+ int authenticators = Authenticators.BIOMETRIC_WEAK;
if (allowDeviceCredential) {
- authenticators |= Authenticator.TYPE_CREDENTIAL;
+ authenticators |= Authenticators.DEVICE_CREDENTIAL;
} else {
bundle.putCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT, "Negative");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
index 6e438e8..162b16e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.biometrics;
+import static android.hardware.biometrics.BiometricManager.Authenticators;
+
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -26,7 +28,6 @@
import static org.mockito.Mockito.verify;
import android.content.Context;
-import android.hardware.biometrics.Authenticator;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricPrompt;
import android.os.Bundle;
@@ -64,7 +65,7 @@
@Test
public void testActionAuthenticated_sendsDismissedAuthenticated() {
- initializeContainer(Authenticator.TYPE_BIOMETRIC);
+ initializeContainer(Authenticators.BIOMETRIC_WEAK);
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_AUTHENTICATED);
@@ -73,7 +74,7 @@
@Test
public void testActionUserCanceled_sendsDismissedUserCanceled() {
- initializeContainer(Authenticator.TYPE_BIOMETRIC);
+ initializeContainer(Authenticators.BIOMETRIC_WEAK);
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_USER_CANCELED);
@@ -82,7 +83,7 @@
@Test
public void testActionButtonNegative_sendsDismissedButtonNegative() {
- initializeContainer(Authenticator.TYPE_BIOMETRIC);
+ initializeContainer(Authenticators.BIOMETRIC_WEAK);
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE);
@@ -91,7 +92,7 @@
@Test
public void testActionTryAgain_sendsTryAgain() {
- initializeContainer(Authenticator.TYPE_BIOMETRIC);
+ initializeContainer(Authenticators.BIOMETRIC_WEAK);
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN);
@@ -100,7 +101,7 @@
@Test
public void testActionError_sendsDismissedError() {
- initializeContainer(Authenticator.TYPE_BIOMETRIC);
+ initializeContainer(Authenticators.BIOMETRIC_WEAK);
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_ERROR);
@@ -110,7 +111,7 @@
@Test
public void testActionUseDeviceCredential_sendsOnDeviceCredentialPressed() {
initializeContainer(
- Authenticator.TYPE_BIOMETRIC | Authenticator.TYPE_CREDENTIAL);
+ Authenticators.BIOMETRIC_WEAK | Authenticators.DEVICE_CREDENTIAL);
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL);
@@ -125,7 +126,7 @@
@Test
public void testAnimateToCredentialUI_invokesStartTransitionToCredentialUI() {
initializeContainer(
- Authenticator.TYPE_BIOMETRIC | Authenticator.TYPE_CREDENTIAL);
+ Authenticators.BIOMETRIC_WEAK | Authenticators.DEVICE_CREDENTIAL);
mAuthContainer.mBiometricView = mock(AuthBiometricView.class);
mAuthContainer.animateToCredentialUI();
@@ -134,7 +135,7 @@
@Test
public void testShowBiometricUI() {
- initializeContainer(Authenticator.TYPE_BIOMETRIC);
+ initializeContainer(Authenticators.BIOMETRIC_WEAK);
assertNotEquals(null, mAuthContainer.mBiometricView);
@@ -146,7 +147,7 @@
@Test
public void testShowCredentialUI_doesNotInflateBiometricUI() {
- initializeContainer(Authenticator.TYPE_CREDENTIAL);
+ initializeContainer(Authenticators.DEVICE_CREDENTIAL);
mAuthContainer.onAttachedToWindowInternal();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index f6375fc..c0e92e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.biometrics;
+import static android.hardware.biometrics.BiometricManager.Authenticators;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
import static junit.framework.TestCase.assertNotNull;
@@ -38,7 +40,6 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
-import android.hardware.biometrics.Authenticator;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
@@ -109,28 +110,28 @@
@Test
public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED);
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
}
@Test
public void testSendsReasonNegative_whenDismissedByButtonNegative() throws Exception {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE);
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
}
@Test
public void testSendsReasonConfirmed_whenDismissedByButtonPositive() throws Exception {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE);
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED);
}
@Test
public void testSendsReasonConfirmNotRequired_whenDismissedByAuthenticated() throws Exception {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED);
verify(mReceiver).onDialogDismissed(
BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED);
@@ -138,14 +139,14 @@
@Test
public void testSendsReasonError_whenDismissedByError() throws Exception {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_ERROR);
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_ERROR);
}
@Test
public void testSendsReasonServerRequested_whenDismissedByServer() throws Exception {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER);
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
}
@@ -153,7 +154,7 @@
@Test
public void testSendsReasonCredentialConfirmed_whenDeviceCredentialAuthenticated()
throws Exception {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED);
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED);
}
@@ -163,20 +164,20 @@
@Test
public void testShowInvoked_whenSystemRequested()
throws Exception {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
verify(mDialog1).show(any(), any());
}
@Test
public void testOnAuthenticationSucceededInvoked_whenSystemRequested() {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onBiometricAuthenticated();
verify(mDialog1).onAuthenticationSucceeded();
}
@Test
public void testOnAuthenticationFailedInvoked_whenBiometricRejected() {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onBiometricError(BiometricAuthenticator.TYPE_NONE,
BiometricConstants.BIOMETRIC_PAUSED_REJECTED,
0 /* vendorCode */);
@@ -189,7 +190,7 @@
@Test
public void testOnAuthenticationFailedInvoked_whenBiometricTimedOut() {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
final int error = BiometricConstants.BIOMETRIC_ERROR_TIMEOUT;
final int vendorCode = 0;
mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
@@ -202,7 +203,7 @@
@Test
public void testOnHelpInvoked_whenSystemRequested() {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
final String helpMessage = "help";
mAuthController.onBiometricHelp(helpMessage);
@@ -214,7 +215,7 @@
@Test
public void testOnErrorInvoked_whenSystemRequested() throws Exception {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
final int error = 1;
final int vendorCode = 0;
mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
@@ -227,7 +228,7 @@
@Test
public void testErrorLockout_whenCredentialAllowed_AnimatesToCredentialUI() {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT;
final int vendorCode = 0;
@@ -240,7 +241,7 @@
@Test
public void testErrorLockoutPermanent_whenCredentialAllowed_AnimatesToCredentialUI() {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
final int vendorCode = 0;
@@ -253,7 +254,7 @@
@Test
public void testErrorLockout_whenCredentialNotAllowed_sendsOnError() {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT;
final int vendorCode = 0;
@@ -266,7 +267,7 @@
@Test
public void testErrorLockoutPermanent_whenCredentialNotAllowed_sendsOnError() {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
final int vendorCode = 0;
@@ -278,30 +279,24 @@
}
@Test
- public void testDismissWithoutCallbackInvoked_whenSystemRequested() {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
- mAuthController.hideAuthenticationDialog();
- verify(mDialog1).dismissFromSystemServer();
- }
-
- @Test
- public void testClientNotified_whenDismissedBySystemServer() {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ public void testHideAuthenticationDialog_invokesDismissFromSystemServer() {
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.hideAuthenticationDialog();
verify(mDialog1).dismissFromSystemServer();
- assertNotNull(mAuthController.mCurrentDialog);
- assertNotNull(mAuthController.mReceiver);
+ // In this case, BiometricService sends the error to the client immediately, without
+ // doing a round trip to SystemUI.
+ assertNull(mAuthController.mCurrentDialog);
}
// Corner case tests
@Test
public void testShowNewDialog_beforeOldDialogDismissed_SkipsAnimations() {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
verify(mDialog1).show(any(), any());
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
// First dialog should be dismissed without animation
verify(mDialog1).dismissWithoutCallback(eq(false) /* animate */);
@@ -312,7 +307,7 @@
@Test
public void testConfigurationPersists_whenOnConfigurationChanged() {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
verify(mDialog1).show(any(), any());
// Return that the UI is in "showing" state
@@ -342,7 +337,7 @@
@Test
public void testConfigurationPersists_whenBiometricFallbackToCredential() {
- showDialog(Authenticator.TYPE_CREDENTIAL | Authenticator.TYPE_BIOMETRIC,
+ showDialog(Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK,
BiometricPrompt.TYPE_FACE);
verify(mDialog1).show(any(), any());
@@ -361,14 +356,14 @@
// Check that the new dialog was initialized to the credential UI.
ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(mDialog2).show(any(), captor.capture());
- assertEquals(Authenticator.TYPE_CREDENTIAL,
+ assertEquals(Authenticators.DEVICE_CREDENTIAL,
mAuthController.mLastBiometricPromptBundle
.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
}
@Test
public void testClientNotified_whenTaskStackChangesDuringAuthentication() throws Exception {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
List<ActivityManager.RunningTaskInfo> tasks = new ArrayList<>();
ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
@@ -388,21 +383,21 @@
@Test
public void testDoesNotCrash_whenTryAgainPressedAfterDismissal() {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED);
mAuthController.onTryAgainPressed();
}
@Test
public void testDoesNotCrash_whenDeviceCredentialPressedAfterDismissal() {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED);
mAuthController.onDeviceCredentialPressed();
}
@Test
public void testActionCloseSystemDialogs_dismissesDialogIfShowing() throws Exception {
- showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mAuthController.mBroadcastReceiver.onReceive(mContext, intent);
waitForIdleSync();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
index f5d6f22..0764d0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
@@ -89,8 +89,17 @@
return mockSubscription
}
}
+ val fakeSettingDataSource = object : DataSource<Boolean> {
+ override fun registerListener(listener: DataListener<Boolean>): Subscription {
+ listener.onDataChanged(true)
+ return mockSubscription
+ }
+ }
val factoryDataSource = PeopleHubViewModelFactoryDataSourceImpl(
- mockActivityStarter, fakeModelDataSource)
+ mockActivityStarter,
+ fakeModelDataSource,
+ fakeSettingDataSource
+ )
val fakeListener = FakeDataListener<PeopleHubViewModelFactory>()
val mockClickView = mock(View::class.java)
@@ -112,6 +121,41 @@
same(mockClickView)
)
}
+
+ @Test
+ fun testViewModelDataSource_notVisibleIfSettingDisabled() {
+ val fakeClickIntent = PendingIntent.getActivity(context, 0, Intent("action"), 0)
+ val fakePerson = fakePersonModel("id", "name", fakeClickIntent)
+ val fakeModel = PeopleHubModel(listOf(fakePerson))
+ val mockSubscription = mock(Subscription::class.java)
+ val fakeModelDataSource = object : DataSource<PeopleHubModel> {
+ override fun registerListener(listener: DataListener<PeopleHubModel>): Subscription {
+ listener.onDataChanged(fakeModel)
+ return mockSubscription
+ }
+ }
+ val fakeSettingDataSource = object : DataSource<Boolean> {
+ override fun registerListener(listener: DataListener<Boolean>): Subscription {
+ listener.onDataChanged(false)
+ return mockSubscription
+ }
+ }
+ val factoryDataSource = PeopleHubViewModelFactoryDataSourceImpl(
+ mockActivityStarter,
+ fakeModelDataSource,
+ fakeSettingDataSource
+ )
+ val fakeListener = FakeDataListener<PeopleHubViewModelFactory>()
+ val mockClickView = mock(View::class.java)
+
+ factoryDataSource.registerListener(fakeListener)
+
+ val viewModel = (fakeListener.lastSeen as Maybe.Just).value
+ .createWithAssociatedClickView(mockClickView)
+ assertThat(viewModel.isVisible).isFalse()
+ val people = viewModel.people.toList()
+ assertThat(people.size).isEqualTo(0)
+ }
}
/** Works around Mockito matchers returning `null` and breaking non-nullable Kotlin code. */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 4d6ff1f..008a349 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -760,6 +760,17 @@
}
@Test
+ public void testWillHideDockedWallpaper() {
+ mAlwaysOnEnabled = false;
+ when(mDockManager.isDocked()).thenReturn(true);
+ mScrimController.setWallpaperSupportsAmbientMode(true);
+
+ mScrimController.transitionTo(ScrimState.AOD);
+
+ verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any());
+ }
+
+ @Test
public void testConservesExpansionOpacityAfterTransition() {
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.setPanelExpansion(0.5f);
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 53f306b..f79b876 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -275,6 +275,11 @@
public abstract ComponentName getDefaultHomeActivity(int userId);
/**
+ * @return The SystemUI service component name.
+ */
+ public abstract ComponentName getSystemUiServiceComponent();
+
+ /**
* Called by DeviceOwnerManagerService to set the package names of device owner and profile
* owners.
*/
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 1e5b915..73b6c7a 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -22,6 +22,8 @@
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.RTC_WAKEUP;
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import static android.os.UserHandle.USER_SYSTEM;
import android.annotation.UserIdInt;
import android.app.Activity;
@@ -41,10 +43,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PermissionInfo;
+import android.content.pm.PackageManagerInternal;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryManager;
@@ -223,15 +222,6 @@
long mLastTimeChangeRealtime;
int mNumTimeChanged;
- // Bookkeeping about the identity of the "System UI" package, determined at runtime.
-
- /**
- * This permission must be defined by the canonical System UI package,
- * with protection level "signature".
- */
- private static final String SYSTEM_UI_SELF_PERMISSION =
- "android.permission.systemui.IDENTITY";
-
/**
* At boot we use SYSTEM_UI_SELF_PERMISSION to look up the definer's uid.
*/
@@ -3201,7 +3191,7 @@
}
void removeUserLocked(int userHandle) {
- if (userHandle == UserHandle.USER_SYSTEM) {
+ if (userHandle == USER_SYSTEM) {
// If we're told we're removing the system user, ignore it.
return;
}
@@ -3845,21 +3835,9 @@
}
int getSystemUiUid() {
- int sysUiUid = -1;
- final PackageManager pm = mContext.getPackageManager();
- try {
- PermissionInfo sysUiPerm = pm.getPermissionInfo(SYSTEM_UI_SELF_PERMISSION, 0);
- ApplicationInfo sysUi = pm.getApplicationInfo(sysUiPerm.packageName, 0);
- if ((sysUi.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
- sysUiUid = sysUi.uid;
- } else {
- Slog.e(TAG, "SysUI permission " + SYSTEM_UI_SELF_PERMISSION
- + " defined by non-privileged app " + sysUi.packageName
- + " - ignoring");
- }
- } catch (NameNotFoundException e) {
- }
- return sysUiUid;
+ PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
+ return pm.getPackageUid(pm.getSystemUiServiceComponent().getPackageName(),
+ MATCH_SYSTEM_ONLY, USER_SYSTEM);
}
ClockReceiver getClockReceiver(AlarmManagerService service) {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 119b987..470300e 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -16,6 +16,9 @@
package com.android.server;
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import static android.os.UserHandle.USER_SYSTEM;
+
import android.Manifest;
import android.app.ActivityManager;
import android.app.AppGlobals;
@@ -42,6 +45,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.os.Binder;
@@ -242,10 +246,10 @@
}
// DISALLOW_BLUETOOTH can only be set by DO or PO on the system user.
- if (userId == UserHandle.USER_SYSTEM
+ if (userId == USER_SYSTEM
&& UserRestrictionsUtils.restrictionsChanged(prevRestrictions,
newRestrictions, UserManager.DISALLOW_BLUETOOTH)) {
- if (userId == UserHandle.USER_SYSTEM && newRestrictions.getBoolean(
+ if (userId == USER_SYSTEM && newRestrictions.getBoolean(
UserManager.DISALLOW_BLUETOOTH)) {
updateOppLauncherComponentState(userId, true); // Sharing disallowed
sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_DISALLOWED,
@@ -437,18 +441,18 @@
}
int systemUiUid = -1;
- try {
- // Check if device is configured with no home screen, which implies no SystemUI.
- boolean noHome = mContext.getResources().getBoolean(R.bool.config_noHomeScreen);
- if (!noHome) {
- systemUiUid = mContext.getPackageManager()
- .getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY,
- UserHandle.USER_SYSTEM);
- }
+ // Check if device is configured with no home screen, which implies no SystemUI.
+ boolean noHome = mContext.getResources().getBoolean(R.bool.config_noHomeScreen);
+ if (!noHome) {
+ PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
+ systemUiUid = pm.getPackageUid(pm.getSystemUiServiceComponent().getPackageName(),
+ MATCH_SYSTEM_ONLY, USER_SYSTEM);
+ }
+ if (systemUiUid >= 0) {
Slog.d(TAG, "Detected SystemUiUid: " + Integer.toString(systemUiUid));
- } catch (PackageManager.NameNotFoundException e) {
+ } else {
// Some platforms, such as wearables do not have a system ui.
- Slog.w(TAG, "Unable to resolve SystemUI's UID.", e);
+ Slog.w(TAG, "Unable to resolve SystemUI's UID.");
}
mSystemUiUid = systemUiUid;
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 29d3a1d7..9b1d9e9 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3824,7 +3824,7 @@
}
}
if ((hasInstall || hasInstallOp) && hasWrite) {
- return Zygote.MOUNT_EXTERNAL_WRITE;
+ return Zygote.MOUNT_EXTERNAL_INSTALLER;
}
// Otherwise we're willing to give out sandboxed or non-sandboxed if
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 6bc117b..4a0c7a3 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1496,16 +1496,6 @@
}
}
- public void notifyDataConnection(int state, boolean isDataAllowed, String apn, String apnType,
- LinkProperties linkProperties,
- NetworkCapabilities networkCapabilities, int networkType,
- boolean roaming) {
- notifyDataConnectionForSubscriber(SubscriptionManager.DEFAULT_PHONE_INDEX,
- SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, state,
- isDataAllowed, apn, apnType, linkProperties,
- networkCapabilities, networkType, roaming);
- }
-
public void notifyDataConnectionForSubscriber(int phoneId, int subId, int state,
boolean isDataAllowed,
String apn, String apnType,
@@ -1574,12 +1564,6 @@
networkCapabilities, roaming, subId);
}
- public void notifyDataConnectionFailed(String apnType) {
- notifyDataConnectionFailedForSubscriber(SubscriptionManager.DEFAULT_PHONE_INDEX,
- SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
- apnType);
- }
-
public void notifyDataConnectionFailedForSubscriber(int phoneId, int subId, String apnType) {
if (!checkNotifyPermission("notifyDataConnectionFailed()")) {
return;
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 3330882..f6c11cd 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -25,6 +25,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.input.InputManager;
@@ -79,7 +80,6 @@
implements InputManager.InputDeviceListener {
private static final String TAG = "VibratorService";
private static final boolean DEBUG = false;
- private static final String SYSTEM_UI_PACKAGE = "com.android.systemui";
private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
private static final String RAMPING_RINGER_ENABLED = "ramping_ringer_enabled";
@@ -139,6 +139,7 @@
private final PowerManager.WakeLock mWakeLock;
private final AppOpsManager mAppOps;
private final IBatteryStats mBatteryStatsService;
+ private final String mSystemUiPackage;
private PowerManagerInternal mPowerManagerInternal;
private InputManager mIm;
private Vibrator mVibrator;
@@ -284,7 +285,7 @@
}
public boolean isFromSystem() {
- return uid == Process.SYSTEM_UID || uid == 0 || SYSTEM_UI_PACKAGE.equals(opPkg);
+ return uid == Process.SYSTEM_UID || uid == 0 || mSystemUiPackage.equals(opPkg);
}
public VibrationInfo toInfo() {
@@ -372,6 +373,8 @@
mAppOps = mContext.getSystemService(AppOpsManager.class);
mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
BatteryStats.SERVICE_NAME));
+ mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
+ .getSystemUiServiceComponent().getPackageName();
mPreviousVibrationsLimit = mContext.getResources().getInteger(
com.android.internal.R.integer.config_previousVibrationsDumpLimit);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b55d6ad..f1cee034 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8389,6 +8389,18 @@
return BugReportHandlerUtil.launchBugReportHandlerApp(mContext);
}
+ /**
+ * Get packages of bugreport-whitelisted apps to handle a bug report.
+ *
+ * @return packages of bugreport-whitelisted apps to handle a bug report.
+ */
+ @Override
+ public List<String> getBugreportWhitelistedPackages() {
+ enforceCallingPermission(android.Manifest.permission.MANAGE_DEBUGGING,
+ "getBugreportWhitelistedPackages");
+ return new ArrayList<>(SystemConfig.getInstance().getBugreportWhitelistedPackages());
+ }
+
public void registerProcessObserver(IProcessObserver observer) {
enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
"registerProcessObserver()");
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 8144a71..60f420e 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -600,19 +600,11 @@
sendMsgNoDelay(MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP);
}
- /*package*/ void cancelA2dpDockTimeout() {
- mBrokerHandler.removeMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
- }
-
/*package*/ void postA2dpActiveDeviceChange(
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo);
}
- /*package*/ boolean hasScheduledA2dpDockTimeout() {
- return mBrokerHandler.hasMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
- }
-
// must be called synchronized on mConnectedDevices
/*package*/ boolean hasScheduledA2dpSinkConnectionState(BluetoothDevice btDevice) {
return (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
@@ -621,8 +613,8 @@
new BtHelper.BluetoothA2dpDeviceInfo(btDevice)));
}
- /*package*/ void setA2dpDockTimeout(String address, int a2dpCodec, int delayMs) {
- sendILMsg(MSG_IL_BTA2DP_DOCK_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs);
+ /*package*/ void setA2dpTimeout(String address, int a2dpCodec, int delayMs) {
+ sendILMsg(MSG_IL_BTA2DP_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs);
}
/*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) {
@@ -781,7 +773,7 @@
}
}
break;
- case MSG_IL_BTA2DP_DOCK_TIMEOUT:
+ case MSG_IL_BTA2DP_TIMEOUT:
// msg.obj == address of BTA2DP device
synchronized (mDeviceStateLock) {
mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1);
@@ -945,7 +937,7 @@
private static final int MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE = 7;
private static final int MSG_IL_SET_HEARING_AID_CONNECTION_STATE = 8;
private static final int MSG_BT_HEADSET_CNCT_FAILED = 9;
- private static final int MSG_IL_BTA2DP_DOCK_TIMEOUT = 10;
+ private static final int MSG_IL_BTA2DP_TIMEOUT = 10;
private static final int MSG_L_A2DP_DEVICE_CONFIG_CHANGE = 11;
private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 12;
private static final int MSG_REPORT_NEW_ROUTES = 13;
@@ -981,7 +973,7 @@
case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
- case MSG_IL_BTA2DP_DOCK_TIMEOUT:
+ case MSG_IL_BTA2DP_TIMEOUT:
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
case MSG_TOGGLE_HDMI:
case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
@@ -1071,7 +1063,7 @@
case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
- case MSG_IL_BTA2DP_DOCK_TIMEOUT:
+ case MSG_IL_BTA2DP_TIMEOUT:
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
if (sLastDeviceConnectMsgTime >= time) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 9061586..df56004 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -59,10 +59,21 @@
private static final String TAG = "AS.AudioDeviceInventory";
- // Actual list of connected devices
+ // lock to synchronize all access to mConnectedDevices and mApmConnectedDevices
+ private final Object mDevicesLock = new Object();
+
+ // List of connected devices
// Key for map created from DeviceInfo.makeDeviceListKey()
+ @GuardedBy("mDevicesLock")
private final LinkedHashMap<String, DeviceInfo> mConnectedDevices = new LinkedHashMap<>();
+ // List of devices actually connected to AudioPolicy (through AudioSystem), only one
+ // by device type, which is used as the key, value is the DeviceInfo generated key.
+ // For the moment only for A2DP sink devices.
+ // TODO: extend to all device types
+ @GuardedBy("mDevicesLock")
+ private final ArrayMap<Integer, String> mApmConnectedDevices = new ArrayMap<>();
+
// List of preferred devices for strategies
private final ArrayMap<Integer, AudioDeviceAddress> mPreferredDevices = new ArrayMap<>();
@@ -94,25 +105,30 @@
*/
private static class DeviceInfo {
final int mDeviceType;
- final String mDeviceName;
- final String mDeviceAddress;
+ final @NonNull String mDeviceName;
+ final @NonNull String mDeviceAddress;
int mDeviceCodecFormat;
DeviceInfo(int deviceType, String deviceName, String deviceAddress, int deviceCodecFormat) {
mDeviceType = deviceType;
- mDeviceName = deviceName;
- mDeviceAddress = deviceAddress;
+ mDeviceName = deviceName == null ? "" : deviceName;
+ mDeviceAddress = deviceAddress == null ? "" : deviceAddress;
mDeviceCodecFormat = deviceCodecFormat;
}
@Override
public String toString() {
return "[DeviceInfo: type:0x" + Integer.toHexString(mDeviceType)
- + " name:" + mDeviceName
+ + " (" + AudioSystem.getDeviceName(mDeviceType)
+ + ") name:" + mDeviceName
+ " addr:" + mDeviceAddress
+ " codec: " + Integer.toHexString(mDeviceCodecFormat) + "]";
}
+ String getKey() {
+ return makeDeviceListKey(mDeviceType, mDeviceAddress);
+ }
+
/**
* Generate a unique key for the mConnectedDevices List by composing the device "type"
* and the "address" associated with a specific instance of that device type
@@ -147,6 +163,14 @@
pw.println("\n" + prefix + "Preferred devices for strategy:");
mPreferredDevices.forEach((strategy, device) -> {
pw.println(" " + prefix + "strategy:" + strategy + " device:" + device); });
+ pw.println("\n" + prefix + "Connected devices:");
+ mConnectedDevices.forEach((key, deviceInfo) -> {
+ pw.println(" " + prefix + deviceInfo.toString()); });
+ pw.println("\n" + prefix + "APM Connected device (A2DP sink only):");
+ mApmConnectedDevices.forEach((keyType, valueAddress) -> {
+ pw.println(" " + prefix + " type:0x" + Integer.toHexString(keyType)
+ + " (" + AudioSystem.getDeviceName(keyType)
+ + ") addr:" + valueAddress); });
}
//------------------------------------------------------------
@@ -158,7 +182,8 @@
*/
// Always executed on AudioDeviceBroker message queue
/*package*/ void onRestoreDevices() {
- synchronized (mConnectedDevices) {
+ synchronized (mDevicesLock) {
+ //TODO iterate on mApmConnectedDevices instead once it handles all device types
for (DeviceInfo di : mConnectedDevices.values()) {
AudioSystem.setDeviceConnectionState(
di.mDeviceType,
@@ -168,7 +193,6 @@
di.mDeviceCodecFormat);
}
}
-
synchronized (mPreferredDevices) {
mPreferredDevices.forEach((strategy, device) -> {
AudioSystem.setPreferredDeviceForStrategy(strategy, device); });
@@ -187,6 +211,9 @@
+ state + " vol=" + a2dpVolume);
}
String address = btDevice.getAddress();
+ if (address == null) {
+ address = "";
+ }
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
address = "";
}
@@ -198,7 +225,7 @@
+ " codec=" + a2dpCodec
+ " vol=" + a2dpVolume));
- synchronized (mConnectedDevices) {
+ synchronized (mDevicesLock) {
final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
btDevice.getAddress());
final DeviceInfo di = mConnectedDevices.get(key);
@@ -238,7 +265,7 @@
address = "";
}
- synchronized (mConnectedDevices) {
+ synchronized (mDevicesLock) {
final String key = DeviceInfo.makeDeviceListKey(
AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address);
final DeviceInfo di = mConnectedDevices.get(key);
@@ -261,7 +288,7 @@
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
"onSetHearingAidConnectionState addr=" + address));
- synchronized (mConnectedDevices) {
+ synchronized (mDevicesLock) {
final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID,
btDevice.getAddress());
final DeviceInfo di = mConnectedDevices.get(key);
@@ -297,7 +324,7 @@
"onBluetoothA2dpActiveDeviceChange addr=" + address
+ " event=" + BtHelper.a2dpDeviceEventToString(event)));
- synchronized (mConnectedDevices) {
+ synchronized (mDevicesLock) {
if (mDeviceBroker.hasScheduledA2dpSinkConnectionState(btDevice)) {
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
"A2dp config change ignored (scheduled connection change)"));
@@ -340,7 +367,7 @@
}
/*package*/ void onMakeA2dpDeviceUnavailableNow(String address, int a2dpCodec) {
- synchronized (mConnectedDevices) {
+ synchronized (mDevicesLock) {
makeA2dpDeviceUnavailableNow(address, a2dpCodec);
}
}
@@ -377,7 +404,7 @@
AudioDeviceInventory.WiredDeviceConnectionState wdcs) {
AudioService.sDeviceLogger.log(new AudioServiceEvents.WiredDevConnectEvent(wdcs));
- synchronized (mConnectedDevices) {
+ synchronized (mDevicesLock) {
if ((wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED)
&& DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(wdcs.mType)) {
mDeviceBroker.setBluetoothA2dpOnInt(true,
@@ -405,7 +432,7 @@
}
/*package*/ void onToggleHdmi() {
- synchronized (mConnectedDevices) {
+ synchronized (mDevicesLock) {
// Is HDMI connected?
final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HDMI, "");
final DeviceInfo di = mConnectedDevices.get(key);
@@ -472,7 +499,7 @@
+ Integer.toHexString(device) + " address:" + address
+ " name:" + deviceName + ")");
}
- synchronized (mConnectedDevices) {
+ synchronized (mDevicesLock) {
final String deviceKey = DeviceInfo.makeDeviceListKey(device, address);
if (AudioService.DEBUG_DEVICES) {
Slog.i(TAG, "deviceKey:" + deviceKey);
@@ -511,7 +538,7 @@
/*package*/ void disconnectA2dp() {
- synchronized (mConnectedDevices) {
+ synchronized (mDevicesLock) {
final ArraySet<String> toRemove = new ArraySet<>();
// Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices
mConnectedDevices.values().forEach(deviceInfo -> {
@@ -531,7 +558,7 @@
}
/*package*/ void disconnectA2dpSink() {
- synchronized (mConnectedDevices) {
+ synchronized (mDevicesLock) {
final ArraySet<String> toRemove = new ArraySet<>();
// Disconnect ALL DEVICE_IN_BLUETOOTH_A2DP devices
mConnectedDevices.values().forEach(deviceInfo -> {
@@ -544,7 +571,7 @@
}
/*package*/ void disconnectHearingAid() {
- synchronized (mConnectedDevices) {
+ synchronized (mDevicesLock) {
final ArraySet<String> toRemove = new ArraySet<>();
// Disconnect ALL DEVICE_OUT_HEARING_AID devices
mConnectedDevices.values().forEach(deviceInfo -> {
@@ -568,7 +595,7 @@
// from AudioSystem
/*package*/ int checkSendBecomingNoisyIntent(int device,
@AudioService.ConnectionState int state, int musicDevice) {
- synchronized (mConnectedDevices) {
+ synchronized (mDevicesLock) {
return checkSendBecomingNoisyIntentInt(device, state, musicDevice);
}
}
@@ -595,7 +622,7 @@
if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) {
throw new IllegalArgumentException("invalid profile " + profile);
}
- synchronized (mConnectedDevices) {
+ synchronized (mDevicesLock) {
if (profile == BluetoothProfile.A2DP && !suppressNoisyIntent) {
@AudioService.ConnectionState int asState =
(state == BluetoothA2dp.STATE_CONNECTED)
@@ -635,7 +662,7 @@
/*package*/ int setWiredDeviceConnectionState(int type, @AudioService.ConnectionState int state,
String address, String name, String caller) {
- synchronized (mConnectedDevices) {
+ synchronized (mDevicesLock) {
int delay = checkSendBecomingNoisyIntentInt(type, state, AudioSystem.DEVICE_NONE);
mDeviceBroker.postSetWiredDeviceConnectionState(
new WiredDeviceConnectionState(type, state, address, name, caller),
@@ -648,7 +675,7 @@
@NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
boolean suppressNoisyIntent, int musicDevice) {
int delay;
- synchronized (mConnectedDevices) {
+ synchronized (mDevicesLock) {
if (!suppressNoisyIntent) {
int intState = (state == BluetoothHearingAid.STATE_CONNECTED) ? 1 : 0;
delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_HEARING_AID,
@@ -665,39 +692,58 @@
//-------------------------------------------------------------------
// Internal utilities
- @GuardedBy("mConnectedDevices")
+ @GuardedBy("mDevicesLock")
private void makeA2dpDeviceAvailable(String address, String name, String eventSource,
int a2dpCodec) {
// enable A2DP before notifying A2DP connection to avoid unnecessary processing in
// audio policy manager
mDeviceBroker.setBluetoothA2dpOnInt(true, eventSource);
+ // at this point there could be another A2DP device already connected in APM, but it
+ // doesn't matter as this new one will overwrite the previous one
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
AudioSystem.DEVICE_STATE_AVAILABLE, address, name, a2dpCodec);
// Reset A2DP suspend state each time a new sink is connected
AudioSystem.setParameters("A2dpSuspended=false");
- mConnectedDevices.put(
- DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address),
- new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
- address, a2dpCodec));
+
+ final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
+ address, a2dpCodec);
+ final String diKey = di.getKey();
+ mConnectedDevices.put(diKey, di);
+ // on a connection always overwrite the device seen by AudioPolicy, see comment above when
+ // calling AudioSystem
+ mApmConnectedDevices.put(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, diKey);
+
mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
setCurrentAudioRouteNameIfPossible(name);
}
- @GuardedBy("mConnectedDevices")
+ @GuardedBy("mDevicesLock")
private void makeA2dpDeviceUnavailableNow(String address, int a2dpCodec) {
if (address == null) {
return;
}
+ final String deviceToRemoveKey =
+ DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
+
+ mConnectedDevices.remove(deviceToRemoveKey);
+ if (!mApmConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)
+ .equals(deviceToRemoveKey)) {
+ // removing A2DP device not currently used by AudioPolicy, log but don't act on it
+ AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+ "A2DP device " + address + " made unavailable, was not used")).printLog(TAG));
+ return;
+ }
+
+ // device to remove was visible by APM, update APM
mDeviceBroker.setAvrcpAbsoluteVolumeSupported(false);
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", a2dpCodec);
- mConnectedDevices.remove(
- DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
+ mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
// Remove A2DP routes as well
setCurrentAudioRouteNameIfPossible(null);
}
- @GuardedBy("mConnectedDevices")
+ @GuardedBy("mDevicesLock")
private void makeA2dpDeviceUnavailableLater(String address, int delayMs) {
// prevent any activity on the A2DP audio output to avoid unwanted
// reconnection of the sink.
@@ -711,11 +757,11 @@
// the device will be made unavailable later, so consider it disconnected right away
mConnectedDevices.remove(deviceKey);
// send the delayed message to make the device unavailable later
- mDeviceBroker.setA2dpDockTimeout(address, a2dpCodec, delayMs);
+ mDeviceBroker.setA2dpTimeout(address, a2dpCodec, delayMs);
}
- @GuardedBy("mConnectedDevices")
+ @GuardedBy("mDevicesLock")
private void makeA2dpSrcAvailable(String address) {
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP,
AudioSystem.DEVICE_STATE_AVAILABLE, address, "",
@@ -726,7 +772,7 @@
address, AudioSystem.AUDIO_FORMAT_DEFAULT));
}
- @GuardedBy("mConnectedDevices")
+ @GuardedBy("mDevicesLock")
private void makeA2dpSrcUnavailable(String address) {
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP,
AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "",
@@ -735,7 +781,7 @@
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address));
}
- @GuardedBy("mConnectedDevices")
+ @GuardedBy("mDevicesLock")
private void makeHearingAidDeviceAvailable(
String address, String name, int streamType, String eventSource) {
final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType,
@@ -755,7 +801,7 @@
setCurrentAudioRouteNameIfPossible(name);
}
- @GuardedBy("mConnectedDevices")
+ @GuardedBy("mDevicesLock")
private void makeHearingAidDeviceUnavailable(String address) {
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID,
AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "",
@@ -766,7 +812,7 @@
setCurrentAudioRouteNameIfPossible(null);
}
- @GuardedBy("mConnectedDevices")
+ @GuardedBy("mDevicesLock")
private void setCurrentAudioRouteNameIfPossible(String name) {
synchronized (mCurAudioRoutes) {
if (TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) {
@@ -779,7 +825,7 @@
}
}
- @GuardedBy("mConnectedDevices")
+ @GuardedBy("mDevicesLock")
private boolean isCurrentDeviceConnected() {
return mConnectedDevices.values().stream().anyMatch(deviceInfo ->
TextUtils.equals(deviceInfo.mDeviceName, mCurAudioRoutes.bluetoothName));
@@ -807,7 +853,7 @@
// must be called before removing the device from mConnectedDevices
// musicDevice argument is used when not AudioSystem.DEVICE_NONE instead of querying
// from AudioSystem
- @GuardedBy("mConnectedDevices")
+ @GuardedBy("mDevicesLock")
private int checkSendBecomingNoisyIntentInt(int device,
@AudioService.ConnectionState int state, int musicDevice) {
if (state != AudioService.CONNECTION_STATE_DISCONNECTED) {
@@ -1015,7 +1061,7 @@
public boolean isA2dpDeviceConnected(@NonNull BluetoothDevice device) {
final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
device.getAddress());
- synchronized (mConnectedDevices) {
+ synchronized (mDevicesLock) {
return (mConnectedDevices.get(key) != null);
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 1a62eb2..335cac8 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4488,12 +4488,13 @@
}
if (mNm.getZenMode() != Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
+ && !isStreamMutedByRingerOrZenMode(AudioSystem.STREAM_MUSIC)
&& DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.contains(newDevice)
&& mStreamStates[AudioSystem.STREAM_MUSIC].mIsMuted
&& mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(newDevice) != 0
&& (newDevice & AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC)) != 0) {
if (DEBUG_VOL) {
- Log.i(TAG, String.format(" onAccessoryPlugMediaUnmute unmuting device=%d [%s]",
+ Log.i(TAG, String.format("onAccessoryPlugMediaUnmute unmuting device=%d [%s]",
newDevice, AudioSystem.getOutputDeviceName(newDevice)));
}
mStreamStates[AudioSystem.STREAM_MUSIC].mute(false);
@@ -6167,6 +6168,7 @@
pw.println("\nRinger mode: ");
pw.println("- mode (internal) = " + RINGER_MODE_NAMES[mRingerMode]);
pw.println("- mode (external) = " + RINGER_MODE_NAMES[mRingerModeExternal]);
+ pw.println("- zen mode:" + Settings.Global.zenModeToString(mNm.getZenMode()));
dumpRingerModeStreams(pw, "affected", mRingerModeAffectedStreams);
dumpRingerModeStreams(pw, "muted", mRingerAndZenModeMutedStreams);
pw.print("- delegate = "); pw.println(mRingerModeDelegate);
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 22cb507..0d88388 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -26,10 +26,12 @@
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.BiometricManager.Authenticators;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.biometrics.IAuthService;
+import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
@@ -43,6 +45,7 @@
import android.os.UserHandle;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemService;
import com.android.server.biometrics.face.FaceAuthenticator;
@@ -57,9 +60,6 @@
private static final String TAG = "AuthService";
private static final boolean DEBUG = false;
- private final boolean mHasFeatureFace;
- private final boolean mHasFeatureFingerprint;
- private final boolean mHasFeatureIris;
private final Injector mInjector;
private IBiometricService mBiometricService;
@@ -89,6 +89,16 @@
public void publishBinderService(AuthService service, IAuthService.Stub impl) {
service.publishBinderService(Context.AUTH_SERVICE, impl);
}
+
+ /**
+ * Allows to test with various device sensor configurations.
+ * @param context
+ * @return
+ */
+ @VisibleForTesting
+ public String[] getConfiguration(Context context) {
+ return context.getResources().getStringArray(R.array.config_biometric_sensors);
+ }
}
private final class AuthServiceImpl extends IAuthService.Stub {
@@ -119,17 +129,17 @@
}
@Override
- public int canAuthenticate(String opPackageName, int userId) throws RemoteException {
+ public int canAuthenticate(String opPackageName, int userId,
+ @Authenticators.Types int authenticators) throws RemoteException {
final int callingUserId = UserHandle.getCallingUserId();
- Slog.d(TAG, "canAuthenticate, userId: " + userId
- + ", callingUserId: " + callingUserId);
-
+ Slog.d(TAG, "canAuthenticate, userId: " + userId + ", callingUserId: " + callingUserId
+ + ", authenticators: " + authenticators);
if (userId != callingUserId) {
checkInternalPermission();
} else {
checkPermission();
}
- return mBiometricService.canAuthenticate(opPackageName, userId);
+ return mBiometricService.canAuthenticate(opPackageName, userId, authenticators);
}
@Override
@@ -169,47 +179,56 @@
mInjector = injector;
mImpl = new AuthServiceImpl();
final PackageManager pm = context.getPackageManager();
- mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
- mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
- mHasFeatureIris = pm.hasSystemFeature(PackageManager.FEATURE_IRIS);
+ }
+
+ private void registerAuthenticator(SensorConfig config) throws RemoteException {
+
+ Slog.d(TAG, "Registering ID: " + config.mId
+ + " Modality: " + config.mModality
+ + " Strength: " + config.mStrength);
+
+ final IBiometricAuthenticator.Stub authenticator;
+
+ switch (config.mModality) {
+ case TYPE_FINGERPRINT:
+ authenticator = new FingerprintAuthenticator(IFingerprintService.Stub.asInterface(
+ ServiceManager.getService(Context.FINGERPRINT_SERVICE)));
+ break;
+
+ case TYPE_FACE:
+ authenticator = new FaceAuthenticator(IFaceService.Stub.asInterface(
+ ServiceManager.getService(Context.FACE_SERVICE)));
+ break;
+
+ case TYPE_IRIS:
+ authenticator = new IrisAuthenticator(IIrisService.Stub.asInterface(
+ ServiceManager.getService(Context.IRIS_SERVICE)));
+ break;
+
+ default:
+ Slog.e(TAG, "Unknown modality: " + config.mModality);
+ return;
+ }
+
+ mBiometricService.registerAuthenticator(config.mId, config.mModality, config.mStrength,
+ authenticator);
}
@Override
public void onStart() {
mBiometricService = mInjector.getBiometricService();
- if (mHasFeatureFace) {
- final FaceAuthenticator faceAuthenticator = new FaceAuthenticator(
- IFaceService.Stub.asInterface(ServiceManager.getService(Context.FACE_SERVICE)));
+ final String[] configs = mInjector.getConfiguration(getContext());
+
+ for (int i = 0; i < configs.length; i++) {
try {
- // TODO(b/141025588): Pass down the real id, strength, and modality.
- mBiometricService.registerAuthenticator(0, 0, TYPE_FACE, faceAuthenticator);
+ registerAuthenticator(new SensorConfig(configs[i]));
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
+
}
- if (mHasFeatureFingerprint) {
- final FingerprintAuthenticator fingerprintAuthenticator = new FingerprintAuthenticator(
- IFingerprintService.Stub.asInterface(
- ServiceManager.getService(Context.FINGERPRINT_SERVICE)));
- try {
- // TODO(b/141025588): Pass down the real id, strength, and modality.
- mBiometricService.registerAuthenticator(1, 0, TYPE_FINGERPRINT,
- fingerprintAuthenticator);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
- }
- if (mHasFeatureIris) {
- final IrisAuthenticator irisAuthenticator = new IrisAuthenticator(
- IIrisService.Stub.asInterface(ServiceManager.getService(Context.IRIS_SERVICE)));
- try {
- // TODO(b/141025588): Pass down the real id, strength, and modality.
- mBiometricService.registerAuthenticator(2, 0, TYPE_IRIS, irisAuthenticator);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
- }
+
mInjector.publishBinderService(this, mImpl);
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 5d36793..0f51e39 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -23,15 +23,16 @@
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 static android.hardware.biometrics.BiometricManager.Authenticators;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.UserSwitchObserver;
+import android.app.trust.ITrustManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
-import android.hardware.biometrics.Authenticator;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
@@ -78,7 +79,7 @@
*/
public class BiometricService extends SystemService {
- private static final String TAG = "BiometricService";
+ static final String TAG = "BiometricService";
private static final boolean DEBUG = true;
private static final int MSG_ON_AUTHENTICATION_SUCCEEDED = 2;
@@ -220,11 +221,15 @@
IStatusBarService mStatusBarService;
@VisibleForTesting
KeyStore mKeyStore;
+ @VisibleForTesting
+ ITrustManager mTrustManager;
// Get and cache the available authenticator (manager) classes. Used since aidl doesn't support
// polymorphism :/
final ArrayList<AuthenticatorWrapper> mAuthenticators = new ArrayList<>();
+ BiometricStrengthController mBiometricStrengthController;
+
// 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.
@VisibleForTesting
@@ -345,17 +350,50 @@
@VisibleForTesting
public static final class AuthenticatorWrapper {
public final int id;
- public final int strength;
+ public final int OEMStrength; // strength as configured by the OEM
+ private int updatedStrength; // strength updated by BiometricStrengthController
public final int modality;
public final IBiometricAuthenticator impl;
- AuthenticatorWrapper(int id, int strength, int modality,
+ AuthenticatorWrapper(int id, int modality, int strength,
IBiometricAuthenticator impl) {
this.id = id;
- this.strength = strength;
this.modality = modality;
+ this.OEMStrength = strength;
+ this.updatedStrength = strength;
this.impl = impl;
}
+
+ /**
+ * Returns the actual strength, taking any updated strengths into effect. Since more bits
+ * means lower strength, the resulting strength is never stronger than the OEM's configured
+ * strength.
+ * @return a bitfield, see {@link Authenticators}
+ */
+ public int getActualStrength() {
+ return OEMStrength | updatedStrength;
+ }
+
+ /**
+ * Stores the updated strength, which takes effect whenever {@link #getActualStrength()}
+ * is checked.
+ * @param newStrength
+ */
+ public void updateStrength(int newStrength) {
+ String log = "updateStrength: Before(" + toString() + ")";
+ updatedStrength = newStrength;
+ log += " After(" + toString() + ")";
+ Slog.d(TAG, log);
+ }
+
+ @Override
+ public String toString() {
+ return "ID(" + id + ")"
+ + " OEMStrength: " + OEMStrength
+ + " updatedStrength: " + updatedStrength
+ + " modality " + modality
+ + " authenticator: " + impl;
+ }
}
@VisibleForTesting
@@ -606,14 +644,14 @@
return;
}
- if (bundle.get(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED) != null) {
- checkInternalPermission();
+ if (!Utils.isValidAuthenticatorConfig(bundle)) {
+ throw new SecurityException("Invalid authenticator configuration");
}
Utils.combineAuthenticatorBundles(bundle);
- // Check the usage of this in system server. Need to remove this check if it becomes
- // a public API.
+ // Check the usage of this in system server. Need to remove this check if it becomes a
+ // public API.
final boolean useDefaultTitle =
bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false);
if (useDefaultTitle) {
@@ -651,9 +689,11 @@
}
@Override // Binder call
- public int canAuthenticate(String opPackageName, int userId) {
+ public int canAuthenticate(String opPackageName, int userId,
+ @Authenticators.Types int authenticators) {
Slog.d(TAG, "canAuthenticate: User=" + userId
- + ", Caller=" + UserHandle.getCallingUserId());
+ + ", Caller=" + UserHandle.getCallingUserId()
+ + ", Authenticators=" + authenticators);
if (userId != UserHandle.getCallingUserId()) {
checkInternalPermission();
@@ -661,16 +701,39 @@
checkPermission();
}
+
+ if (!Utils.isValidAuthenticatorConfig(authenticators)) {
+ throw new SecurityException("Invalid authenticator configuration");
+ }
+
+ final Bundle bundle = new Bundle();
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+
+ int biometricConstantsResult = BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
final long ident = Binder.clearCallingIdentity();
- int error;
try {
- final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId,
- opPackageName);
- error = result.second;
+ biometricConstantsResult =
+ checkAndGetAuthenticators(userId, bundle, opPackageName).second;
+ if (biometricConstantsResult != BiometricConstants.BIOMETRIC_SUCCESS
+ && Utils.isDeviceCredentialAllowed(bundle)) {
+ // If there's an issue with biometrics, but device credential is allowed and
+ // set up, return SUCCESS. If device credential isn't set up either, return
+ // ERROR_NO_DEVICE_CREDENTIAL.
+ if (mTrustManager.isDeviceSecure(userId)) {
+ biometricConstantsResult = BiometricConstants.BIOMETRIC_SUCCESS;
+ } else {
+ biometricConstantsResult =
+ BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL;
+ }
+ }
+
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
} finally {
Binder.restoreCallingIdentity(ident);
}
- return error;
+
+ return Utils.biometricConstantsToBiometricManager(biometricConstantsResult);
}
@Override
@@ -693,11 +756,46 @@
}
@Override
- public void registerAuthenticator(int id, int strength, int modality,
+ public void registerAuthenticator(int id, int modality, int strength,
IBiometricAuthenticator authenticator) {
checkInternalPermission();
- mAuthenticators.add(new AuthenticatorWrapper(id, strength, modality, authenticator));
+ Slog.d(TAG, "Registering ID: " + id
+ + " Modality: " + modality
+ + " Strength: " + strength);
+
+ if (authenticator == null) {
+ throw new IllegalArgumentException("Authenticator must not be null."
+ + " Did you forget to modify the core/res/res/values/xml overlay for"
+ + " config_biometric_sensors?");
+ }
+
+ if (strength != Authenticators.BIOMETRIC_STRONG
+ && strength != Authenticators.BIOMETRIC_WEAK) {
+ throw new IllegalStateException("Unsupported strength");
+ }
+
+ for (AuthenticatorWrapper wrapper : mAuthenticators) {
+ if (wrapper.id == id) {
+ throw new IllegalStateException("Cannot register duplicate authenticator");
+ }
+ }
+
+ // This happens infrequently enough, not worth caching.
+ final String[] configs = mInjector.getConfiguration(getContext());
+ boolean idFound = false;
+ for (int i = 0; i < configs.length; i++) {
+ SensorConfig config = new SensorConfig(configs[i]);
+ if (config.mId == id) {
+ idFound = true;
+ break;
+ }
+ }
+ if (!idFound) {
+ throw new IllegalStateException("Cannot register unknown id");
+ }
+
+ mAuthenticators.add(new AuthenticatorWrapper(id, modality, strength, authenticator));
}
@Override // Binder call
@@ -771,6 +869,11 @@
}
@VisibleForTesting
+ public ITrustManager getTrustManager() {
+ return ITrustManager.Stub.asInterface(ServiceManager.getService(Context.TRUST_SERVICE));
+ }
+
+ @VisibleForTesting
public IStatusBarService getStatusBarService() {
return IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
@@ -805,6 +908,25 @@
public void publishBinderService(BiometricService service, IBiometricService.Stub impl) {
service.publishBinderService(Context.BIOMETRIC_SERVICE, impl);
}
+
+ /**
+ * Allows to mock BiometricStrengthController for testing.
+ */
+ @VisibleForTesting
+ public BiometricStrengthController getBiometricStrengthController(
+ BiometricService service) {
+ return new BiometricStrengthController(service);
+ }
+
+ /**
+ * Allows to test with various device sensor configurations.
+ * @param context System Server context
+ * @return the sensor configuration from core/res/res/values/config.xml
+ */
+ @VisibleForTesting
+ public String[] getConfiguration(Context context) {
+ return context.getResources().getStringArray(R.array.config_biometric_sensors);
+ }
}
/**
@@ -849,7 +971,10 @@
public void onStart() {
mKeyStore = mInjector.getKeyStore();
mStatusBarService = mInjector.getStatusBarService();
+ mTrustManager = mInjector.getTrustManager();
mInjector.publishBinderService(this, mImpl);
+ mBiometricStrengthController = mInjector.getBiometricStrengthController(this);
+ mBiometricStrengthController.startListening();
}
/**
@@ -857,25 +982,36 @@
* 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
+ * @param userId the user to check for
+ * @param bundle passed from {@link BiometricPrompt}
+ * @param opPackageName see {@link android.app.AppOpsManager}
+ *
+ * @return 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.
+ *
+ * TODO(kchyn): Update this to handle DEVICE_CREDENTIAL better, reduce duplicate code in callers
*/
- private Pair<Integer, Integer> checkAndGetBiometricModality(int userId, String opPackageName) {
- // No biometric features, send error
- if (mAuthenticators.isEmpty()) {
- return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
+ private Pair<Integer, Integer> checkAndGetAuthenticators(int userId, Bundle bundle,
+ String opPackageName) throws RemoteException {
+ if (!Utils.isBiometricAllowed(bundle)
+ && Utils.isDeviceCredentialAllowed(bundle)
+ && !mTrustManager.isDeviceSecure(userId)) {
+ // If only device credential is being checked, and the user doesn't have one set up
+ return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL);
}
// Assuming that authenticators are listed in priority-order, the rest of this function
- // will go through and find the first authenticator that's available, enrolled, and enabled.
- // The tricky part is returning the correct error. Error strings that are modality-specific
- // should also respect the priority-order.
+ // will attempt to find the first authenticator that's as strong or stronger than the
+ // requested strength, available, enrolled, and enabled. The tricky part is returning the
+ // correct error. Error strings that are modality-specific should also respect the
+ // priority-order.
- // Find first authenticator that's detected, enrolled, and enabled.
+ // Find first authenticator that's strong enough, detected, enrolled, and enabled.
+ boolean hasSufficientStrength = false;
boolean isHardwareDetected = false;
boolean hasTemplatesEnrolled = false;
boolean enabledForApps = false;
@@ -883,13 +1019,16 @@
int modality = TYPE_NONE;
int firstHwAvailable = TYPE_NONE;
for (AuthenticatorWrapper authenticator : mAuthenticators) {
- modality = authenticator.modality;
- try {
+ final int actualStrength = authenticator.getActualStrength();
+ final int requestedStrength = Utils.getPublicBiometricStrength(bundle);
+ if (Utils.isAtLeastStrength(actualStrength, requestedStrength)) {
+ hasSufficientStrength = true;
+ modality = authenticator.modality;
if (authenticator.impl.isHardwareDetected(opPackageName)) {
isHardwareDetected = true;
if (firstHwAvailable == TYPE_NONE) {
- // Store the first one since we want to return the error in correct priority
- // order.
+ // Store the first one since we want to return the error in correct
+ // priority order.
firstHwAvailable = modality;
}
if (authenticator.impl.hasEnrolledTemplates(userId, opPackageName)) {
@@ -900,18 +1039,18 @@
}
}
}
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
}
}
- Slog.d(TAG, "checkAndGetBiometricModality: user=" + userId
+ Slog.d(TAG, "checkAndGetAuthenticators: user=" + userId
+ " isHardwareDetected=" + isHardwareDetected
+ " hasTemplatesEnrolled=" + hasTemplatesEnrolled
+ " enabledForApps=" + enabledForApps);
// Check error conditions
- if (!isHardwareDetected) {
+ if (!hasSufficientStrength) {
+ return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
+ } else if (!isHardwareDetected) {
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
@@ -1107,13 +1246,16 @@
// SystemUI handles transition from biometric to device credential.
mCurrentAuthSession.mState = STATE_SHOWING_DEVICE_CREDENTIAL;
mStatusBarService.onBiometricError(modality, error, vendorCode);
+ } else if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
+ mStatusBarService.hideAuthenticationDialog();
+ // TODO: If multiple authenticators are simultaneously running, this will
+ // need to be modified. Send the error to the client here, instead of doing
+ // a round trip to SystemUI.
+ mCurrentAuthSession.mClientReceiver.onError(modality, error, vendorCode);
+ mCurrentAuthSession = null;
} else {
mCurrentAuthSession.mState = STATE_ERROR_PENDING_SYSUI;
- if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
- mStatusBarService.hideAuthenticationDialog();
- } else {
- mStatusBarService.onBiometricError(modality, error, vendorCode);
- }
+ mStatusBarService.onBiometricError(modality, error, vendorCode);
}
} else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) {
// In the "try again" state, we should forward canceled errors to
@@ -1135,10 +1277,11 @@
// If any error is received while preparing the auth session (lockout, etc),
// and if device credential is allowed, just show the credential UI.
if (mPendingAuthSession.isAllowDeviceCredential()) {
- int authenticators = mPendingAuthSession.mBundle
- .getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, 0);
+ @Authenticators.Types int authenticators =
+ mPendingAuthSession.mBundle.getInt(
+ BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, 0);
// Disallow biometric and notify SystemUI to show the authentication prompt.
- authenticators &= ~Authenticator.TYPE_BIOMETRIC;
+ authenticators &= ~Authenticators.BIOMETRIC_WEAK;
mPendingAuthSession.mBundle.putInt(
BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED,
authenticators);
@@ -1355,31 +1498,43 @@
int callingUid, int callingPid, int callingUserId) {
mHandler.post(() -> {
- final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId,
- opPackageName);
- final int modality = result.first;
- final int error = result.second;
+ int modality = TYPE_NONE;
+ int result;
- final boolean credentialAllowed = Utils.isDeviceCredentialAllowed(bundle);
-
- if (error != BiometricConstants.BIOMETRIC_SUCCESS && credentialAllowed) {
- // If there's a problem but device credential is allowed, only show credential UI.
- bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED,
- Authenticator.TYPE_CREDENTIAL);
- } else if (error != BiometricConstants.BIOMETRIC_SUCCESS) {
- // Check for errors, notify callback, and return
- try {
- receiver.onError(modality, error, 0 /* vendorCode */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to send error", e);
- }
- return;
+ try {
+ final Pair<Integer, Integer> pair = checkAndGetAuthenticators(userId, bundle,
+ opPackageName);
+ modality = pair.first;
+ result = pair.second;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ result = BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
}
- // Start preparing for authentication. Authentication starts when
- // all modalities requested have invoked onReadyForAuthentication.
- authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
- callingUid, callingPid, callingUserId, modality);
+ try {
+ if (result == BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL) {
+ // If the app allowed device credential but the user hasn't set it up yet,
+ // return this error.
+ receiver.onError(modality, result, 0 /* vendorCode */);
+ } else if (result != BiometricConstants.BIOMETRIC_SUCCESS) {
+ if (Utils.isDeviceCredentialAllowed(bundle)) {
+ // If there's a problem with biometrics but device credential is allowed,
+ // only show credential UI.
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED,
+ Authenticators.DEVICE_CREDENTIAL);
+ authenticateInternal(token, sessionId, userId, receiver, opPackageName,
+ bundle, callingUid, callingPid, callingUserId, modality);
+ } else {
+ receiver.onError(modality, result, 0 /* vendorCode */);
+ }
+ } else {
+ // BIOMETRIC_SUCCESS, proceed to authentication
+ authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
+ callingUid, callingPid, callingUserId, modality);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
});
}
@@ -1407,7 +1562,8 @@
// 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;
- final int authenticators = bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED);
+ final @Authenticators.Types int authenticators = bundle.getInt(
+ BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, 0);
Slog.d(TAG, "Creating auth session. Modality: " + modality
+ ", cookie: " + cookie
+ ", authenticators: " + authenticators);
@@ -1415,7 +1571,7 @@
// If it's only device credential, we don't need to wait - LockSettingsService is
// always ready to check credential (SystemUI invokes that path).
- if ((authenticators & ~Authenticator.TYPE_CREDENTIAL) != 0) {
+ if ((authenticators & ~Authenticators.DEVICE_CREDENTIAL) != 0) {
modalities.put(modality, cookie);
}
mPendingAuthSession = new AuthSession(modalities, token, sessionId, userId,
@@ -1423,7 +1579,7 @@
modality, requireConfirmation);
try {
- if (authenticators == Authenticator.TYPE_CREDENTIAL) {
+ if (authenticators == Authenticators.DEVICE_CREDENTIAL) {
mPendingAuthSession.mState = STATE_SHOWING_DEVICE_CREDENTIAL;
mCurrentAuthSession = mPendingAuthSession;
mPendingAuthSession = null;
@@ -1438,9 +1594,13 @@
} else {
mPendingAuthSession.mState = STATE_AUTH_CALLED;
for (AuthenticatorWrapper authenticator : mAuthenticators) {
- authenticator.impl.prepareForAuthentication(requireConfirmation, token,
- sessionId, userId, mInternalReceiver, opPackageName, cookie, callingUid,
- callingPid, callingUserId);
+ // TODO(b/141025588): use ids instead of modalities to avoid ambiguity.
+ if (authenticator.modality == modality) {
+ authenticator.impl.prepareForAuthentication(requireConfirmation, token,
+ sessionId, userId, mInternalReceiver, opPackageName, cookie,
+ callingUid, callingPid, callingUserId);
+ break;
+ }
}
}
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/biometrics/BiometricStrengthController.java b/services/core/java/com/android/server/biometrics/BiometricStrengthController.java
new file mode 100644
index 0000000..4e16189
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/BiometricStrengthController.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics;
+
+import android.annotation.NonNull;
+import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import com.android.internal.os.BackgroundThread;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Class for maintaining and updating the strengths for biometric sensors. Strengths can only
+ * be downgraded from the device's default, and never upgraded.
+ */
+public class BiometricStrengthController implements DeviceConfig.OnPropertiesChangedListener {
+ private static final String TAG = "BiometricStrengthController";
+
+ private final BiometricService mService;
+
+ /**
+ * Flag stored in the DeviceConfig API: biometric modality strengths to downgrade.
+ * This is encoded as a key:value list, separated by comma, e.g.
+ *
+ * "id1:strength1,id2:strength2,id3:strength3"
+ *
+ * where strength is one of the values defined in
+ * {@link android.hardware.biometrics.Authenticators}
+ *
+ * Both id and strength should be int, otherwise Exception will be thrown when parsing and the
+ * downgrade will fail.
+ */
+ private static final String KEY_BIOMETRIC_STRENGTHS = "biometric_strengths";
+
+ /**
+ * Default (no-op) value of the flag KEY_BIOMETRIC_STRENGTHS
+ */
+ public static final String DEFAULT_BIOMETRIC_STRENGTHS = null;
+
+ BiometricStrengthController(@NonNull BiometricService service) {
+ mService = service;
+ }
+
+ void startListening() {
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_BIOMETRICS,
+ BackgroundThread.getExecutor(), this);
+ updateStrengths();
+ }
+
+ @Override
+ public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
+ for (String name : properties.getKeyset()) {
+ if (KEY_BIOMETRIC_STRENGTHS.equals(name)) {
+ updateStrengths();
+ }
+ }
+ }
+
+ /**
+ * Updates the strengths of authenticators in BiometricService if a matching ID's configuration
+ * has been changed.
+ */
+ private void updateStrengths() {
+ final Map<Integer, Integer> idToStrength = getIdToStrengthMap();
+ if (idToStrength == null) {
+ return;
+ }
+
+ for (BiometricService.AuthenticatorWrapper authenticator : mService.mAuthenticators) {
+ final int id = authenticator.id;
+ if (idToStrength.containsKey(id)) {
+ final int newStrength = idToStrength.get(id);
+ authenticator.updateStrength(newStrength);
+ }
+ }
+ }
+
+ /**
+ * @return a map of <ID, Strength>
+ */
+ private Map<Integer, Integer> getIdToStrengthMap() {
+ final String flags = DeviceConfig.getString(DeviceConfig.NAMESPACE_BIOMETRICS,
+ KEY_BIOMETRIC_STRENGTHS, DEFAULT_BIOMETRIC_STRENGTHS);
+ if (flags == null || flags.isEmpty()) {
+ Slog.d(TAG, "Flags are null or empty");
+ return null;
+ }
+
+ Map<Integer, Integer> map = new HashMap<>();
+ try {
+ for (String item : flags.split(",")) {
+ String[] elems = item.split(":");
+ final int id = Integer.parseInt(elems[0]);
+ final int strength = Integer.parseInt(elems[1]);
+ map.put(id, strength);
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Can't parse flag: " + flags);
+ map = null;
+ }
+ return map;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/SensorConfig.java b/services/core/java/com/android/server/biometrics/SensorConfig.java
new file mode 100644
index 0000000..9eda6da
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/SensorConfig.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics;
+
+/**
+ * Parsed sensor config. See core/res/res/values/config.xml config_biometric_sensors
+ */
+class SensorConfig {
+ final int mId;
+ final int mModality;
+ final int mStrength;
+
+ public SensorConfig(String config) {
+ String[] elems = config.split(":");
+ mId = Integer.parseInt(elems[0]);
+ mModality = Integer.parseInt(elems[1]);
+ mStrength = Integer.parseInt(elems[2]);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index ed5f9de..19f5358 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -16,15 +16,17 @@
package com.android.server.biometrics;
+import static android.hardware.biometrics.BiometricManager.Authenticators;
+
import android.content.Context;
-import android.hardware.biometrics.Authenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
import android.os.Build;
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
-
-import com.android.internal.annotations.VisibleForTesting;
+import android.util.Slog;
public class Utils {
public static boolean isDebugEnabled(Context context, int targetUserId) {
@@ -45,41 +47,167 @@
}
/**
- * Combine {@link BiometricPrompt#KEY_ALLOW_DEVICE_CREDENTIAL} with
- * {@link BiometricPrompt#KEY_AUTHENTICATORS_ALLOWED}, as the former is not flexible
- * enough.
+ * Combines {@link BiometricPrompt#KEY_ALLOW_DEVICE_CREDENTIAL} with
+ * {@link BiometricPrompt#KEY_AUTHENTICATORS_ALLOWED}, as the former is not flexible enough.
*/
public static void combineAuthenticatorBundles(Bundle bundle) {
- boolean biometricEnabled = true; // enabled by default
- boolean credentialEnabled = bundle.getBoolean(
- BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, false);
- if (bundle.get(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED) != null) {
- final int authenticatorFlags =
- bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED);
- biometricEnabled = (authenticatorFlags & Authenticator.TYPE_BIOMETRIC) != 0;
- // Using both KEY_ALLOW_DEVICE_CREDENTIAL and KEY_AUTHENTICATORS_ALLOWED together
- // is not supported. Default to overwriting.
- credentialEnabled = (authenticatorFlags & Authenticator.TYPE_CREDENTIAL) != 0;
- }
-
+ // Cache and remove explicit ALLOW_DEVICE_CREDENTIAL boolean flag from the bundle.
+ final boolean deviceCredentialAllowed =
+ bundle.getBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, false);
bundle.remove(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL);
- int authenticators = 0;
- if (biometricEnabled) {
- authenticators |= Authenticator.TYPE_BIOMETRIC;
+ final @Authenticators.Types int authenticators;
+ if (bundle.containsKey(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED)) {
+ // Ignore ALLOW_DEVICE_CREDENTIAL flag if AUTH_TYPES_ALLOWED is defined.
+ authenticators = bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, 0);
+ } else {
+ // Otherwise, use ALLOW_DEVICE_CREDENTIAL flag along with Weak+ biometrics by default.
+ authenticators = deviceCredentialAllowed
+ ? Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK
+ : Authenticators.BIOMETRIC_WEAK;
}
- if (credentialEnabled) {
- authenticators |= Authenticator.TYPE_CREDENTIAL;
- }
+
bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
}
/**
+ * @param authenticators composed of one or more values from {@link Authenticators}
+ * @return true if device credential is allowed.
+ */
+ public static boolean isDeviceCredentialAllowed(@Authenticators.Types int authenticators) {
+ return (authenticators & Authenticators.DEVICE_CREDENTIAL) != 0;
+ }
+
+ /**
* @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)}
- * @return true if device credential allowed.
+ * @return true if device credential is allowed.
*/
public static boolean isDeviceCredentialAllowed(Bundle bundle) {
+ return isDeviceCredentialAllowed(bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
+ }
+
+ /**
+ * Checks if any of the publicly defined strengths are set.
+ *
+ * @param authenticators composed of one or more values from {@link Authenticators}
+ * @return minimal allowed biometric strength or 0 if biometric authentication is not allowed.
+ */
+ public static int getPublicBiometricStrength(@Authenticators.Types int authenticators) {
+ // Only biometrics WEAK and above are allowed to integrate with the public APIs.
+ return authenticators & Authenticators.BIOMETRIC_WEAK;
+ }
+
+ /**
+ * Checks if any of the publicly defined strengths are set.
+ *
+ * @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)}
+ * @return minimal allowed biometric strength or 0 if biometric authentication is not allowed.
+ */
+ public static int getPublicBiometricStrength(Bundle bundle) {
+ return getPublicBiometricStrength(
+ bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
+ }
+
+ /**
+ * Checks if any of the publicly defined strengths are set.
+ *
+ * @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)}
+ * @return true if biometric authentication is allowed.
+ */
+ public static boolean isBiometricAllowed(Bundle bundle) {
+ return getPublicBiometricStrength(bundle) != 0;
+ }
+
+ /**
+ * @param sensorStrength the strength of the sensor
+ * @param requestedStrength the strength that it must meet
+ * @return true only if the sensor is at least as strong as the requested strength
+ */
+ public static boolean isAtLeastStrength(int sensorStrength, int requestedStrength) {
+ // If the authenticator contains bits outside of the requested strength, it is too weak.
+ return (~requestedStrength & sensorStrength) == 0;
+ }
+
+ /**
+ * Checks if the authenticator configuration is a valid combination of the public APIs
+ * @param bundle
+ * @return
+ */
+ public static boolean isValidAuthenticatorConfig(Bundle bundle) {
final int authenticators = bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED);
- return (authenticators & Authenticator.TYPE_CREDENTIAL) != 0;
+ return isValidAuthenticatorConfig(authenticators);
+ }
+
+ /**
+ * Checks if the authenticator configuration is a valid combination of the public APIs
+ * @param authenticators
+ * @return
+ */
+ public static boolean isValidAuthenticatorConfig(int authenticators) {
+ // The caller is not required to set the authenticators. But if they do, check the below.
+ if (authenticators == 0) {
+ return true;
+ }
+
+ // Check if any of the non-biometric and non-credential bits are set. If so, this is
+ // invalid.
+ final int testBits = ~(Authenticators.DEVICE_CREDENTIAL
+ | Authenticators.BIOMETRIC_MIN_STRENGTH);
+ if ((authenticators & testBits) != 0) {
+ Slog.e(BiometricService.TAG, "Non-biometric, non-credential bits found."
+ + " Authenticators: " + authenticators);
+ return false;
+ }
+
+ // Check that biometrics bits are either NONE, WEAK, or STRONG. If NONE, DEVICE_CREDENTIAL
+ // should be set.
+ final int biometricBits = authenticators & Authenticators.BIOMETRIC_MIN_STRENGTH;
+ if (biometricBits == Authenticators.EMPTY_SET
+ && isDeviceCredentialAllowed(authenticators)) {
+ return true;
+ } else if (biometricBits == Authenticators.BIOMETRIC_STRONG) {
+ return true;
+ } else if (biometricBits == Authenticators.BIOMETRIC_WEAK) {
+ return true;
+ }
+
+ Slog.e(BiometricService.TAG, "Unsupported biometric flags. Authenticators: "
+ + authenticators);
+ // Non-supported biometric flags are being used
+ return false;
+ }
+
+ /**
+ * Converts error codes from BiometricConstants, which are used in most of the internal plumbing
+ * and eventually returned to {@link BiometricPrompt.AuthenticationCallback} to public
+ * {@link BiometricManager} constants, which are used by APIs such as
+ * {@link BiometricManager#canAuthenticate(int)}
+ *
+ * @param biometricConstantsCode see {@link BiometricConstants}
+ * @return see {@link BiometricManager}
+ */
+ public static int biometricConstantsToBiometricManager(int biometricConstantsCode) {
+ final int biometricManagerCode;
+
+ switch (biometricConstantsCode) {
+ case BiometricConstants.BIOMETRIC_SUCCESS:
+ biometricManagerCode = BiometricManager.BIOMETRIC_SUCCESS;
+ break;
+ case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS:
+ case BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL:
+ biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED;
+ break;
+ case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE:
+ biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+ break;
+ case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
+ biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE;
+ break;
+ default:
+ Slog.e(BiometricService.TAG, "Unhandled result code: " + biometricConstantsCode);
+ biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+ break;
+ }
+ return biometricManagerCode;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index dceca0a..a54534b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -637,7 +637,7 @@
session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
mInstallThread.getLooper(), mStagingManager, sessionId, userId, callingUid,
installSource, params, createdMillis,
- stageDir, stageCid, false, false, false, null, SessionInfo.INVALID_ID,
+ stageDir, stageCid, null, false, false, false, null, SessionInfo.INVALID_ID,
false, false, false, SessionInfo.STAGED_SESSION_NO_ERROR, "");
synchronized (mSessions) {
@@ -1014,12 +1014,28 @@
}
}
+ static void sendPendingStreaming(Context context, IntentSender target, int sessionId,
+ Throwable cause) {
+ final Intent intent = new Intent();
+ intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+ intent.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_STREAMING);
+ if (cause != null && !TextUtils.isEmpty(cause.getMessage())) {
+ intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
+ "Staging Image Not Ready [" + cause.getMessage() + "]");
+ } else {
+ intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, "Staging Image Not Ready");
+ }
+ try {
+ target.sendIntent(context, 0, intent, null, null);
+ } catch (SendIntentException ignored) {
+ }
+ }
+
static void sendOnUserActionRequired(Context context, IntentSender target, int sessionId,
Intent intent) {
final Intent fillIn = new Intent();
fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
- fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_PENDING_USER_ACTION);
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_USER_ACTION);
fillIn.putExtra(Intent.EXTRA_INTENT, intent);
try {
target.sendIntent(context, 0, fillIn, null, null);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 518ec50..286d291 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -21,6 +21,7 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
+import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT;
import static android.content.pm.PackageParser.APEX_FILE_EXTENSION;
import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
@@ -30,11 +31,13 @@
import static com.android.internal.util.XmlUtils.readBitmapAttribute;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
+import static com.android.internal.util.XmlUtils.readByteArrayAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
import static com.android.internal.util.XmlUtils.readStringAttribute;
import static com.android.internal.util.XmlUtils.readUriAttribute;
import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
+import static com.android.internal.util.XmlUtils.writeByteArrayAttribute;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
@@ -123,6 +126,7 @@
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
@@ -142,6 +146,7 @@
/** XML constants used for persisting a session */
static final String TAG_SESSION = "session";
static final String TAG_CHILD_SESSION = "childSession";
+ static final String TAG_SESSION_FILE = "sessionFile";
private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
private static final String TAG_WHITELISTED_RESTRICTED_PERMISSION =
"whitelisted-restricted-permission";
@@ -183,6 +188,9 @@
private static final String ATTR_VOLUME_UUID = "volumeUuid";
private static final String ATTR_NAME = "name";
private static final String ATTR_INSTALL_REASON = "installRason";
+ private static final String ATTR_DATA_LOADER_PACKAGE_NAME = "dataLoaderPackageName";
+ private static final String ATTR_LENGTH_BYTES = "lengthBytes";
+ private static final String ATTR_METADATA = "metadata";
private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
private static final int[] EMPTY_CHILD_SESSION_ARRAY = {};
@@ -278,6 +286,29 @@
@GuardedBy("mLock")
private int mParentSessionId;
+ static class FileInfo {
+ public final String name;
+ public final Long lengthBytes;
+ public final byte[] metadata;
+
+ public static FileInfo added(String name, Long lengthBytes, byte[] metadata) {
+ return new FileInfo(name, lengthBytes, metadata);
+ }
+
+ public static FileInfo removed(String name) {
+ return new FileInfo(name, -1L, null);
+ }
+
+ FileInfo(String name, Long lengthBytes, byte[] metadata) {
+ this.name = name;
+ this.lengthBytes = lengthBytes;
+ this.metadata = metadata;
+ }
+ }
+
+ @GuardedBy("mLock")
+ private ArrayList<FileInfo> mFiles = new ArrayList<>();
+
@GuardedBy("mLock")
private boolean mStagedSessionApplied;
@GuardedBy("mLock")
@@ -313,6 +344,7 @@
@GuardedBy("mLock")
private boolean mVerityFound;
+ // TODO(b/146080380): merge file list with Callback installation.
private IncrementalFileStorages mIncrementalFileStorages;
private static final FileFilter sAddedFilter = new FileFilter() {
@@ -344,7 +376,9 @@
IntentSender statusReceiver;
switch (msg.what) {
case MSG_SEAL:
- handleSeal((IntentSender) msg.obj);
+ statusReceiver = (IntentSender) msg.obj;
+
+ handleSeal(statusReceiver);
break;
case MSG_COMMIT:
handleCommit();
@@ -378,6 +412,10 @@
}
};
+ private boolean isDataLoaderInstallation() {
+ return !TextUtils.isEmpty(params.dataLoaderPackageName);
+ }
+
/**
* @return {@code true} iff the installing is app an device owner or affiliated profile owner.
*/
@@ -435,7 +473,8 @@
PackageSessionProvider sessionProvider, Looper looper, StagingManager stagingManager,
int sessionId, int userId, int installerUid, @NonNull InstallSource installSource,
SessionParams params, long createdMillis,
- File stageDir, String stageCid, boolean prepared, boolean committed, boolean sealed,
+ File stageDir, String stageCid, FileInfo[] files, boolean prepared,
+ boolean committed, boolean sealed,
@Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
boolean isFailed, boolean isApplied, int stagedSessionErrorCode,
String stagedSessionErrorMessage) {
@@ -464,6 +503,12 @@
}
this.mParentSessionId = parentSessionId;
+ if (files != null) {
+ for (FileInfo file : files) {
+ mFiles.add(file);
+ }
+ }
+
if (!params.isMultiPackage && (stageDir == null) == (stageCid == null)) {
throw new IllegalArgumentException(
"Exactly one of stageDir or stageCid stage must be set");
@@ -592,15 +637,19 @@
}
}
+ @GuardedBy("mLock")
+ private void setClientProgressLocked(float progress) {
+ // Always publish first staging movement
+ final boolean forcePublish = (mClientProgress == 0);
+ mClientProgress = progress;
+ computeProgressLocked(forcePublish);
+ }
+
@Override
public void setClientProgress(float progress) {
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
-
- // Always publish first staging movement
- final boolean forcePublish = (mClientProgress == 0);
- mClientProgress = progress;
- computeProgressLocked(forcePublish);
+ setClientProgressLocked(progress);
}
}
@@ -608,8 +657,7 @@
public void addClientProgress(float progress) {
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
-
- setClientProgress(mClientProgress + progress);
+ setClientProgressLocked(mClientProgress + progress);
}
}
@@ -637,7 +685,10 @@
@GuardedBy("mLock")
private String[] getNamesLocked() {
- return stageDir.list();
+ if (!isDataLoaderInstallation()) {
+ return stageDir.list();
+ }
+ return mFiles.stream().map(fileInfo -> fileInfo.name).toArray(String[]::new);
}
private static File[] filterFiles(File parent, String[] names, FileFilter filter) {
@@ -659,6 +710,10 @@
@Override
public void removeSplit(String splitName) {
+ if (isDataLoaderInstallation()) {
+ throw new IllegalStateException(
+ "Cannot remove splits in a callback installation session.");
+ }
if (TextUtils.isEmpty(params.appPackageName)) {
throw new IllegalStateException("Must specify package name to remove a split");
}
@@ -693,8 +748,31 @@
}
}
+ private void assertCanWrite(boolean reverseMode) {
+ if (isDataLoaderInstallation()) {
+ throw new IllegalStateException(
+ "Cannot write regular files in a callback installation session.");
+ }
+ synchronized (mLock) {
+ assertCallerIsOwnerOrRootLocked();
+ assertPreparedAndNotSealedLocked("assertCanWrite");
+ }
+ if (reverseMode) {
+ switch (Binder.getCallingUid()) {
+ case android.os.Process.SHELL_UID:
+ case android.os.Process.ROOT_UID:
+ case android.os.Process.SYSTEM_UID:
+ break;
+ default:
+ throw new SecurityException(
+ "Reverse mode only supported from shell or system");
+ }
+ }
+ }
+
@Override
public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
+ assertCanWrite(false);
try {
return doWriteInternal(name, offsetBytes, lengthBytes, null);
} catch (IOException e) {
@@ -705,6 +783,7 @@
@Override
public void write(String name, long offsetBytes, long lengthBytes,
ParcelFileDescriptor fd) {
+ assertCanWrite(fd != null);
try {
doWriteInternal(name, offsetBytes, lengthBytes, fd);
} catch (IOException e) {
@@ -720,9 +799,6 @@
final RevocableFileDescriptor fd;
final FileBridge bridge;
synchronized (mLock) {
- assertCallerIsOwnerOrRootLocked();
- assertPreparedAndNotSealedLocked("openWrite");
-
if (PackageInstaller.ENABLE_REVOCABLE_FD) {
fd = new RevocableFileDescriptor();
bridge = null;
@@ -765,16 +841,6 @@
}
if (incomingFd != null) {
- switch (Binder.getCallingUid()) {
- case android.os.Process.SHELL_UID:
- case android.os.Process.ROOT_UID:
- case android.os.Process.SYSTEM_UID:
- break;
- default:
- throw new SecurityException(
- "Reverse mode only supported from shell or system");
- }
-
// In "reverse" mode, we're streaming data ourselves from the
// incoming FD, which means we never have to hand out our
// sensitive internal FD. We still rely on a "bridge" being
@@ -786,7 +852,10 @@
if (params.sizeBytes > 0) {
final long delta = progress - last.value;
last.value = progress;
- addClientProgress((float) delta / (float) params.sizeBytes);
+ synchronized (mLock) {
+ setClientProgressLocked(mClientProgress
+ + (float) delta / (float) params.sizeBytes);
+ }
}
});
} finally {
@@ -821,6 +890,10 @@
@Override
public ParcelFileDescriptor openRead(String name) {
+ if (isDataLoaderInstallation()) {
+ throw new IllegalStateException(
+ "Cannot read regular files in a callback installation session.");
+ }
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotCommittedOrDestroyedLocked("openRead");
@@ -926,6 +999,7 @@
return;
}
}
+
mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
}
@@ -988,6 +1062,14 @@
}
}
+ /** {@hide} */
+ private class StreamingException extends Exception {
+ StreamingException(Throwable cause) {
+ super(cause);
+ }
+ }
+
+
/**
* Sanity checks to make sure it's ok to commit the session.
*/
@@ -1039,12 +1121,22 @@
}
wasSealed = mSealed;
- if (!mSealed) {
+ try {
+ if (!mSealed) {
+ sealLocked(childSessions);
+ }
+
try {
- sealAndValidateLocked(childSessions);
- } catch (PackageManagerException e) {
+ streamAndValidateLocked();
+ } catch (StreamingException e) {
+ // In case of streaming failure we don't want to fail or commit the session.
+ // Just return from this method and allow caller to commit again.
+ PackageInstallerService.sendPendingStreaming(mContext, mRemoteStatusReceiver,
+ sessionId, e);
return false;
}
+ } catch (PackageManagerException e) {
+ return false;
}
// Client staging is fully done at this point
@@ -1131,17 +1223,26 @@
}
/**
- * Seal the session to prevent further modification and validate the contents of it.
+ * Convenience wrapper, see {@link #sealLocked(List<PackageInstallerSession>) seal} and
+ * {@link #streamAndValidateLocked()}.
+ */
+ @GuardedBy("mLock")
+ private void sealAndValidateLocked(List<PackageInstallerSession> childSessions)
+ throws PackageManagerException, StreamingException {
+ sealLocked(childSessions);
+ streamAndValidateLocked();
+ }
+
+ /**
+ * Seal the session to prevent further modification.
*
* <p>The session will be sealed after calling this method even if it failed.
*
- * @param childSessions the child sessions of a multipackage that will be checked for
- * consistency. Can be null if session is not multipackage.
* @throws PackageManagerException if the session was sealed but something went wrong. If the
* session was sealed this is the only possible exception.
*/
@GuardedBy("mLock")
- private void sealAndValidateLocked(List<PackageInstallerSession> childSessions)
+ private void sealLocked(List<PackageInstallerSession> childSessions)
throws PackageManagerException {
try {
assertNoWriteFileTransfersOpenLocked();
@@ -1152,7 +1253,26 @@
if (childSessions != null) {
assertMultiPackageConsistencyLocked(childSessions);
}
+ } catch (PackageManagerException e) {
+ throw onSessionVerificationFailure(e);
+ } catch (Throwable e) {
+ // Convert all exceptions into package manager exceptions as only those are handled
+ // in the code above.
+ throw onSessionVerificationFailure(new PackageManagerException(e));
+ }
+ }
+ /**
+ * Prepare DataLoader and stream content for DataLoader sessions.
+ * Validate the contents of all session.
+ *
+ * @throws StreamingException if streaming failed.
+ * @throws PackageManagerException if validation failed.
+ */
+ @GuardedBy("mLock")
+ private void streamAndValidateLocked()
+ throws PackageManagerException, StreamingException {
+ try {
// Read transfers from the original owner stay open, but as the session's data cannot
// be modified anymore, there is no leak of information. For staged sessions, further
// validation is performed by the staging manager.
@@ -1161,6 +1281,8 @@
params.appPackageName, PackageManager.GET_SIGNATURES
| PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
+ prepareDataLoader();
+
if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
validateApexInstallLocked();
} else {
@@ -1173,6 +1295,8 @@
}
} catch (PackageManagerException e) {
throw onSessionVerificationFailure(e);
+ } catch (StreamingException e) {
+ throw e;
} catch (Throwable e) {
// Convert all exceptions into package manager exceptions as only those are handled
// in the code above.
@@ -1208,6 +1332,8 @@
synchronized (mLock) {
try {
sealAndValidateLocked(childSessions);
+ } catch (StreamingException e) {
+ Slog.e(TAG, "Streaming failed", e);
} catch (PackageManagerException e) {
Slog.e(TAG, "Package not valid", e);
}
@@ -1277,6 +1403,8 @@
try {
sealAndValidateLocked(childSessions);
+ } catch (StreamingException e) {
+ throw new IllegalArgumentException("Streaming failed", e);
} catch (PackageManagerException e) {
throw new IllegalArgumentException("Package is not valid", e);
}
@@ -1517,9 +1645,10 @@
mInternalProgress = 0.5f;
computeProgressLocked(true);
- // Unpack native libraries
- // TODO(b/136132412): skip for incremental installation
- extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs());
+ // Unpack native libraries for non-incremental installation
+ if (params.incrementalParams == null) {
+ extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs());
+ }
}
// We've reached point of no return; call into PMS to install the stage.
@@ -2223,6 +2352,132 @@
}
@Override
+ public void addFile(String name, long lengthBytes, byte[] metadata) {
+ if (mIncrementalFileStorages != null) {
+ try {
+ mIncrementalFileStorages.addFile(new InstallationFile(name, lengthBytes, metadata));
+ } catch (IOException ex) {
+ throw new IllegalStateException(
+ "Failed to add and configure Incremental File: " + name, ex);
+ }
+ }
+ if (!isDataLoaderInstallation()) {
+ throw new IllegalStateException(
+ "Cannot add files to non-callback installation session.");
+ }
+ // Use installer provided name for now; we always rename later
+ if (!FileUtils.isValidExtFilename(name)) {
+ throw new IllegalArgumentException("Invalid name: " + name);
+ }
+
+ synchronized (mLock) {
+ assertCallerIsOwnerOrRootLocked();
+ assertPreparedAndNotSealedLocked("addFile");
+
+ mFiles.add(FileInfo.added(name, lengthBytes, metadata));
+ }
+ }
+
+ @Override
+ public void removeFile(String name) {
+ if (!isDataLoaderInstallation()) {
+ throw new IllegalStateException(
+ "Cannot add files to non-callback installation session.");
+ }
+ if (TextUtils.isEmpty(params.appPackageName)) {
+ throw new IllegalStateException("Must specify package name to remove a split");
+ }
+
+ synchronized (mLock) {
+ assertCallerIsOwnerOrRootLocked();
+ assertPreparedAndNotSealedLocked("removeFile");
+
+ mFiles.add(FileInfo.removed(getRemoveMarkerName(name)));
+ }
+ }
+
+ /**
+ * Makes sure files are present in staging location.
+ */
+ private void prepareDataLoader()
+ throws PackageManagerException, StreamingException {
+ if (!isDataLoaderInstallation()) {
+ return;
+ }
+
+ FilesystemConnector connector = new FilesystemConnector();
+
+ FileInfo[] addedFiles = mFiles.stream().filter(
+ file -> sAddedFilter.accept(new File(file.name))).toArray(FileInfo[]::new);
+ String[] removedFiles = mFiles.stream().filter(
+ file -> sRemovedFilter.accept(new File(file.name))).map(
+ file -> file.name.substring(0,
+ file.name.length() - REMOVE_MARKER_EXTENSION.length())).toArray(
+ String[]::new);
+
+ DataLoader dataLoader = new DataLoader();
+ try {
+ dataLoader.onCreate(connector);
+
+ if (!dataLoader.onPrepareImage(addedFiles, removedFiles)) {
+ throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
+ "Failed to prepare image.");
+ }
+ } catch (IOException e) {
+ throw new StreamingException(e);
+ } finally {
+ dataLoader.onDestroy();
+ }
+ }
+
+ // TODO(b/146080380): implement DataLoader using Incremental infrastructure.
+ class FilesystemConnector {
+ void writeData(FileInfo fileInfo, long offset, long lengthBytes,
+ ParcelFileDescriptor incomingFd) throws IOException {
+ doWriteInternal(fileInfo.name, offset, lengthBytes, incomingFd);
+ }
+ }
+
+ static class DataLoader {
+ private ParcelFileDescriptor mInFd = null;
+ private FilesystemConnector mConnector = null;
+
+ void onCreate(FilesystemConnector connector) throws IOException {
+ mConnector = connector;
+ }
+
+ void onDestroy() {
+ IoUtils.closeQuietly(mInFd);
+ }
+
+ private static final String STDIN_PATH = "-";
+ boolean onPrepareImage(FileInfo[] addedFiles, String[] removedFiles) throws IOException {
+ for (FileInfo fileInfo : addedFiles) {
+ String filePath = new String(fileInfo.metadata, StandardCharsets.UTF_8);
+ if (STDIN_PATH.equals(filePath) || TextUtils.isEmpty(filePath)) {
+ if (mInFd == null) {
+ Slog.e(TAG, "Invalid stdin file descriptor.");
+ return false;
+ }
+ ParcelFileDescriptor inFd = ParcelFileDescriptor.dup(mInFd.getFileDescriptor());
+ mConnector.writeData(fileInfo, 0, fileInfo.lengthBytes, inFd);
+ } else {
+ File localFile = new File(filePath);
+ ParcelFileDescriptor incomingFd = null;
+ try {
+ incomingFd = ParcelFileDescriptor.open(localFile,
+ ParcelFileDescriptor.MODE_READ_ONLY);
+ mConnector.writeData(fileInfo, 0, localFile.length(), incomingFd);
+ } finally {
+ IoUtils.closeQuietly(incomingFd);
+ }
+ }
+ }
+ return true;
+ }
+ }
+
+ @Override
public int[] getChildSessionIds() {
final int[] childSessionIds = mChildSessionIds.copyKeys();
if (childSessionIds != null) {
@@ -2294,20 +2549,6 @@
return mParentSessionId;
}
- @Override
- public void addFile(@NonNull String name, long size, @NonNull byte[] metadata) {
- if (mIncrementalFileStorages == null) {
- throw new IllegalStateException(
- "Cannot add Incremental File to a non-Incremental session.");
- }
- try {
- mIncrementalFileStorages.addFile(new InstallationFile(name, size, metadata));
- } catch (IOException ex) {
- throw new IllegalStateException(
- "Failed to add and configure Incremental File: " + name, ex);
- }
- }
-
private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
final IntentSender statusReceiver;
final String packageName;
@@ -2320,7 +2561,7 @@
}
if (statusReceiver != null) {
- // Execute observer.onPackageInstalled on different tread as we don't want callers
+ // Execute observer.onPackageInstalled on different thread as we don't want callers
// inside the system server have to worry about catching the callbacks while they are
// calling into the session
final SomeArgs args = SomeArgs.obtain();
@@ -2594,6 +2835,8 @@
writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason);
+ writeStringAttribute(out, ATTR_DATA_LOADER_PACKAGE_NAME, params.dataLoaderPackageName);
+
writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions);
writeWhitelistedRestrictedPermissionsLocked(out,
params.whitelistedRestrictedPermissions);
@@ -2623,6 +2866,13 @@
writeIntAttribute(out, ATTR_SESSION_ID, childSessionId);
out.endTag(null, TAG_CHILD_SESSION);
}
+ for (FileInfo fileInfo : mFiles) {
+ out.startTag(null, TAG_SESSION_FILE);
+ writeStringAttribute(out, ATTR_NAME, fileInfo.name);
+ writeLongAttribute(out, ATTR_LENGTH_BYTES, fileInfo.lengthBytes);
+ writeByteArrayAttribute(out, ATTR_METADATA, fileInfo.metadata);
+ out.endTag(null, TAG_SESSION_FILE);
+ }
}
out.endTag(null, TAG_SESSION);
@@ -2696,6 +2946,8 @@
params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON);
+ params.dataLoaderPackageName = readStringAttribute(in, ATTR_DATA_LOADER_PACKAGE_NAME);
+
final File appIconFile = buildAppIconFile(sessionId, sessionsDir);
if (appIconFile.exists()) {
params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
@@ -2722,6 +2974,7 @@
List<String> grantedRuntimePermissions = new ArrayList<>();
List<String> whitelistedRestrictedPermissions = new ArrayList<>();
List<Integer> childSessionIds = new ArrayList<>();
+ List<FileInfo> files = new ArrayList<>();
int outerDepth = in.getDepth();
int type;
while ((type = in.next()) != XmlPullParser.END_DOCUMENT
@@ -2739,6 +2992,11 @@
if (TAG_CHILD_SESSION.equals(in.getName())) {
childSessionIds.add(readIntAttribute(in, ATTR_SESSION_ID, SessionInfo.INVALID_ID));
}
+ if (TAG_SESSION_FILE.equals(in.getName())) {
+ files.add(new FileInfo(readStringAttribute(in, ATTR_NAME),
+ readLongAttribute(in, ATTR_LENGTH_BYTES, -1),
+ readByteArrayAttribute(in, ATTR_METADATA)));
+ }
}
if (grantedRuntimePermissions.size() > 0) {
@@ -2757,11 +3015,16 @@
childSessionIdsArray = EMPTY_CHILD_SESSION_ARRAY;
}
+ FileInfo[] fileInfosArray = null;
+ if (!files.isEmpty()) {
+ fileInfosArray = files.stream().toArray(FileInfo[]::new);
+ }
+
InstallSource installSource = InstallSource.create(installInitiatingPackageName,
installOriginatingPackageName, installerPackageName, false);
return new PackageInstallerSession(callback, context, pm, sessionProvider,
installerThread, stagingManager, sessionId, userId, installerUid,
- installSource, params, createdMillis, stageDir, stageCid,
+ installSource, params, createdMillis, stageDir, stageCid, fileInfosArray,
prepared, committed, sealed, childSessionIdsArray, parentSessionId,
isReady, isFailed, isApplied, stagedSessionErrorCode, stagedSessionErrorMessage);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 17b1daf..2d7bcd0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -14608,7 +14608,8 @@
return false;
}
- if (!SELinux.restoreconRecursive(afterCodeFile)) {
+ //TODO(b/136132412): enable selinux restorecon for incremental directories
+ if (!onIncremental && !SELinux.restoreconRecursive(afterCodeFile)) {
Slog.w(TAG, "Failed to restorecon");
return false;
}
@@ -22747,6 +22748,12 @@
}
@Override
+ public ComponentName getSystemUiServiceComponent() {
+ return ComponentName.unflattenFromString(mContext.getResources().getString(
+ com.android.internal.R.string.config_systemUIServiceComponent));
+ }
+
+ @Override
public void setDeviceAndProfileOwnerPackages(
int deviceOwnerUserId, String deviceOwnerPackage,
SparseArray<String> profileOwnerPackages) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index fff404f..dfffbd6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -114,6 +114,7 @@
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -135,6 +136,8 @@
private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/";
private static final int DEFAULT_WAIT_MS = 60 * 1000;
+ private static final String PM_SHELL_DATALOADER = "com.android.pm.dataloader";
+
final IPackageManager mInterface;
final IPermissionManager mPermissionManager;
final private WeakHashMap<String, Resources> mResourceCache =
@@ -175,6 +178,8 @@
return runQueryIntentReceivers();
case "install":
return runInstall();
+ case "install-streaming":
+ return runStreamingInstall();
case "install-abandon":
case "install-destroy":
return runInstallAbandon();
@@ -1152,9 +1157,21 @@
return 0;
}
- private int runInstall() throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
+ private int runStreamingInstall() throws RemoteException {
final InstallParams params = makeInstallParams();
+ if (TextUtils.isEmpty(params.sessionParams.dataLoaderPackageName)) {
+ params.sessionParams.setDataLoaderPackageName(PM_SHELL_DATALOADER);
+ }
+ return doRunInstall(params);
+ }
+
+ private int runInstall() throws RemoteException {
+ return doRunInstall(makeInstallParams());
+ }
+
+ private int doRunInstall(final InstallParams params) throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final boolean streaming = !TextUtils.isEmpty(params.sessionParams.dataLoaderPackageName);
ArrayList<String> inPaths = getRemainingArgs();
if (inPaths.isEmpty()) {
@@ -1181,17 +1198,30 @@
return 1;
}
- setParamsSize(params, inPaths);
+ if (!streaming) {
+ setParamsSize(params, inPaths);
+ }
+
final int sessionId = doCreateSession(params.sessionParams,
params.installerPackageName, params.userId);
boolean abandonSession = true;
try {
for (String inPath : inPaths) {
- String splitName = hasSplits ? (new File(inPath)).getName()
- : "base." + (isApex ? "apex" : "apk");
- if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, splitName,
- false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
- return 1;
+ if (streaming) {
+ String name = new File(inPath).getName();
+ byte[] metadata = inPath.getBytes(StandardCharsets.UTF_8);
+ if (doAddFile(sessionId, name, params.sessionParams.sizeBytes, metadata,
+ false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
+ return 1;
+ }
+ } else {
+ String splitName = hasSplits ? new File(inPath).getName()
+ : "base." + (isApex ? "apex" : "apk");
+
+ if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, splitName,
+ false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
+ return 1;
+ }
}
}
if (doCommitSession(sessionId, false /*logSuccess*/)
@@ -2927,11 +2957,32 @@
return sessionId;
}
+ private int doAddFile(int sessionId, String name, long sizeBytes, byte[] metadata,
+ boolean logSuccess) throws RemoteException {
+ PackageInstaller.Session session = new PackageInstaller.Session(
+ mInterface.getPackageInstaller().openSession(sessionId));
+ try {
+ session.addFile(name, sizeBytes, metadata);
+
+ if (logSuccess) {
+ getOutPrintWriter().println("Success");
+ }
+
+ return 0;
+ } finally {
+ IoUtils.closeQuietly(session);
+ }
+ }
+
private int doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName,
boolean logSuccess) throws RemoteException {
PackageInstaller.Session session = null;
try {
+ session = new PackageInstaller.Session(
+ mInterface.getPackageInstaller().openSession(sessionId));
+
final PrintWriter pw = getOutPrintWriter();
+
final ParcelFileDescriptor fd;
if (STDIN_PATH.equals(inPath)) {
fd = ParcelFileDescriptor.dup(getInFileDescriptor());
@@ -2953,8 +3004,6 @@
return 1;
}
- session = new PackageInstaller.Session(
- mInterface.getPackageInstaller().openSession(sessionId));
session.write(splitName, 0, sizeBytes, fd);
if (logSuccess) {
@@ -3000,7 +3049,6 @@
try {
session = new PackageInstaller.Session(
mInterface.getPackageInstaller().openSession(sessionId));
-
for (String splitName : splitNames) {
session.removeSplit(splitName);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 6de9dbd..6bfa1ae 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1811,7 +1811,7 @@
} else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
} else if (taskSwitch && allowTaskSnapshot) {
- return snapshot == null ? STARTING_WINDOW_TYPE_NONE
+ return snapshot == null ? STARTING_WINDOW_TYPE_SPLASH_SCREEN
: snapshotOrientationSameAsTask(snapshot) || fromRecents
? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
} else {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 677d2a1..6f124ac 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -3594,7 +3594,10 @@
pw.println(prefix + "* " + task);
task.dump(pw, prefix + " ");
final ArrayList<ActivityRecord> activities = new ArrayList<>();
- forAllActivities((Consumer<ActivityRecord>) activities::add);
+ // Add activities by traversing the hierarchy from bottom to top, since activities
+ // are dumped in reverse order in {@link ActivityStackSupervisor#dumpHistoryList()}.
+ forAllActivities((Consumer<ActivityRecord>) activities::add,
+ false /* traverseTopToBottom */);
dumpHistoryList(fd, pw, activities, prefix, "Hist", true, !dumpAll, dumpClient,
dumpPackage, false, null, task);
});
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 43dce73..26d9dbc 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -2527,8 +2527,8 @@
void scheduleUpdatePictureInPictureModeIfNeeded(Task task, ActivityStack prevStack) {
final ActivityStack stack = task.getStack();
- if (prevStack == null || prevStack == stack
- || (!prevStack.inPinnedWindowingMode() && !stack.inPinnedWindowingMode())) {
+ if ((prevStack == null || (prevStack != stack
+ && !prevStack.inPinnedWindowingMode() && !stack.inPinnedWindowingMode()))) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index eab8d05..6918c96 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2149,13 +2149,27 @@
return false;
}
- final int fl = mAttrs.flags & (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
- final int type = mAttrs.type;
+ if (PixelFormat.formatHasAlpha(mAttrs.format)) {
+ // Support legacy use cases where transparent windows can still be ime target with
+ // FLAG_NOT_FOCUSABLE and ALT_FOCUSABLE_IM set.
+ // Certain apps listen for IME insets using transparent windows and ADJUST_NOTHING to
+ // manually synchronize app content to IME animation b/144619551.
+ // TODO(b/145812508): remove this once new focus management is complete b/141738570
+ final int fl = mAttrs.flags & (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
+ final int type = mAttrs.type;
- // Can only be an IME target if both FLAG_NOT_FOCUSABLE and FLAG_ALT_FOCUSABLE_IM are set or
- // both are cleared...and not a starting window.
- if (fl != 0 && fl != (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM)
- && type != TYPE_APPLICATION_STARTING) {
+ // Can only be an IME target if both FLAG_NOT_FOCUSABLE and FLAG_ALT_FOCUSABLE_IM are
+ // set or both are cleared...and not a starting window.
+ if (fl != 0 && fl != (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM)
+ && type != TYPE_APPLICATION_STARTING) {
+ return false;
+ }
+ } else if (!WindowManager.LayoutParams.mayUseInputMethod(mAttrs.flags)
+ || mAttrs.type == TYPE_APPLICATION_STARTING) {
+ // Can be an IME target only if:
+ // 1. FLAG_NOT_FOCUSABLE is not set
+ // 2. FLAG_ALT_FOCUSABLE_IM is not set
+ // 3. not a starting window.
return false;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index eb1753b..aa7bf5b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -7440,7 +7440,7 @@
}
/**
- * Returns whether or auto time is used on the device or not.
+ * Returns whether auto time is used on the device or not.
*/
@Override
public boolean getAutoTime(ComponentName who) {
@@ -7453,6 +7453,42 @@
return mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) > 0;
}
+ /**
+ * Set whether auto time zone is enabled on the device.
+ */
+ @Override
+ public void setAutoTimeZone(ComponentName who, boolean enabled) {
+ if (!mHasFeature) {
+ return;
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ // TODO (b/145286957) Refactor security checks
+ enforceDeviceOwnerOrProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
+
+ mInjector.binderWithCleanCallingIdentity(() ->
+ mInjector.settingsGlobalPutInt(Global.AUTO_TIME_ZONE, enabled ? 1 : 0));
+
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_AUTO_TIME_ZONE)
+ .setAdmin(who)
+ .setBoolean(enabled)
+ .write();
+ }
+
+ /**
+ * Returns whether auto time zone is used on the device or not.
+ */
+ @Override
+ public boolean getAutoTimeZone(ComponentName who) {
+ if (!mHasFeature) {
+ return false;
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ enforceDeviceOwnerOrProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
+
+ return mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) > 0;
+ }
+
@Override
public void setForceEphemeralUsers(ComponentName who, boolean forceEphemeralUsers) {
if (!mHasFeature) {
@@ -13737,8 +13773,9 @@
} else {
deviceOwner.lastNetworkLoggingNotificationTimeMs = now;
}
+ final PackageManagerInternal pm = mInjector.getPackageManagerInternal();
final Intent intent = new Intent(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG);
- intent.setPackage("com.android.systemui");
+ intent.setPackage(pm.getSystemUiServiceComponent().getPackageName());
final PendingIntent pendingIntent = PendingIntent.getBroadcastAsUser(mContext, 0, intent, 0,
UserHandle.CURRENT);
Notification notification =
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 401a094..f3ac7d6 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -30,12 +30,12 @@
import android.app.AppCompatCallbacks;
import android.app.INotificationManager;
import android.app.usage.UsageStatsManagerInternal;
-import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.res.Configuration;
import android.content.res.Resources.Theme;
import android.database.sqlite.SQLiteCompatibilityWalFlags;
@@ -2395,9 +2395,9 @@
}
private static void startSystemUi(Context context, WindowManagerService windowManager) {
+ PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
Intent intent = new Intent();
- intent.setComponent(new ComponentName("com.android.systemui",
- "com.android.systemui.SystemUIService"));
+ intent.setComponent(pm.getSystemUiServiceComponent());
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
//Slog.d(TAG, "Starting service: " + intent);
context.startServiceAsUser(intent, UserHandle.SYSTEM);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index 106a723..d38c80c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -20,6 +20,7 @@
import static junit.framework.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -65,8 +66,16 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
+ // Dummy test config
+ final String[] config = {
+ "0:2:15", // ID0:Fingerprint:Strong
+ "1:4:15", // ID1:Iris:Strong
+ "2:8:15", // ID2:Face:Strong
+ };
+
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mInjector.getBiometricService()).thenReturn(mBiometricService);
+ when(mInjector.getConfiguration(any())).thenReturn(config);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
.thenReturn(true);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IRIS)).thenReturn(true);
@@ -76,8 +85,7 @@
// TODO(b/141025588): Check that an exception is thrown when the userId != callingUserId
@Test
- public void testAuthenticate_callsBiometricServiceAuthenticate() throws
- Exception {
+ public void testAuthenticate_callsBiometricServiceAuthenticate() throws Exception {
mAuthService = new AuthService(mContext, mInjector);
mAuthService.onStart();
@@ -104,22 +112,25 @@
}
@Test
- public void testCanAuthenticate_callsBiometricServiceCanAuthenticate() throws
- Exception {
+ public void testCanAuthenticate_callsBiometricServiceCanAuthenticate() throws Exception {
mAuthService = new AuthService(mContext, mInjector);
mAuthService.onStart();
final int userId = 0;
final int expectedResult = BIOMETRIC_SUCCESS;
- when(mBiometricService.canAuthenticate(anyString(), anyInt())).thenReturn(expectedResult);
+ final int authenticators = 0;
+ when(mBiometricService.canAuthenticate(anyString(), anyInt(), anyInt()))
+ .thenReturn(expectedResult);
- final int result = mAuthService.mImpl.canAuthenticate(TEST_OP_PACKAGE_NAME, userId);
+ final int result = mAuthService.mImpl
+ .canAuthenticate(TEST_OP_PACKAGE_NAME, userId, authenticators);
assertEquals(expectedResult, result);
waitForIdle();
verify(mBiometricService).canAuthenticate(
eq(TEST_OP_PACKAGE_NAME),
- eq(userId));
+ eq(userId),
+ eq(authenticators));
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 4ced421..211fc4d 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -16,12 +16,14 @@
package com.android.server.biometrics;
+import static android.hardware.biometrics.BiometricManager.Authenticators;
+
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
import static junit.framework.TestCase.assertNotNull;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -34,12 +36,13 @@
import static org.mockito.Mockito.when;
import android.app.IActivityManager;
+import android.app.trust.ITrustManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
-import android.hardware.biometrics.Authenticator;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricService;
@@ -81,8 +84,6 @@
private static final String FINGERPRINT_ACQUIRED_SENSOR_DIRTY = "sensor_dirty";
- private static final int STRENGTH_STRONG = 1;
-
private BiometricService mBiometricService;
@Mock
@@ -101,6 +102,8 @@
IBiometricAuthenticator mFingerprintAuthenticator;
@Mock
IBiometricAuthenticator mFaceAuthenticator;
+ @Mock
+ ITrustManager mTrustManager;
@Before
public void setUp() {
@@ -111,10 +114,13 @@
when(mInjector.getActivityManagerService()).thenReturn(mock(IActivityManager.class));
when(mInjector.getStatusBarService()).thenReturn(mock(IStatusBarService.class));
- when(mInjector.getSettingObserver(any(), any(), any())).thenReturn(
- mock(BiometricService.SettingObserver.class));
+ when(mInjector.getSettingObserver(any(), any(), any()))
+ .thenReturn(mock(BiometricService.SettingObserver.class));
when(mInjector.getKeyStore()).thenReturn(mock(KeyStore.class));
when(mInjector.isDebugEnabled(any(), anyInt())).thenReturn(false);
+ when(mInjector.getBiometricStrengthController(any()))
+ .thenReturn(mock(BiometricStrengthController.class));
+ when(mInjector.getTrustManager()).thenReturn(mTrustManager);
when(mResources.getString(R.string.biometric_error_hw_unavailable))
.thenReturn(ERROR_HW_UNAVAILABLE);
@@ -122,6 +128,56 @@
.thenReturn(ERROR_NOT_RECOGNIZED);
when(mResources.getString(R.string.biometric_error_user_canceled))
.thenReturn(ERROR_USER_CANCELED);
+
+ final String[] config = {
+ "0:2:15", // ID0:Fingerprint:Strong
+ "1:8:15", // ID1:Face:Strong
+ "2:4:255", // ID2:Iris:Weak
+ };
+
+ when(mInjector.getConfiguration(any())).thenReturn(config);
+ }
+
+ @Test
+ public void testAuthenticate_credentialAllowedButNotSetup_returnsNoDeviceCredential()
+ throws Exception {
+ when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(false);
+
+ mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService.onStart();
+
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
+ Authenticators.DEVICE_CREDENTIAL);
+ waitForIdle();
+ verify(mReceiver1).onError(
+ eq(BiometricAuthenticator.TYPE_NONE),
+ eq(BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL),
+ eq(0 /* vendorCode */));
+ }
+
+ @Test
+ public void testAuthenticate_credentialAllowedAndSetup_callsSystemUI() throws Exception {
+ // When no biometrics are enrolled, but credentials are set up, status bar should be
+ // invoked right away with showAuthenticationDialog
+
+ when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+
+ mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService.onStart();
+
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
+ Authenticators.DEVICE_CREDENTIAL);
+ waitForIdle();
+
+ assertNull(mBiometricService.mPendingAuthSession);
+ // StatusBar showBiometricDialog invoked
+ verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
+ eq(mBiometricService.mCurrentAuthSession.mBundle),
+ any(IBiometricServiceReceiverInternal.class),
+ eq(0),
+ anyBoolean() /* requireConfirmation */,
+ anyInt() /* userId */,
+ eq(TEST_PACKAGE_NAME));
}
@Test
@@ -131,7 +187,7 @@
mBiometricService.onStart();
invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
- false /* allowDeviceCredential */);
+ null /* authenticators */);
waitForIdle();
verify(mReceiver1).onError(
eq(BiometricAuthenticator.TYPE_NONE),
@@ -145,12 +201,12 @@
mBiometricService = new BiometricService(mContext, mInjector);
mBiometricService.onStart();
- mBiometricService.mImpl.registerAuthenticator(0 /* id */, STRENGTH_STRONG,
- BiometricAuthenticator.TYPE_FINGERPRINT, mFingerprintAuthenticator);
-
+ mBiometricService.mImpl.registerAuthenticator(0 /* id */,
+ BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
+ mFingerprintAuthenticator);
invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
- false /* allowDeviceCredential */);
+ null /* authenticators */);
waitForIdle();
verify(mReceiver1).onError(
eq(BiometricAuthenticator.TYPE_FINGERPRINT),
@@ -159,6 +215,54 @@
}
@Test
+ public void testAuthenticate_notStrongEnough_returnsHardwareNotPresent() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_WEAK);
+
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
+ Authenticators.BIOMETRIC_STRONG);
+ waitForIdle();
+ verify(mReceiver1).onError(
+ eq(BiometricAuthenticator.TYPE_NONE),
+ eq(BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT),
+ eq(0 /* vendorCode */));
+ }
+
+ @Test
+ public void testAuthenticate_picksStrongIfAvailable() throws Exception {
+ // If both strong and weak are available, and the caller requires STRONG, authentication
+ // is able to proceed.
+
+ final int[] modalities = new int[] {
+ BiometricAuthenticator.TYPE_FINGERPRINT,
+ BiometricAuthenticator.TYPE_FACE,
+ };
+
+ final int[] strengths = new int[] {
+ Authenticators.BIOMETRIC_WEAK,
+ Authenticators.BIOMETRIC_STRONG,
+ };
+
+ setupAuthForMultiple(modalities, strengths);
+
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */, Authenticators.BIOMETRIC_STRONG);
+ waitForIdle();
+ verify(mReceiver1, never()).onError(
+ anyInt(),
+ anyInt(),
+ anyInt() /* vendorCode */);
+
+ // StatusBar showBiometricDialog invoked with face, which was set up to be STRONG
+ verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
+ eq(mBiometricService.mCurrentAuthSession.mBundle),
+ any(IBiometricServiceReceiverInternal.class),
+ eq(BiometricAuthenticator.TYPE_FACE),
+ eq(false) /* requireConfirmation */,
+ anyInt() /* userId */,
+ eq(TEST_PACKAGE_NAME));
+ }
+
+ @Test
public void testAuthenticate_whenHalIsDead_returnsErrorHardwareUnavailable() throws
Exception {
when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
@@ -166,11 +270,12 @@
mBiometricService = new BiometricService(mContext, mInjector);
mBiometricService.onStart();
- mBiometricService.mImpl.registerAuthenticator(0 /* id */, STRENGTH_STRONG,
- BiometricAuthenticator.TYPE_FINGERPRINT, mFingerprintAuthenticator);
+ mBiometricService.mImpl.registerAuthenticator(0 /* id */,
+ BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
+ mFingerprintAuthenticator);
invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
- false /* allowDeviceCredential */);
+ null /* authenticators */);
waitForIdle();
verify(mReceiver1).onError(
eq(BiometricAuthenticator.TYPE_NONE),
@@ -181,18 +286,12 @@
@Test
public void testAuthenticateFace_respectsUserSetting()
throws Exception {
- when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
- when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true);
-
- mBiometricService = new BiometricService(mContext, mInjector);
- mBiometricService.onStart();
- mBiometricService.mImpl.registerAuthenticator(0 /* id */, STRENGTH_STRONG,
- BiometricAuthenticator.TYPE_FACE, mFaceAuthenticator);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
// Disabled in user settings receives onError
when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(false);
invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
- false /* allowDeviceCredential */);
+ null /* authenticators */);
waitForIdle();
verify(mReceiver1).onError(
eq(BiometricAuthenticator.TYPE_NONE),
@@ -205,7 +304,7 @@
when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt()))
.thenReturn(true);
invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
- false /* allowDeviceCredential */);
+ null /* authenticators */);
waitForIdle();
verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
verify(mBiometricService.mAuthenticators.get(0).impl).prepareForAuthentication(
@@ -225,7 +324,7 @@
when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt()))
.thenReturn(false);
invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
- false /* allowDeviceCredential */);
+ null /* authenticators */);
waitForIdle();
verify(mBiometricService.mAuthenticators.get(0).impl).prepareForAuthentication(
eq(false) /* requireConfirmation */,
@@ -242,11 +341,11 @@
@Test
public void testAuthenticate_happyPathWithoutConfirmation() throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
// Start testing the happy path
invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
- false /* allowDeviceCredential */);
+ null /* authenticators */);
waitForIdle();
// Creates a pending auth session with the correct initial states
@@ -314,15 +413,16 @@
@Test
public void testAuthenticate_noBiometrics_credentialAllowed() throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(false);
invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
- true /* requireConfirmation */, true /* allowDeviceCredential */);
+ true /* requireConfirmation */,
+ Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK);
waitForIdle();
assertEquals(BiometricService.STATE_SHOWING_DEVICE_CREDENTIAL,
mBiometricService.mCurrentAuthSession.mState);
- assertEquals(Authenticator.TYPE_CREDENTIAL,
+ assertEquals(Authenticators.DEVICE_CREDENTIAL,
mBiometricService.mCurrentAuthSession.mBundle
.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
@@ -336,9 +436,9 @@
@Test
public void testAuthenticate_happyPathWithConfirmation() throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- true /* requireConfirmation */, false /* allowDeviceCredential */);
+ true /* requireConfirmation */, null /* authenticators */);
// Test authentication succeeded goes to PENDING_CONFIRMATION and that the HAT is not
// sent to KeyStore yet
@@ -362,9 +462,9 @@
@Test
public void testRejectFace_whenAuthenticating_notifiesSystemUIAndClient_thenPaused()
throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */, false /* allowDeviceCredential */);
+ false /* requireConfirmation */, null /* authenticators */);
mBiometricService.mInternalReceiver.onAuthenticationFailed();
waitForIdle();
@@ -381,9 +481,9 @@
@Test
public void testRejectFingerprint_whenAuthenticating_notifiesAndKeepsAuthenticating()
throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */, false /* allowDeviceCredential */);
+ false /* requireConfirmation */, null /* authenticators */);
mBiometricService.mInternalReceiver.onAuthenticationFailed();
waitForIdle();
@@ -398,53 +498,10 @@
}
@Test
- public void testErrorCanceled_whenAuthenticating_notifiesSystemUIAndClient() throws
- Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
- invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */, false /* allowDeviceCredential */);
-
- // Create a new pending auth session but don't start it yet. HAL contract is that previous
- // one must get ERROR_CANCELED. Simulate that here by creating the pending auth session,
- // sending ERROR_CANCELED to the current auth session, and then having the second one
- // onReadyForAuthentication.
- invokeAuthenticate(mBiometricService.mImpl, mReceiver2, false /* requireConfirmation */,
- false /* allowDeviceCredential */);
- waitForIdle();
-
- assertEquals(mBiometricService.mCurrentAuthSession.mState,
- BiometricService.STATE_AUTH_STARTED);
- mBiometricService.mInternalReceiver.onError(
- getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
- BiometricAuthenticator.TYPE_FINGERPRINT,
- BiometricConstants.BIOMETRIC_ERROR_CANCELED, 0 /* vendorCode */);
- waitForIdle();
-
- // Auth session doesn't become null until SystemUI responds that the animation is completed
- assertNotNull(mBiometricService.mCurrentAuthSession);
- // ERROR_CANCELED is not sent until SystemUI responded that animation is completed
- verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
- verify(mReceiver2, never()).onError(anyInt(), anyInt(), anyInt());
-
- // SystemUI dialog closed
- verify(mBiometricService.mStatusBarService).hideAuthenticationDialog();
-
- // After SystemUI notifies that the animation has completed
- mBiometricService.mInternalReceiver
- .onDialogDismissed(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
- waitForIdle();
- verify(mReceiver1).onError(
- eq(BiometricAuthenticator.TYPE_FINGERPRINT),
- eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED),
- eq(0 /* vendorCode */));
- assertNull(mBiometricService.mCurrentAuthSession);
- }
-
- @Test
public void testErrorHalTimeout_whenAuthenticating_entersPausedState() throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */, false /* allowDeviceCredential */);
+ false /* requireConfirmation */, null /* authenticators */);
mBiometricService.mInternalReceiver.onError(
getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
@@ -491,9 +548,9 @@
@Test
public void testErrorFromHal_whenPaused_notifiesSystemUIAndClient() throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */, false /* allowDeviceCredential */);
+ false /* requireConfirmation */, null /* authenticators */);
mBiometricService.mInternalReceiver.onError(
getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
@@ -524,9 +581,9 @@
// For errors that show in SystemUI, BiometricService stays in STATE_ERROR_PENDING_SYSUI
// until SystemUI notifies us that the dialog is dismissed at which point the current
// session is done.
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */, false /* allowDeviceCredential */);
+ false /* requireConfirmation */, null /* authenticators */);
mBiometricService.mInternalReceiver.onError(
getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
@@ -558,9 +615,10 @@
@Test
public void testErrorFromHal_whilePreparingAuthentication_credentialAllowed() throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */, true /* allowDeviceCredential */);
+ false /* requireConfirmation */,
+ Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK);
waitForIdle();
mBiometricService.mInternalReceiver.onError(
@@ -576,7 +634,7 @@
assertNotNull(mBiometricService.mCurrentAuthSession);
assertEquals(BiometricService.STATE_SHOWING_DEVICE_CREDENTIAL,
mBiometricService.mCurrentAuthSession.mState);
- assertEquals(Authenticator.TYPE_CREDENTIAL,
+ assertEquals(Authenticators.DEVICE_CREDENTIAL,
mBiometricService.mCurrentAuthSession.mBundle.getInt(
BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
@@ -591,9 +649,9 @@
@Test
public void testErrorFromHal_whilePreparingAuthentication_credentialNotAllowed()
throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */, false /* allowDeviceCredential */);
+ false /* requireConfirmation */, null /* authenticators */);
waitForIdle();
mBiometricService.mInternalReceiver.onError(
@@ -609,58 +667,64 @@
}
@Test
- public void testCombineAuthenticatorBundle_keyAllowDeviceCredentialAlwaysRemoved() {
- Bundle bundle;
- int authenticators;
+ public void testCombineAuthenticatorBundles_withKeyDeviceCredential_andKeyAuthenticators() {
+ final boolean allowDeviceCredential = false;
+ final @Authenticators.Types int authenticators =
+ Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK;
+ final Bundle bundle = new Bundle();
- // In:
- // KEY_ALLOW_DEVICE_CREDENTIAL = true
- // KEY_AUTHENTICATORS_ALLOWED = TYPE_BIOMETRIC | TYPE_CREDENTIAL
- // Out:
- // KEY_ALLOW_DEVICE_CREDENTIAL = null
- // KEY_AUTHENTICATORS_ALLOWED = TYPE_BIOMETRIC | TYPE_CREDENTIAL
- bundle = new Bundle();
- bundle.putBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, true);
- authenticators = Authenticator.TYPE_CREDENTIAL | Authenticator.TYPE_BIOMETRIC;
+ bundle.putBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, allowDeviceCredential);
bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
Utils.combineAuthenticatorBundles(bundle);
- assertNull(bundle.get(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL));
- assertEquals(authenticators, bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
- // In:
- // KEY_ALLOW_DEVICE_CREDENTIAL = true
- // KEY_AUTHENTICATORS_ALLOWED = TYPE_BIOMETRIC
- // Out:
- // KEY_ALLOW_DEVICE_CREDENTIAL = null
- // KEY_AUTHENTICATORS_ALLOWED = TYPE_BIOMETRIC | TYPE_CREDENTIAL
- bundle = new Bundle();
- bundle.putBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, true);
- authenticators = Authenticator.TYPE_BIOMETRIC;
+ assertNull(bundle.get(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL));
+ assertEquals(bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED), authenticators);
+ }
+
+ @Test
+ public void testCombineAuthenticatorBundles_withNoKeyDeviceCredential_andKeyAuthenticators() {
+ final @Authenticators.Types int authenticators =
+ Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK;
+ final Bundle bundle = new Bundle();
+
bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
Utils.combineAuthenticatorBundles(bundle);
- assertNull(bundle.get(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL));
- assertEquals(authenticators, bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
- // In:
- // KEY_ALLOW_DEVICE_CREDENTIAL = null
- // KEY_AUTHENTICATORS_ALLOWED = TYPE_BIOMETRIC | TYPE_CREDENTIAL
- // Out:
- // KEY_ALLOW_DEVICE_CREDENTIAL = null
- // KEY_AUTHENTICATORS_ALLOWED = TYPE_BIOMETRIC | TYPE_CREDENTIAL
- bundle = new Bundle();
- authenticators = Authenticator.TYPE_BIOMETRIC | Authenticator.TYPE_CREDENTIAL;
- bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
- Utils.combineAuthenticatorBundles(bundle);
assertNull(bundle.get(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL));
- assertEquals(authenticators, bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
+ assertEquals(bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED), authenticators);
+ }
+
+ @Test
+ public void testCombineAuthenticatorBundles_withKeyDeviceCredential_andNoKeyAuthenticators() {
+ final boolean allowDeviceCredential = true;
+ final Bundle bundle = new Bundle();
+
+ bundle.putBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, allowDeviceCredential);
+ Utils.combineAuthenticatorBundles(bundle);
+
+ assertNull(bundle.get(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL));
+ assertEquals(bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED),
+ Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK);
+ }
+
+ @Test
+ public void testCombineAuthenticatorBundles_withNoKeyDeviceCredential_andNoKeyAuthenticators() {
+ final Bundle bundle = new Bundle();
+
+ Utils.combineAuthenticatorBundles(bundle);
+
+ assertNull(bundle.get(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL));
+ assertEquals(bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED),
+ Authenticators.BIOMETRIC_WEAK);
}
@Test
public void testErrorFromHal_whileShowingDeviceCredential_doesntNotifySystemUI()
throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */, true /* allowDeviceCredential */);
+ false /* requireConfirmation */,
+ Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK);
mBiometricService.mInternalReceiver.onDeviceCredentialPressed();
waitForIdle();
@@ -683,9 +747,10 @@
@Test
public void testLockout_whileAuthenticating_credentialAllowed() throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */, true /* allowDeviceCredential */);
+ false /* requireConfirmation */,
+ Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK);
assertEquals(BiometricService.STATE_AUTH_STARTED,
mBiometricService.mCurrentAuthSession.mState);
@@ -707,9 +772,9 @@
@Test
public void testLockout_whenAuthenticating_credentialNotAllowed() throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */, false /* allowDeviceCredential */);
+ false /* requireConfirmation */, null /* authenticators */);
assertEquals(BiometricService.STATE_AUTH_STARTED,
mBiometricService.mCurrentAuthSession.mState);
@@ -732,9 +797,9 @@
@Test
public void testDismissedReasonUserCancel_whileAuthenticating_cancelsHalAuthentication()
throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */, false /* allowDeviceCredential */);
+ false /* requireConfirmation */, null /* authenticators */);
mBiometricService.mInternalReceiver
.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
@@ -755,9 +820,9 @@
@Test
public void testDismissedReasonNegative_whilePaused_doesntInvokeHalCancel() throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */, false /* allowDeviceCredential */);
+ false /* requireConfirmation */, null /* authenticators */);
mBiometricService.mInternalReceiver.onError(
getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
@@ -781,9 +846,9 @@
@Test
public void testDismissedReasonUserCancel_whilePaused_doesntInvokeHalCancel() throws
Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */, false /* allowDeviceCredential */);
+ false /* requireConfirmation */, null /* authenticators */);
mBiometricService.mInternalReceiver.onError(
getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
@@ -806,9 +871,9 @@
@Test
public void testDismissedReasonUserCancel_whenPendingConfirmation() throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- true /* requireConfirmation */, false /* allowDeviceCredential */);
+ true /* requireConfirmation */, null /* authenticators */);
mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
true /* requireConfirmation */,
@@ -835,9 +900,9 @@
@Test
public void testAcquire_whenAuthenticating_sentToSystemUI() throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */, false /* allowDeviceCredential */);
+ false /* requireConfirmation */, null /* authenticators */);
mBiometricService.mInternalReceiver.onAcquired(
FingerprintManager.FINGERPRINT_ACQUIRED_IMAGER_DIRTY,
@@ -851,26 +916,354 @@
BiometricService.STATE_AUTH_STARTED);
}
+ @Test
+ public void testCancel_whenAuthenticating() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */, null /* authenticators */);
+
+ mBiometricService.mImpl.cancelAuthentication(mBiometricService.mCurrentAuthSession.mToken,
+ TEST_PACKAGE_NAME);
+ waitForIdle();
+
+ // Pretend that the HAL has responded to cancel with ERROR_CANCELED
+ mBiometricService.mInternalReceiver.onError(getCookieForCurrentSession(
+ mBiometricService.mCurrentAuthSession), BiometricAuthenticator.TYPE_FINGERPRINT,
+ BiometricConstants.BIOMETRIC_ERROR_CANCELED, 0 /* vendorCode */);
+ waitForIdle();
+
+ // Hides system dialog and invokes the onError callback
+ verify(mReceiver1).onError(eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+ eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED),
+ eq(0 /* vendorCode */));
+ verify(mBiometricService.mStatusBarService).hideAuthenticationDialog();
+ }
+
+ @Test
+ public void testCanAuthenticate_whenDeviceHasRequestedBiometricStrength() throws Exception {
+ // When only biometric is requested, and sensor is strong enough
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+
+ assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
+ invokeCanAuthenticate(mBiometricService, Authenticators.BIOMETRIC_STRONG));
+ }
+
+ @Test
+ public void testCanAuthenticate_whenDeviceDoesNotHaveRequestedBiometricStrength()
+ throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_WEAK);
+
+ // When only biometric is requested, and sensor is not strong enough
+ when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(false);
+ int authenticators = Authenticators.BIOMETRIC_STRONG;
+ assertEquals(BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE,
+ invokeCanAuthenticate(mBiometricService, authenticators));
+
+ // When credential and biometric are requested, and sensor is not strong enough
+ when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+ authenticators = Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL;
+ assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
+ invokeCanAuthenticate(mBiometricService, authenticators));
+ }
+
+ @Test
+ public void testCanAuthenticate_onlyCredentialRequested() throws Exception {
+ mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService.onStart();
+
+ // Credential requested but not set up
+ when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(false);
+ assertEquals(BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED,
+ invokeCanAuthenticate(mBiometricService, Authenticators.DEVICE_CREDENTIAL));
+
+ // Credential requested and set up
+ when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+ assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
+ invokeCanAuthenticate(mBiometricService, Authenticators.DEVICE_CREDENTIAL));
+ }
+
+ @Test
+ public void testCanAuthenticate_whenNoBiometricsEnrolled() throws Exception {
+ // With credential set up, test the following.
+ when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
+ false /* enrolled */);
+
+ // When only biometric is requested
+ int authenticators = Authenticators.BIOMETRIC_STRONG;
+ assertEquals(BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED,
+ invokeCanAuthenticate(mBiometricService, authenticators));
+
+ // When credential and biometric are requested
+ authenticators = Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL;
+ assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
+ invokeCanAuthenticate(mBiometricService, authenticators));
+ }
+
+ @Test
+ public void testCanAuthenticate_whenBiometricsNotEnabledForApps() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
+ when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(false);
+ when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+
+ // When only biometric is requested
+ int authenticators = Authenticators.BIOMETRIC_STRONG;
+ assertEquals(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ invokeCanAuthenticate(mBiometricService, authenticators));
+
+ // When credential and biometric are requested
+ authenticators = Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL;
+ assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
+ invokeCanAuthenticate(mBiometricService, authenticators));
+ }
+
+ @Test
+ public void testCanAuthenticate_whenNoBiometricSensor() throws Exception {
+ mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService.onStart();
+
+ // When only biometric is requested
+ int authenticators = Authenticators.BIOMETRIC_STRONG;
+ assertEquals(BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE,
+ invokeCanAuthenticate(mBiometricService, authenticators));
+
+ // When credential and biometric are requested, and credential is not set up
+ authenticators = Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL;
+ assertEquals(BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED,
+ invokeCanAuthenticate(mBiometricService, authenticators));
+
+ // When credential and biometric are requested, and credential is set up
+ when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+ assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
+ invokeCanAuthenticate(mBiometricService, authenticators));
+ }
+
+ @Test
+ public void testAuthenticatorActualStrength() {
+ // Tuple of OEM config, updatedStrength, and expectedStrength
+ final int[][] testCases = {
+ // Downgrades to the specified strength
+ {Authenticators.BIOMETRIC_STRONG, Authenticators.BIOMETRIC_WEAK,
+ Authenticators.BIOMETRIC_WEAK},
+
+ // Cannot be upgraded
+ {Authenticators.BIOMETRIC_WEAK, Authenticators.BIOMETRIC_STRONG,
+ Authenticators.BIOMETRIC_WEAK},
+
+ // Downgrades to convenience
+ {Authenticators.BIOMETRIC_WEAK, Authenticators.BIOMETRIC_CONVENIENCE,
+ Authenticators.BIOMETRIC_CONVENIENCE},
+
+ // EMPTY_SET does not modify specified strength
+ {Authenticators.BIOMETRIC_WEAK, Authenticators.EMPTY_SET,
+ Authenticators.BIOMETRIC_WEAK},
+ };
+
+ for (int i = 0; i < testCases.length; i++) {
+ final BiometricService.AuthenticatorWrapper authenticator =
+ new BiometricService.AuthenticatorWrapper(0 /* id */,
+ BiometricAuthenticator.TYPE_FINGERPRINT,
+ testCases[i][0],
+ null /* impl */);
+ authenticator.updateStrength(testCases[i][1]);
+ assertEquals(testCases[i][2], authenticator.getActualStrength());
+ }
+ }
+
+ @Test
+ public void testWithDowngradedAuthenticator() throws Exception {
+ mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService.onStart();
+
+ final int testId = 0;
+
+ when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any()))
+ .thenReturn(true);
+ when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true);
+ mBiometricService.mImpl.registerAuthenticator(testId /* id */,
+ BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
+ mFingerprintAuthenticator);
+
+ // Downgrade the authenticator
+ for (BiometricService.AuthenticatorWrapper wrapper : mBiometricService.mAuthenticators) {
+ if (wrapper.id == testId) {
+ wrapper.updateStrength(Authenticators.BIOMETRIC_WEAK);
+ }
+ }
+
+ // STRONG-only auth is not available
+ int authenticators = Authenticators.BIOMETRIC_STRONG;
+ assertEquals(BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE,
+ invokeCanAuthenticate(mBiometricService, authenticators));
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
+ authenticators);
+ waitForIdle();
+ verify(mReceiver1).onError(
+ eq(BiometricAuthenticator.TYPE_NONE),
+ eq(BiometricPrompt.BIOMETRIC_ERROR_HW_NOT_PRESENT),
+ eq(0) /* vendorCode */);
+
+ // Request for weak auth works
+ resetReceiver();
+ authenticators = Authenticators.BIOMETRIC_WEAK;
+ assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
+ invokeCanAuthenticate(mBiometricService, authenticators));
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */,
+ authenticators);
+ waitForIdle();
+ verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
+ eq(mBiometricService.mCurrentAuthSession.mBundle),
+ any(IBiometricServiceReceiverInternal.class),
+ eq(BiometricAuthenticator.TYPE_FINGERPRINT /* biometricModality */),
+ anyBoolean() /* requireConfirmation */,
+ anyInt() /* userId */,
+ eq(TEST_PACKAGE_NAME));
+
+ // Requesting strong and credential, when credential is setup
+ resetReceiver();
+ authenticators = Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL;
+ when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+ assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
+ invokeCanAuthenticate(mBiometricService, authenticators));
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */,
+ authenticators);
+ waitForIdle();
+ assertTrue(Utils.isDeviceCredentialAllowed(mBiometricService.mCurrentAuthSession.mBundle));
+ verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
+ eq(mBiometricService.mCurrentAuthSession.mBundle),
+ any(IBiometricServiceReceiverInternal.class),
+ eq(BiometricAuthenticator.TYPE_NONE /* biometricModality */),
+ anyBoolean() /* requireConfirmation */,
+ anyInt() /* userId */,
+ eq(TEST_PACKAGE_NAME));
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testRegistrationWithDuplicateId_throwsIllegalStateException() throws Exception {
+ mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService.onStart();
+
+ mBiometricService.mImpl.registerAuthenticator(
+ 0 /* id */, 2 /* modality */, 15 /* strength */,
+ mFingerprintAuthenticator);
+ mBiometricService.mImpl.registerAuthenticator(
+ 0 /* id */, 2 /* modality */, 15 /* strength */,
+ mFingerprintAuthenticator);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testRegistrationWithUnknownId_throwsIllegalStateException() throws Exception {
+ mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService.onStart();
+
+ mBiometricService.mImpl.registerAuthenticator(
+ 100 /* id */, 2 /* modality */, 15 /* strength */,
+ mFingerprintAuthenticator);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testRegistrationWithUnsupportedStrength_throwsIllegalStateException()
+ throws Exception {
+ mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService.onStart();
+
+ // Only STRONG and WEAK are supported. Let's enforce that CONVENIENCE cannot be
+ // registered. If there is a compelling reason, we can remove this constraint.
+ mBiometricService.mImpl.registerAuthenticator(
+ 0 /* id */, 2 /* modality */,
+ Authenticators.BIOMETRIC_CONVENIENCE /* strength */,
+ mFingerprintAuthenticator);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testRegistrationWithNullAuthenticator_throwsIllegalArgumentException()
+ throws Exception {
+ mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService.onStart();
+
+ mBiometricService.mImpl.registerAuthenticator(
+ 0 /* id */, 2 /* modality */,
+ Authenticators.BIOMETRIC_STRONG /* strength */,
+ null /* authenticator */);
+ }
+
+ @Test
+ public void testRegistrationHappyPath_isOk() throws Exception {
+ // This is being tested in many of the other cases, but here's the base case.
+ mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService.onStart();
+
+ for (String s : mInjector.getConfiguration(null)) {
+ SensorConfig config = new SensorConfig(s);
+ mBiometricService.mImpl.registerAuthenticator(config.mId, config.mModality,
+ config.mStrength, mFingerprintAuthenticator);
+ }
+ }
+
// Helper methods
- private void setupAuthForOnly(int modality) throws RemoteException {
+ private int invokeCanAuthenticate(BiometricService service, int authenticators)
+ throws Exception {
+ return service.mImpl.canAuthenticate(TEST_PACKAGE_NAME, 0 /* userId */, authenticators);
+ }
+
+ private void setupAuthForOnly(int modality, int strength) throws Exception {
+ setupAuthForOnly(modality, strength, true /* enrolled */);
+ }
+
+ // TODO: Reconcile the registration strength with the injector
+ private void setupAuthForOnly(int modality, int strength, boolean enrolled) throws Exception {
mBiometricService = new BiometricService(mContext, mInjector);
mBiometricService.onStart();
when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true);
- if (modality == BiometricAuthenticator.TYPE_FINGERPRINT) {
- when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
+ if ((modality & BiometricAuthenticator.TYPE_FINGERPRINT) != 0) {
+ when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any()))
+ .thenReturn(enrolled);
when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true);
- mBiometricService.mImpl.registerAuthenticator(0 /* id */, STRENGTH_STRONG, modality,
+ mBiometricService.mImpl.registerAuthenticator(0 /* id */, modality, strength,
mFingerprintAuthenticator);
- } else if (modality == BiometricAuthenticator.TYPE_FACE) {
- when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
+ }
+
+ if ((modality & BiometricAuthenticator.TYPE_FACE) != 0) {
+ when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(enrolled);
when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true);
- mBiometricService.mImpl.registerAuthenticator(0 /* id */, STRENGTH_STRONG, modality,
+ mBiometricService.mImpl.registerAuthenticator(1 /* id */, modality, strength,
mFaceAuthenticator);
- } else {
- fail("Unknown modality: " + modality);
+ }
+ }
+
+ // TODO: Reduce duplicated code, currently we cannot start the BiometricService in setUp() for
+ // all tests.
+ private void setupAuthForMultiple(int[] modalities, int[] strengths) throws RemoteException {
+ mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService.onStart();
+
+ when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true);
+
+ assertEquals(modalities.length, strengths.length);
+
+ for (int i = 0; i < modalities.length; i++) {
+ final int modality = modalities[i];
+ final int strength = strengths[i];
+
+ if ((modality & BiometricAuthenticator.TYPE_FINGERPRINT) != 0) {
+ when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any()))
+ .thenReturn(true);
+ when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true);
+ mBiometricService.mImpl.registerAuthenticator(0 /* id */, modality, strength,
+ mFingerprintAuthenticator);
+ }
+
+ if ((modality & BiometricAuthenticator.TYPE_FACE) != 0) {
+ when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
+ when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true);
+ mBiometricService.mImpl.registerAuthenticator(1 /* id */, modality, strength,
+ mFaceAuthenticator);
+ }
}
}
@@ -885,9 +1278,9 @@
private void invokeAuthenticateAndStart(IBiometricService.Stub service,
IBiometricServiceReceiver receiver, boolean requireConfirmation,
- boolean allowDeviceCredential) throws Exception {
+ Integer authenticators) throws Exception {
// Request auth, creates a pending session
- invokeAuthenticate(service, receiver, requireConfirmation, allowDeviceCredential);
+ invokeAuthenticate(service, receiver, requireConfirmation, authenticators);
waitForIdle();
startPendingAuthSession(mBiometricService);
@@ -908,23 +1301,24 @@
private static void invokeAuthenticate(IBiometricService.Stub service,
IBiometricServiceReceiver receiver, boolean requireConfirmation,
- boolean allowDeviceCredential) throws Exception {
+ Integer authenticators) throws Exception {
service.authenticate(
new Binder() /* token */,
0 /* sessionId */,
0 /* userId */,
receiver,
TEST_PACKAGE_NAME /* packageName */,
- createTestBiometricPromptBundle(requireConfirmation, allowDeviceCredential));
+ createTestBiometricPromptBundle(requireConfirmation, authenticators));
}
- private static Bundle createTestBiometricPromptBundle(boolean requireConfirmation,
- boolean allowDeviceCredential) {
+ private static Bundle createTestBiometricPromptBundle(
+ boolean requireConfirmation,
+ Integer authenticators) {
final Bundle bundle = new Bundle();
bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, requireConfirmation);
- if (allowDeviceCredential) {
- bundle.putBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, true);
+ if (authenticators != null) {
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
}
return bundle;
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
new file mode 100644
index 0000000..abe39f0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics;
+
+import static android.hardware.biometrics.BiometricManager.Authenticators;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertNull;
+
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.BiometricPrompt;
+import android.os.Bundle;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@SmallTest
+public class UtilsTest {
+
+ @Test
+ public void testCombineAuthenticatorBundles_withKeyDeviceCredential_andKeyAuthenticators() {
+ final boolean allowDeviceCredential = false;
+ final @Authenticators.Types int authenticators =
+ Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK;
+ final Bundle bundle = new Bundle();
+
+ bundle.putBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, allowDeviceCredential);
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+ Utils.combineAuthenticatorBundles(bundle);
+
+ assertNull(bundle.get(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL));
+ assertEquals(bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED), authenticators);
+ }
+
+ @Test
+ public void testCombineAuthenticatorBundles_withNoKeyDeviceCredential_andKeyAuthenticators() {
+ final @Authenticators.Types int authenticators =
+ Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK;
+ final Bundle bundle = new Bundle();
+
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+ Utils.combineAuthenticatorBundles(bundle);
+
+ assertNull(bundle.get(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL));
+ assertEquals(bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED), authenticators);
+ }
+
+ @Test
+ public void testCombineAuthenticatorBundles_withKeyDeviceCredential_andNoKeyAuthenticators() {
+ final boolean allowDeviceCredential = true;
+ final Bundle bundle = new Bundle();
+
+ bundle.putBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, allowDeviceCredential);
+ Utils.combineAuthenticatorBundles(bundle);
+
+ assertNull(bundle.get(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL));
+ assertEquals(bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED),
+ Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK);
+ }
+
+ @Test
+ public void testCombineAuthenticatorBundles_withNoKeyDeviceCredential_andNoKeyAuthenticators() {
+ final Bundle bundle = new Bundle();
+
+ Utils.combineAuthenticatorBundles(bundle);
+
+ assertNull(bundle.get(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL));
+ assertEquals(bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED),
+ Authenticators.BIOMETRIC_WEAK);
+ }
+
+ @Test
+ public void testIsDeviceCredentialAllowed_withIntegerFlags() {
+ int authenticators = 0;
+ assertFalse(Utils.isDeviceCredentialAllowed(authenticators));
+
+ authenticators |= Authenticators.DEVICE_CREDENTIAL;
+ assertTrue(Utils.isDeviceCredentialAllowed(authenticators));
+
+ authenticators |= Authenticators.BIOMETRIC_WEAK;
+ assertTrue(Utils.isDeviceCredentialAllowed(authenticators));
+ }
+
+ @Test
+ public void testIsDeviceCredentialAllowed_withBundle() {
+ Bundle bundle = new Bundle();
+ assertFalse(Utils.isDeviceCredentialAllowed(bundle));
+
+ int authenticators = 0;
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+ assertFalse(Utils.isDeviceCredentialAllowed(bundle));
+
+ authenticators |= Authenticators.DEVICE_CREDENTIAL;
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+ assertTrue(Utils.isDeviceCredentialAllowed(bundle));
+
+ authenticators |= Authenticators.BIOMETRIC_WEAK;
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+ assertTrue(Utils.isDeviceCredentialAllowed(bundle));
+ }
+
+ @Test
+ public void testGetBiometricStrength_removeUnrelatedBits() {
+ // BIOMETRIC_MIN_STRENGTH uses all of the allowed bits for biometric strength, so any other
+ // bits aside from these should be clipped off.
+
+ int authenticators = Integer.MAX_VALUE;
+ assertEquals(Authenticators.BIOMETRIC_WEAK,
+ Utils.getPublicBiometricStrength(authenticators));
+
+ Bundle bundle = new Bundle();
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+ assertEquals(Authenticators.BIOMETRIC_WEAK, Utils.getPublicBiometricStrength(bundle));
+ }
+
+ @Test
+ public void testIsBiometricAllowed() {
+ // Only the lowest 8 bits (BIOMETRIC_WEAK mask) are allowed to integrate with the
+ // Biometric APIs
+ Bundle bundle = new Bundle();
+ for (int i = 0; i <= 7; i++) {
+ int authenticators = 1 << i;
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+ assertTrue(Utils.isBiometricAllowed(bundle));
+ }
+
+ // The rest of the bits are not allowed to integrate with the public APIs
+ for (int i = 8; i < 32; i++) {
+ int authenticators = 1 << i;
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+ assertFalse(Utils.isBiometricAllowed(bundle));
+ }
+ }
+
+ @Test
+ public void testIsValidAuthenticatorConfig() {
+ assertTrue(Utils.isValidAuthenticatorConfig(Authenticators.EMPTY_SET));
+
+ assertTrue(Utils.isValidAuthenticatorConfig(Authenticators.BIOMETRIC_STRONG));
+
+ assertTrue(Utils.isValidAuthenticatorConfig(Authenticators.BIOMETRIC_WEAK));
+
+ assertTrue(Utils.isValidAuthenticatorConfig(Authenticators.DEVICE_CREDENTIAL));
+
+ assertTrue(Utils.isValidAuthenticatorConfig(Authenticators.DEVICE_CREDENTIAL
+ | Authenticators.BIOMETRIC_STRONG));
+
+ assertTrue(Utils.isValidAuthenticatorConfig(Authenticators.DEVICE_CREDENTIAL
+ | Authenticators.BIOMETRIC_WEAK));
+
+ assertFalse(Utils.isValidAuthenticatorConfig(Authenticators.BIOMETRIC_CONVENIENCE));
+
+ assertFalse(Utils.isValidAuthenticatorConfig(Authenticators.BIOMETRIC_CONVENIENCE
+ | Authenticators.DEVICE_CREDENTIAL));
+
+ assertFalse(Utils.isValidAuthenticatorConfig(Authenticators.BIOMETRIC_MAX_STRENGTH));
+
+ assertFalse(Utils.isValidAuthenticatorConfig(Authenticators.BIOMETRIC_MIN_STRENGTH));
+
+ // The rest of the bits are not allowed to integrate with the public APIs
+ for (int i = 8; i < 32; i++) {
+ final int authenticator = 1 << i;
+ if (authenticator == Authenticators.DEVICE_CREDENTIAL) {
+ continue;
+ }
+ assertFalse(Utils.isValidAuthenticatorConfig(1 << i));
+ }
+ }
+
+ @Test
+ public void testIsAtLeastStrength() {
+ int sensorStrength = Authenticators.BIOMETRIC_STRONG;
+ int requestedStrength = Authenticators.BIOMETRIC_WEAK;
+ assertTrue(Utils.isAtLeastStrength(sensorStrength, requestedStrength));
+
+ requestedStrength = Authenticators.BIOMETRIC_STRONG;
+ assertTrue(Utils.isAtLeastStrength(sensorStrength, requestedStrength));
+
+ sensorStrength = Authenticators.BIOMETRIC_WEAK;
+ requestedStrength = Authenticators.BIOMETRIC_STRONG;
+ assertFalse(Utils.isAtLeastStrength(sensorStrength, requestedStrength));
+
+ requestedStrength = Authenticators.BIOMETRIC_WEAK;
+ assertTrue(Utils.isAtLeastStrength(sensorStrength, requestedStrength));
+ }
+
+ @Test
+ public void testBiometricConstantsConversion() {
+ final int[][] testCases = {
+ {BiometricConstants.BIOMETRIC_SUCCESS,
+ BiometricManager.BIOMETRIC_SUCCESS},
+ {BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS,
+ BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED},
+ {BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
+ BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED},
+ {BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE},
+ {BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT,
+ BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE}
+ };
+
+ for (int i = 0; i < testCases.length; i++) {
+ assertEquals(testCases[i][1],
+ Utils.biometricConstantsToBiometricManager(testCases[i][0]));
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 3f09f57..f54f885 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3673,6 +3673,45 @@
verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 0);
}
+ public void testSetAutoTimeZoneModifiesSetting() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ dpm.setAutoTimeZone(admin1, true);
+ verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 1);
+
+ dpm.setAutoTimeZone(admin1, false);
+ verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 0);
+ }
+
+ public void testSetAutoTimeZoneWithPOOnUser0() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupProfileOwnerOnUser0();
+ dpm.setAutoTimeZone(admin1, true);
+ verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 1);
+
+ dpm.setAutoTimeZone(admin1, false);
+ verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 0);
+ }
+
+ public void testSetAutoTimeZoneFailWithPONotOnUser0() throws Exception {
+ setupProfileOwner();
+ assertExpectException(SecurityException.class, null,
+ () -> dpm.setAutoTimeZone(admin1, false));
+ verify(getServices().settings, never()).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE,
+ 0);
+ }
+
+ public void testSetAutoTimeZoneWithPOOfOrganizationOwnedDevice() throws Exception {
+ setupProfileOwner();
+ configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
+
+ dpm.setAutoTimeZone(admin1, true);
+ verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 1);
+
+ dpm.setAutoTimeZone(admin1, false);
+ verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 0);
+ }
+
public void testSetTime() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 7a2350e..919a3f6 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -35,6 +35,7 @@
import android.app.timezonedetector.TimeZoneDetector;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
@@ -162,6 +163,8 @@
// Package manager is huge, so we use a partial mock instead.
packageManager = spy(realContext.getPackageManager());
+ when(packageManagerInternal.getSystemUiServiceComponent()).thenReturn(
+ new ComponentName("com.android.systemui", ".Service"));
contentResolver = new MockContentResolver();
contentResolver.addProvider("telephony", new MockContentProvider(realContext) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
index b751308..c478ec4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -173,6 +173,7 @@
/* createdMillis */ 0L,
/* stageDir */ mTmpDir,
/* stageCid */ null,
+ /* files */ null,
/* prepared */ true,
/* committed */ true,
/* sealed */ false, // Setting to true would trigger some PM logic.
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
index cb49fef..2429cfc 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
@@ -453,7 +453,7 @@
@Override
public boolean isDeviceTimeZoneInitialized() {
- return mTimeZoneId != null;
+ return mTimeZoneId.getLatest() != null;
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 72baedb..8ade4d2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -61,6 +61,7 @@
import android.graphics.Insets;
import android.graphics.Matrix;
+import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.Size;
@@ -222,8 +223,7 @@
final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
final WindowState imeWindow = createWindow(null, TYPE_INPUT_METHOD, "imeWindow");
- // Setting FLAG_NOT_FOCUSABLE without FLAG_ALT_FOCUSABLE_IM prevents the window from being
- // an IME target.
+ // Setting FLAG_NOT_FOCUSABLE prevents the window from being an IME target.
appWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
imeWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
@@ -231,7 +231,7 @@
appWindow.setHasSurface(true);
imeWindow.setHasSurface(true);
- // Windows without flags (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM) can't be IME targets
+ // Windows with FLAG_NOT_FOCUSABLE can't be IME targets
assertFalse(appWindow.canBeImeTarget());
assertFalse(imeWindow.canBeImeTarget());
@@ -239,11 +239,22 @@
appWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
imeWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
- // Visible app window with flags can be IME target while an IME window can never be an IME
- // target regardless of its visibility or flags.
- assertTrue(appWindow.canBeImeTarget());
+ // Visible app window with flags FLAG_NOT_FOCUSABLE or FLAG_ALT_FOCUSABLE_IM can't be IME
+ // target while an IME window can never be an IME target regardless of its visibility
+ // or flags.
+ assertFalse(appWindow.canBeImeTarget());
assertFalse(imeWindow.canBeImeTarget());
+ // b/145812508: special legacy use-case for transparent/translucent windows.
+ appWindow.mAttrs.format = PixelFormat.TRANSPARENT;
+ assertTrue(appWindow.canBeImeTarget());
+
+ appWindow.mAttrs.format = PixelFormat.OPAQUE;
+ appWindow.mAttrs.flags &= ~FLAG_ALT_FOCUSABLE_IM;
+ assertFalse(appWindow.canBeImeTarget());
+ appWindow.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE;
+ assertTrue(appWindow.canBeImeTarget());
+
// Make windows invisible
appWindow.hideLw(false /* doAnimation */);
imeWindow.hideLw(false /* doAnimation */);
diff --git a/services/usb/java/com/android/server/usb/UsbHandlerManager.java b/services/usb/java/com/android/server/usb/UsbHandlerManager.java
index 1730d8f..f311274 100644
--- a/services/usb/java/com/android/server/usb/UsbHandlerManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHandlerManager.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
@@ -59,8 +60,9 @@
if (uri != null && uri.length() > 0) {
// display URI to user
Intent dialogIntent = createDialogIntent();
- dialogIntent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbAccessoryUriActivity");
+ dialogIntent.setComponent(ComponentName.unflattenFromString(
+ mContext.getResources().getString(
+ com.android.internal.R.string.config_usbAccessoryUriActivity)));
dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
dialogIntent.putExtra("uri", uri);
try {
@@ -84,8 +86,9 @@
@Nullable UsbAccessory accessory) {
Intent resolverIntent = createDialogIntent();
// start UsbConfirmActivity if there is only one choice
- resolverIntent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbConfirmActivity");
+ resolverIntent.setComponent(ComponentName.unflattenFromString(
+ mContext.getResources().getString(
+ com.android.internal.R.string.config_usbConfirmActivity)));
resolverIntent.putExtra("rinfo", rInfo);
UserHandle user =
UserHandle.getUserHandleForUid(rInfo.activityInfo.applicationInfo.uid);
@@ -115,8 +118,9 @@
void selectUsbHandler(@NonNull ArrayList<ResolveInfo> matches,
@NonNull UserHandle user, @NonNull Intent intent) {
Intent resolverIntent = createDialogIntent();
- resolverIntent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbResolverActivity");
+ resolverIntent.setComponent(ComponentName.unflattenFromString(
+ mContext.getResources().getString(
+ com.android.internal.R.string.config_usbResolverActivity)));
resolverIntent.putParcelableArrayListExtra("rlist", matches);
resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 749258e..c3e2013 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -34,6 +34,7 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -225,8 +226,8 @@
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbContaminantActivity");
+ intent.setComponent(ComponentName.unflattenFromString(r.getString(
+ com.android.internal.R.string.config_usbContaminantActivity)));
intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(currentPortInfo.mUsbPort));
PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0,
diff --git a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
index 58f5484..2a94393 100644
--- a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -515,8 +516,8 @@
intent.putExtra(Intent.EXTRA_UID, uid);
intent.putExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, canBeDefault);
intent.putExtra(UsbManager.EXTRA_PACKAGE, packageName);
- intent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbPermissionActivity");
+ intent.setComponent(ComponentName.unflattenFromString(userContext.getResources().getString(
+ com.android.internal.R.string.config_usbPermissionActivity)));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index 02d8be3..39af34c 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -16,7 +16,10 @@
package android.telephony.ims;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.Context;
import android.telephony.SubscriptionManager;
@@ -25,12 +28,15 @@
*
* @hide
*/
+@SystemApi
+@TestApi
@SystemService(Context.TELEPHONY_IMS_SERVICE)
public class ImsManager {
private Context mContext;
- public ImsManager(Context context) {
+ /** @hide */
+ public ImsManager(@NonNull Context context) {
mContext = context;
}
@@ -41,6 +47,7 @@
* @throws IllegalArgumentException if the subscription is invalid.
* @return a ImsRcsManager instance with the specific subscription ID.
*/
+ @NonNull
public ImsRcsManager getImsRcsManager(int subscriptionId) {
if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
@@ -56,6 +63,7 @@
* @throws IllegalArgumentException if the subscription is invalid.
* @return a ImsMmTelManager instance with the specific subscription ID.
*/
+ @NonNull
public ImsMmTelManager getImsMmTelManager(int subscriptionId) {
if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
diff --git a/telephony/java/android/telephony/NetworkScanRequest.java b/telephony/java/android/telephony/NetworkScanRequest.java
index 0ceb103..c8b8ffb 100644
--- a/telephony/java/android/telephony/NetworkScanRequest.java
+++ b/telephony/java/android/telephony/NetworkScanRequest.java
@@ -222,9 +222,13 @@
private NetworkScanRequest(Parcel in) {
mScanType = in.readInt();
Parcelable[] tempSpecifiers = in.readParcelableArray(Object.class.getClassLoader());
- mSpecifiers = new RadioAccessSpecifier[tempSpecifiers.length];
- for (int i = 0; i < tempSpecifiers.length; i++) {
- mSpecifiers[i] = (RadioAccessSpecifier) tempSpecifiers[i];
+ if (tempSpecifiers != null) {
+ mSpecifiers = new RadioAccessSpecifier[tempSpecifiers.length];
+ for (int i = 0; i < tempSpecifiers.length; i++) {
+ mSpecifiers[i] = (RadioAccessSpecifier) tempSpecifiers[i];
+ }
+ } else {
+ mSpecifiers = null;
}
mSearchPeriodicity = in.readInt();
mMaxSearchTime = in.readInt();
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index d2c8517..d7d85c2 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -941,7 +941,7 @@
rtString = "LTE_CA";
break;
case RIL_RADIO_TECHNOLOGY_NR:
- rtString = "LTE_NR";
+ rtString = "NR_SA";
break;
default:
rtString = "Unexpected";
@@ -1479,8 +1479,9 @@
return AccessNetworkType.CDMA2000;
case RIL_RADIO_TECHNOLOGY_LTE:
case RIL_RADIO_TECHNOLOGY_LTE_CA:
- case RIL_RADIO_TECHNOLOGY_NR:
return AccessNetworkType.EUTRAN;
+ case RIL_RADIO_TECHNOLOGY_NR:
+ return AccessNetworkType.NGRAN;
case RIL_RADIO_TECHNOLOGY_IWLAN:
return AccessNetworkType.IWLAN;
case RIL_RADIO_TECHNOLOGY_UNKNOWN:
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 5a4a3d0..6c321e5 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2864,6 +2864,24 @@
//
//
+ /** @hide */
+ @IntDef(prefix = {"SIM_STATE_"},
+ value = {
+ SIM_STATE_UNKNOWN,
+ SIM_STATE_ABSENT,
+ SIM_STATE_PIN_REQUIRED,
+ SIM_STATE_PUK_REQUIRED,
+ SIM_STATE_NETWORK_LOCKED,
+ SIM_STATE_READY,
+ SIM_STATE_NOT_READY,
+ SIM_STATE_PERM_DISABLED,
+ SIM_STATE_CARD_IO_ERROR,
+ SIM_STATE_CARD_RESTRICTED,
+ SIM_STATE_LOADED,
+ SIM_STATE_PRESENT,
+ })
+ public @interface SimState {}
+
/**
* SIM card state: Unknown. Signifies that the SIM is in transition
* between states. For example, when the user inputs the SIM pin
@@ -3069,7 +3087,7 @@
* @see #SIM_STATE_CARD_IO_ERROR
* @see #SIM_STATE_CARD_RESTRICTED
*/
- public int getSimState() {
+ public @SimState int getSimState() {
int simState = getSimStateIncludingLoaded();
if (simState == SIM_STATE_LOADED) {
simState = SIM_STATE_READY;
@@ -3077,7 +3095,7 @@
return simState;
}
- private int getSimStateIncludingLoaded() {
+ private @SimState int getSimStateIncludingLoaded() {
int slotIndex = getSlotIndex();
// slotIndex may be invalid due to sim being absent. In that case query all slots to get
// sim state
@@ -3111,7 +3129,7 @@
* @hide
*/
@SystemApi
- public int getSimCardState() {
+ public @SimState int getSimCardState() {
int simState = getSimState();
return getSimCardStateFromSimState(simState);
}
@@ -3131,7 +3149,7 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public int getSimCardState(int physicalSlotIndex) {
+ public @SimState int getSimCardState(int physicalSlotIndex) {
int simState = getSimState(getLogicalSlotIndex(physicalSlotIndex));
return getSimCardStateFromSimState(simState);
}
@@ -3141,7 +3159,7 @@
* @param simState
* @return SIM card state
*/
- private int getSimCardStateFromSimState(int simState) {
+ private @SimState int getSimCardStateFromSimState(int simState) {
switch (simState) {
case SIM_STATE_UNKNOWN:
case SIM_STATE_ABSENT:
@@ -3181,7 +3199,7 @@
* @hide
*/
@SystemApi
- public int getSimApplicationState() {
+ public @SimState int getSimApplicationState() {
int simState = getSimStateIncludingLoaded();
return getSimApplicationStateFromSimState(simState);
}
@@ -3204,7 +3222,7 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public int getSimApplicationState(int physicalSlotIndex) {
+ public @SimState int getSimApplicationState(int physicalSlotIndex) {
int simState =
SubscriptionManager.getSimStateForSlotIndex(getLogicalSlotIndex(physicalSlotIndex));
return getSimApplicationStateFromSimState(simState);
@@ -3215,7 +3233,7 @@
* @param simState
* @return SIM application state
*/
- private int getSimApplicationStateFromSimState(int simState) {
+ private @SimState int getSimApplicationStateFromSimState(int simState) {
switch (simState) {
case SIM_STATE_UNKNOWN:
case SIM_STATE_ABSENT:
@@ -3272,7 +3290,7 @@
* @see #SIM_STATE_CARD_IO_ERROR
* @see #SIM_STATE_CARD_RESTRICTED
*/
- public int getSimState(int slotIndex) {
+ public @SimState int getSimState(int slotIndex) {
int simState = SubscriptionManager.getSimStateForSlotIndex(slotIndex);
if (simState == SIM_STATE_LOADED) {
simState = SIM_STATE_READY;
@@ -6468,75 +6486,6 @@
}
/**
- * Sets a per-phone telephony property with the value specified.
- *
- * @hide
- */
- @UnsupportedAppUsage
- public static void setTelephonyProperty(int phoneId, String property, String value) {
- String propVal = "";
- String p[] = null;
- String prop = SystemProperties.get(property);
-
- if (value == null) {
- value = "";
- }
- value.replace(',', ' ');
- if (prop != null) {
- p = prop.split(",");
- }
-
- if (!SubscriptionManager.isValidPhoneId(phoneId)) {
- Rlog.d(TAG, "setTelephonyProperty: invalid phoneId=" + phoneId +
- " property=" + property + " value: " + value + " prop=" + prop);
- return;
- }
-
- for (int i = 0; i < phoneId; i++) {
- String str = "";
- if ((p != null) && (i < p.length)) {
- str = p[i];
- }
- propVal = propVal + str + ",";
- }
-
- propVal = propVal + value;
- if (p != null) {
- for (int i = phoneId + 1; i < p.length; i++) {
- propVal = propVal + "," + p[i];
- }
- }
-
- int propValLen = propVal.length();
- try {
- propValLen = propVal.getBytes("utf-8").length;
- } catch (java.io.UnsupportedEncodingException e) {
- Rlog.d(TAG, "setTelephonyProperty: utf-8 not supported");
- }
- if (propValLen > SystemProperties.PROP_VALUE_MAX) {
- Rlog.d(TAG, "setTelephonyProperty: property too long phoneId=" + phoneId +
- " property=" + property + " value: " + value + " propVal=" + propVal);
- return;
- }
-
- SystemProperties.set(property, propVal);
- }
-
- /**
- * Sets a global telephony property with the value specified.
- *
- * @hide
- */
- public static void setTelephonyProperty(String property, String value) {
- if (value == null) {
- value = "";
- }
- Rlog.d(TAG, "setTelephonyProperty: success" + " property=" +
- property + " value: " + value);
- SystemProperties.set(property, value);
- }
-
- /**
* Inserts or updates a list property. Expands the list if its length is not enough.
*/
private static <T> List<T> updateTelephonyProperty(List<T> prop, int phoneId, T value) {
@@ -6873,7 +6822,8 @@
* If the list is longer than the size of EFfplmn, then the file will be written from the
* beginning of the list up to the file size.
*
- * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * <p>Requires Permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE
+ * MODIFY_PHONE_STATE}
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
* @param fplmns a list of PLMNs to be forbidden.
@@ -6887,7 +6837,7 @@
public int setForbiddenPlmns(@NonNull List<String> fplmns) {
try {
ITelephony telephony = getITelephony();
- if (telephony == null) return 0;
+ if (telephony == null) return -1;
return telephony.setForbiddenPlmns(
getSubId(), APPTYPE_USIM, fplmns, getOpPackageName(), getFeatureId());
} catch (RemoteException ex) {
@@ -6896,7 +6846,7 @@
// This could happen before phone starts
Rlog.e(TAG, "setForbiddenPlmns NullPointerException: " + ex.getMessage());
}
- return 0;
+ return -1;
}
/**
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index b37d7c7..d3fb37f 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -20,6 +20,8 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -30,6 +32,7 @@
import android.telephony.ims.aidl.IImsRcsController;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Log;
import java.util.concurrent.Executor;
@@ -42,6 +45,8 @@
* Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this manager.
* @hide
*/
+@SystemApi
+@TestApi
public class ImsRcsManager implements RegistrationManager {
private static final String TAG = "ImsRcsManager";
@@ -105,7 +110,7 @@
*
* @param capabilities The new availability of the capabilities.
*/
- public void onAvailabilityChanged(RcsFeature.RcsImsCapabilities capabilities) {
+ public void onAvailabilityChanged(@NonNull RcsFeature.RcsImsCapabilities capabilities) {
}
/**@hide*/
@@ -142,6 +147,7 @@
/**{@inheritDoc}*/
@Override
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void registerImsRegistrationCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull RegistrationCallback c)
@@ -159,6 +165,7 @@
/**{@inheritDoc}*/
@Override
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void unregisterImsRegistrationCallback(
@NonNull RegistrationManager.RegistrationCallback c) {
if (c == null) {
@@ -170,6 +177,7 @@
/**{@inheritDoc}*/
@Override
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
@NonNull @ImsRegistrationState Consumer<Integer> stateCallback) {
if (stateCallback == null) {
@@ -184,6 +192,7 @@
/**{@inheritDoc}*/
@Override
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor,
@NonNull @AccessNetworkConstants.TransportType
Consumer<Integer> transportTypeCallback) {
@@ -219,7 +228,7 @@
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public void registerRcsAvailabilityCallback(@CallbackExecutor Executor executor,
+ public void registerRcsAvailabilityCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull AvailabilityCallback c) throws ImsException {
if (c == null) {
throw new IllegalArgumentException("Must include a non-null AvailabilityCallback.");
@@ -231,13 +240,13 @@
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
Log.e(TAG, "Register availability callback: IImsRcsController is null");
- throw new ImsException("Can not find remote IMS service",
+ throw new ImsException("Cannot find remote IMS service",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
c.setExecutor(executor);
try {
- imsRcsController.registerRcsAvailabilityCallback(c.getBinder());
+ imsRcsController.registerRcsAvailabilityCallback(mSubId, c.getBinder());
} catch (RemoteException e) {
Log.e(TAG, "Error calling IImsRcsController#registerRcsAvailabilityCallback", e);
throw new ImsException("Remote IMS Service is not available",
@@ -267,12 +276,12 @@
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
Log.e(TAG, "Unregister availability callback: IImsRcsController is null");
- throw new ImsException("Can not find remote IMS service",
+ throw new ImsException("Cannot find remote IMS service",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
try {
- imsRcsController.unregisterRcsAvailabilityCallback(c.getBinder());
+ imsRcsController.unregisterRcsAvailabilityCallback(mSubId, c.getBinder());
} catch (RemoteException e) {
Log.e(TAG, "Error calling IImsRcsController#unregisterRcsAvailabilityCallback", e);
throw new ImsException("Remote IMS Service is not available",
@@ -287,6 +296,9 @@
* RCS capabilities provided over-the-top by applications.
*
* @param capability The RCS capability to query.
+ * @param radioTech The radio tech that this capability failed for, defined as
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}.
* @return true if the RCS capability is capable for this subscription, false otherwise. This
* does not necessarily mean that we are registered for IMS and the capability is available, but
* rather the subscription is capable of this service over IMS.
@@ -297,17 +309,17 @@
* See {@link ImsException#getCode()} for more information on the error codes.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public boolean isCapable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability)
- throws ImsException {
+ public boolean isCapable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException {
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
Log.e(TAG, "isCapable: IImsRcsController is null");
- throw new ImsException("Can not find remote IMS service",
+ throw new ImsException("Cannot find remote IMS service",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
try {
- return imsRcsController.isCapable(mSubId, capability);
+ return imsRcsController.isCapable(mSubId, capability, radioTech);
} catch (RemoteException e) {
Log.e(TAG, "Error calling IImsRcsController#isCapable", e);
throw new ImsException("Remote IMS Service is not available",
@@ -336,7 +348,7 @@
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
Log.e(TAG, "isAvailable: IImsRcsController is null");
- throw new ImsException("Can not find remote IMS service",
+ throw new ImsException("Cannot find remote IMS service",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index b379bd0..e81bac0 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -26,9 +26,9 @@
* {@hide}
*/
interface IImsRcsController {
- void registerRcsAvailabilityCallback(IImsCapabilityCallback c);
- void unregisterRcsAvailabilityCallback(IImsCapabilityCallback c);
- boolean isCapable(int subId, int capability);
+ void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback c);
+ void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback c);
+ boolean isCapable(int subId, int capability, int radioTech);
boolean isAvailable(int subId, int capability);
// ImsUceAdapter specific
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 72390d0..4cb8575 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -22,7 +22,6 @@
import android.annotation.TestApi;
import android.content.Context;
import android.os.IInterface;
-import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.telephony.SubscriptionManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
@@ -31,6 +30,7 @@
import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.util.RemoteCallbackListExt;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -310,12 +310,12 @@
/** @hide */
protected final Object mLock = new Object();
- private final RemoteCallbackList<IImsFeatureStatusCallback> mStatusCallbacks =
- new RemoteCallbackList<>();
+ private final RemoteCallbackListExt<IImsFeatureStatusCallback> mStatusCallbacks =
+ new RemoteCallbackListExt<>();
private @ImsState int mState = STATE_UNAVAILABLE;
private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
- private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks =
- new RemoteCallbackList<>();
+ private final RemoteCallbackListExt<IImsCapabilityCallback> mCapabilityCallbacks =
+ new RemoteCallbackListExt<>();
private Capabilities mCapabilityStatus = new Capabilities();
/**
@@ -391,7 +391,7 @@
* Internal method called by ImsFeature when setFeatureState has changed.
*/
private void notifyFeatureState(@ImsState int state) {
- mStatusCallbacks.broadcast((c) -> {
+ mStatusCallbacks.broadcastAction((c) -> {
try {
c.notifyImsFeatureStatus(state);
} catch (RemoteException e) {
@@ -470,7 +470,7 @@
synchronized (mLock) {
mCapabilityStatus = caps.copy();
}
- mCapabilityCallbacks.broadcast((callback) -> {
+ mCapabilityCallbacks.broadcastAction((callback) -> {
try {
callback.onCapabilitiesStatusChanged(caps.mCapabilities);
} catch (RemoteException e) {
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 0eaf8dc..884a0bc 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -193,7 +193,6 @@
* of the capability and notify the capability status as true using
* {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
* framework that the capability is available for usage.
- * @hide
*/
public static class RcsImsCapabilities extends Capabilities {
/** @hide*/
@@ -207,7 +206,6 @@
/**
* Undefined capability type for initialization
- * @hide
*/
public static final int CAPABILITY_TYPE_NONE = 0;
@@ -215,7 +213,6 @@
* This carrier supports User Capability Exchange using SIP OPTIONS as defined by the
* framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS.
* If not set, this RcsFeature should not service capability requests.
- * @hide
*/
public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0;
@@ -224,33 +221,27 @@
* framework. If set, the RcsFeature should support capability exchange using a presence
* server. If not set, this RcsFeature should not publish capabilities or service capability
* requests using presence.
- * @hide
*/
public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1;
- /**@hide*/
public RcsImsCapabilities(@RcsImsCapabilityFlag int capabilities) {
super(capabilities);
}
- /**@hide*/
private RcsImsCapabilities(Capabilities c) {
super(c.getMask());
}
- /**@hide*/
@Override
public void addCapabilities(@RcsImsCapabilityFlag int capabilities) {
super.addCapabilities(capabilities);
}
- /**@hide*/
@Override
public void removeCapabilities(@RcsImsCapabilityFlag int capabilities) {
super.removeCapabilities(capabilities);
}
- /**@hide*/
@Override
public boolean isCapable(@RcsImsCapabilityFlag int capabilities) {
return super.isCapable(capabilities);
@@ -295,10 +286,9 @@
* set, the {@link RcsFeature} has brought up the capability and is ready for framework
* requests. To change the status of the capabilities
* {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called.
- * @hide
*/
@Override
- public final RcsImsCapabilities queryCapabilityStatus() {
+ public @NonNull final RcsImsCapabilities queryCapabilityStatus() {
return new RcsImsCapabilities(super.queryCapabilityStatus());
}
@@ -306,7 +296,6 @@
* Notify the framework that the capabilities status has changed. If a capability is enabled,
* this signals to the framework that the capability has been initialized and is ready.
* Call {@link #queryCapabilityStatus()} to return the current capability status.
- * @hide
*/
public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) {
if (c == null) {
@@ -321,7 +310,6 @@
* {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to
* enable or disable capability A, this method should return the correct configuration for
* capability A afterwards (until it has changed).
- * @hide
*/
public boolean queryCapabilityConfiguration(
@RcsImsCapabilities.RcsImsCapabilityFlag int capability,
@@ -343,11 +331,10 @@
* If for some reason one or more of these capabilities can not be enabled/disabled,
* {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should
* be called for each capability change that resulted in an error.
- * @hide
*/
@Override
- public void changeEnabledCapabilities(CapabilityChangeRequest request,
- CapabilityCallbackProxy c) {
+ public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
+ @NonNull CapabilityCallbackProxy c) {
// Base Implementation - Override to provide functionality
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index 4c0de7f..d18e93c 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -21,7 +21,6 @@
import android.annotation.TestApi;
import android.content.Context;
import android.os.PersistableBundle;
-import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsConfigCallback;
@@ -29,6 +28,7 @@
import com.android.ims.ImsConfig;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.util.RemoteCallbackListExt;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -248,7 +248,8 @@
})
public @interface SetConfigResult {}
- private final RemoteCallbackList<IImsConfigCallback> mCallbacks = new RemoteCallbackList<>();
+ private final RemoteCallbackListExt<IImsConfigCallback> mCallbacks =
+ new RemoteCallbackListExt<>();
ImsConfigStub mImsConfigStub;
/**
@@ -289,7 +290,7 @@
if (mCallbacks == null) {
return;
}
- mCallbacks.broadcast(c -> {
+ mCallbacks.broadcastAction(c -> {
try {
c.onIntConfigChanged(item, value);
} catch (RemoteException e) {
@@ -303,7 +304,7 @@
if (mCallbacks == null) {
return;
}
- mCallbacks.broadcast(c -> {
+ mCallbacks.broadcastAction(c -> {
try {
c.onStringConfigChanged(item, value);
} catch (RemoteException e) {
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index c0f16e5..14a64d2 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -20,7 +20,6 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.net.Uri;
-import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.RegistrationManager;
@@ -29,6 +28,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.util.RemoteCallbackListExt;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -94,8 +94,8 @@
}
};
- private final RemoteCallbackList<IImsRegistrationCallback> mCallbacks
- = new RemoteCallbackList<>();
+ private final RemoteCallbackListExt<IImsRegistrationCallback> mCallbacks =
+ new RemoteCallbackListExt<>();
private final Object mLock = new Object();
// Locked on mLock
private @ImsRegistrationTech
@@ -129,7 +129,7 @@
*/
public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERED);
- mCallbacks.broadcast((c) -> {
+ mCallbacks.broadcastAction((c) -> {
try {
c.onRegistered(imsRadioTech);
} catch (RemoteException e) {
@@ -147,7 +147,7 @@
*/
public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERING);
- mCallbacks.broadcast((c) -> {
+ mCallbacks.broadcastAction((c) -> {
try {
c.onRegistering(imsRadioTech);
} catch (RemoteException e) {
@@ -175,7 +175,7 @@
*/
public final void onDeregistered(ImsReasonInfo info) {
updateToDisconnectedState(info);
- mCallbacks.broadcast((c) -> {
+ mCallbacks.broadcastAction((c) -> {
try {
c.onDeregistered(info);
} catch (RemoteException e) {
@@ -194,7 +194,7 @@
*/
public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
ImsReasonInfo info) {
- mCallbacks.broadcast((c) -> {
+ mCallbacks.broadcastAction((c) -> {
try {
c.onTechnologyChangeFailed(imsRadioTech, info);
} catch (RemoteException e) {
@@ -210,7 +210,7 @@
* @param uris
*/
public final void onSubscriberAssociatedUriChanged(Uri[] uris) {
- mCallbacks.broadcast((c) -> {
+ mCallbacks.broadcastAction((c) -> {
try {
c.onSubscriberAssociatedUriChanged(uris);
} catch (RemoteException e) {
diff --git a/telephony/java/com/android/internal/telephony/util/RemoteCallbackListExt.java b/telephony/java/com/android/internal/telephony/util/RemoteCallbackListExt.java
new file mode 100644
index 0000000..d66bda9
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/util/RemoteCallbackListExt.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.util;
+
+import android.os.IInterface;
+import android.os.RemoteCallbackList;
+
+import java.util.function.Consumer;
+
+/**
+ * Extension of RemoteCallbackList
+ * @param <E> defines the type of registered callbacks
+ */
+public class RemoteCallbackListExt<E extends IInterface> extends RemoteCallbackList<E> {
+ /**
+ * Performs {@code action} on each callback, calling
+ * {@link RemoteCallbackListExt#beginBroadcast()}
+ * /{@link RemoteCallbackListExt#finishBroadcast()} before/after looping
+ * @param action to be performed on each callback
+ *
+ */
+ public void broadcastAction(Consumer<E> action) {
+ int itemCount = beginBroadcast();
+ try {
+ for (int i = 0; i < itemCount; i++) {
+ action.accept(getBroadcastItem(i));
+ }
+ } finally {
+ finishBroadcast();
+ }
+ }
+}
diff --git a/wifi/Android.bp b/wifi/Android.bp
new file mode 100644
index 0000000..26064cb
--- /dev/null
+++ b/wifi/Android.bp
@@ -0,0 +1,102 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+filegroup {
+ name: "framework-wifi-updatable-sources",
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.aidl",
+ ],
+ exclude_srcs: [
+ ":framework-wifi-non-updatable-sources"
+ ],
+ path: "java",
+}
+
+filegroup {
+ name: "framework-wifi-non-updatable-sources",
+ srcs: [
+ // TODO(b/146011398) package android.net.wifi is now split amongst 2 jars: framework.jar and
+ // framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache
+ // to a separate package.
+ "java/android/net/wifi/WifiNetworkScoreCache.java",
+ "java/android/net/wifi/WifiCondManager.java",
+ "java/android/net/wifi/wificond/*.java",
+ ":libwificond_ipc_aidl",
+ ],
+}
+
+filegroup {
+ name: "framework-wifi-annotations",
+ srcs: ["java/android/net/wifi/WifiAnnotations.java"],
+}
+
+java_library {
+ name: "framework-wifi",
+ sdk_version: "core_platform", // TODO(b/140299412) should be core_current
+ libs: [
+ "framework-minus-apex", // TODO(b/140299412) should be framework-system-stubs
+ ],
+ srcs: [
+ ":framework-wifi-updatable-sources",
+ ],
+ installable: true,
+ optimize: {
+ enabled: false
+ }
+}
+
+metalava_wifi_docs_args =
+ "--hide-package com.android.server " +
+ "--error UnhiddenSystemApi " +
+ "--hide RequiresPermission " +
+ "--hide MissingPermission " +
+ "--hide BroadcastBehavior " +
+ "--hide HiddenSuperclass " +
+ "--hide DeprecationMismatch " +
+ "--hide UnavailableSymbol " +
+ "--hide SdkConstant " +
+ "--hide HiddenTypeParameter " +
+ "--hide Todo --hide Typo " +
+ "--hide HiddenTypedefConstant " +
+ "--show-annotation android.annotation.SystemApi "
+
+droidstubs {
+ name: "framework-wifi-stubs-srcs",
+ srcs: [
+ ":framework-annotations",
+ ":framework-wifi-updatable-sources",
+ ],
+ aidl: {
+ include_dirs: ["frameworks/base/core/java"],
+ },
+ args: metalava_wifi_docs_args,
+ sdk_version: "core_current",
+ libs: ["android_system_stubs_current"],
+}
+
+java_library {
+ name: "framework-wifi-stubs",
+ srcs: [":framework-wifi-stubs-srcs"],
+ aidl: {
+ export_include_dirs: [
+ "java",
+ ],
+ },
+ sdk_version: "core_current",
+ libs: ["android_system_stubs_current"],
+ installable: false,
+}
+
diff --git a/wifi/java/android/net/wifi/WifiAnnotations.java b/wifi/java/android/net/wifi/WifiAnnotations.java
new file mode 100644
index 0000000..4a7dee1
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiAnnotations.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.annotation.IntDef;
+import android.annotation.StringDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Wifi annotations meant to be statically linked into client modules, since they cannot be
+ * exposed as @SystemApi.
+ *
+ * e.g. {@link IntDef}, {@link StringDef}
+ *
+ * @hide
+ */
+public final class WifiAnnotations {
+ private WifiAnnotations() {}
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"SCAN_TYPE_"}, value = {
+ WifiScanner.SCAN_TYPE_LOW_LATENCY,
+ WifiScanner.SCAN_TYPE_LOW_POWER,
+ WifiScanner.SCAN_TYPE_HIGH_ACCURACY})
+ public @interface ScanType {}
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"WIFI_BAND_"}, value = {
+ WifiScanner.WIFI_BAND_UNSPECIFIED,
+ WifiScanner.WIFI_BAND_24_GHZ,
+ WifiScanner.WIFI_BAND_5_GHZ,
+ WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY,
+ WifiScanner.WIFI_BAND_6_GHZ})
+ public @interface WifiBandBasic {}
+}
diff --git a/wifi/java/android/net/wifi/WifiCondManager.java b/wifi/java/android/net/wifi/WifiCondManager.java
index 9ae7e3a..c05ba34 100644
--- a/wifi/java/android/net/wifi/WifiCondManager.java
+++ b/wifi/java/android/net/wifi/WifiCondManager.java
@@ -725,7 +725,7 @@
/**
* Return scan type for the parcelable {@link SingleScanSettings}
*/
- private static int getScanType(@WifiScanner.ScanType int scanType) {
+ private static int getScanType(@WifiAnnotations.ScanType int scanType) {
switch (scanType) {
case WifiScanner.SCAN_TYPE_LOW_LATENCY:
return IWifiScannerImpl.SCAN_TYPE_LOW_SPAN;
@@ -746,7 +746,7 @@
* @param hiddenNetworkSSIDs List of hidden networks to be scanned for.
* @return Returns true on success.
*/
- public boolean scan(@NonNull String ifaceName, @WifiScanner.ScanType int scanType,
+ public boolean scan(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType,
Set<Integer> freqs, List<byte[]> hiddenNetworkSSIDs) {
IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
if (scannerImpl == null) {
@@ -868,7 +868,7 @@
* @return frequencies vector of valid frequencies (MHz), or null for error.
* @throws IllegalArgumentException if band is not recognized.
*/
- public int [] getChannelsForBand(@WifiScanner.WifiBandBasic int band) {
+ public int [] getChannelsForBand(@WifiAnnotations.WifiBandBasic int band) {
if (mWificond == null) {
Log.e(TAG, "No valid wificond scanner interface handler");
return null;
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 760497b..8fedda4 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -89,16 +89,6 @@
/** 6 GHz band */
public static final int WIFI_BAND_6_GHZ = 1 << WIFI_BAND_INDEX_6_GHZ;
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"WIFI_BAND_"}, value = {
- WIFI_BAND_UNSPECIFIED,
- WIFI_BAND_24_GHZ,
- WIFI_BAND_5_GHZ,
- WIFI_BAND_5_GHZ_DFS_ONLY,
- WIFI_BAND_6_GHZ})
- public @interface WifiBandBasic {}
-
/**
* Combination of bands
* Note that those are only the common band combinations,
@@ -249,14 +239,6 @@
*/
public static final int REPORT_EVENT_NO_BATCH = (1 << 2);
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"SCAN_TYPE_"}, value = {
- SCAN_TYPE_LOW_LATENCY,
- SCAN_TYPE_LOW_POWER,
- SCAN_TYPE_HIGH_ACCURACY})
- public @interface ScanType {}
-
/**
* Optimize the scan for lower latency.
* @see ScanSettings#type
@@ -354,7 +336,7 @@
* {@link #SCAN_TYPE_HIGH_ACCURACY}.
* Default value: {@link #SCAN_TYPE_LOW_LATENCY}.
*/
- @ScanType
+ @WifiAnnotations.ScanType
@RequiresPermission(android.Manifest.permission.NETWORK_STACK)
public int type = SCAN_TYPE_LOW_LATENCY;
/**