summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp2
-rw-r--r--ApiDocs.bp9
-rw-r--r--StubLibraries.bp5
-rw-r--r--cmds/idmap2/idmap2/Lookup.cpp4
-rw-r--r--cmds/idmap2/libidmap2/XmlParser.cpp2
-rw-r--r--core/api/current.txt15
-rw-r--r--core/api/system-current.txt2
-rw-r--r--core/java/android/app/ContextImpl.java18
-rw-r--r--core/java/android/app/LoadedApk.java28
-rw-r--r--core/java/android/app/WindowContext.java22
-rw-r--r--core/java/android/app/admin/DevicePolicyManagerInternal.java8
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java40
-rw-r--r--core/java/android/content/Context.java40
-rw-r--r--core/java/android/content/ContextWrapper.java7
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java8
-rw-r--r--core/java/android/net/IConnectivityManager.aidl7
-rw-r--r--core/java/android/net/vcn/VcnConfig.java109
-rw-r--r--core/java/android/net/vcn/VcnGatewayConnectionConfig.java401
-rw-r--r--core/java/android/net/vcn/VcnManager.java40
-rw-r--r--core/java/android/os/IRecoverySystem.aidl1
-rw-r--r--core/java/android/os/RecoverySystem.java29
-rw-r--r--core/java/android/os/incremental/OWNERS5
-rw-r--r--core/java/android/provider/ContactsContract.java10
-rw-r--r--core/java/android/service/attestation/OWNERS2
-rw-r--r--core/java/android/view/inputmethod/EditorInfo.java23
-rw-r--r--core/java/android/widget/TextView.java4
-rw-r--r--core/java/android/window/DisplayAreaOrganizer.java18
-rw-r--r--core/java/android/window/IDisplayAreaOrganizerController.aidl16
-rw-r--r--core/java/com/android/internal/os/KernelWakelockReader.java32
-rw-r--r--core/res/Android.bp7
-rw-r--r--core/res/AndroidManifest.xml1
-rw-r--r--core/tests/coretests/src/android/widget/TextViewTest.java22
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java1
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java159
-rw-r--r--data/keyboards/keyboards.mk12
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java25
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java7
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java4
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java111
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java25
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java35
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java26
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java48
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java31
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java81
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java2
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java29
-rw-r--r--keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java67
-rw-r--r--keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt10
-rw-r--r--libs/androidfw/AssetManager2.cpp2
-rw-r--r--libs/androidfw/ResourceTypes.cpp4
-rw-r--r--libs/androidfw/ResourceUtils.cpp12
-rw-r--r--media/java/android/media/AudioPlaybackConfiguration.java65
-rw-r--r--media/java/android/media/AudioTrack.java27
-rw-r--r--media/java/android/media/HwAudioSource.java26
-rwxr-xr-xmedia/java/android/media/IAudioService.aidl2
-rw-r--r--media/java/android/media/MediaCodecInfo.java33
-rw-r--r--media/java/android/media/MediaPlayer.java87
-rw-r--r--media/java/android/media/OWNERS1
-rw-r--r--media/java/android/media/PlayerBase.java39
-rw-r--r--media/java/android/media/SoundPool.java3
-rw-r--r--media/jni/android_media_tv_Tuner.cpp613
-rw-r--r--media/jni/android_media_tv_Tuner.h65
-rw-r--r--media/jni/tuner/DemuxClient.cpp6
-rw-r--r--media/jni/tuner/FilterClient.cpp59
-rw-r--r--media/jni/tuner/FilterClient.h19
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java29
-rw-r--r--packages/SystemUI/res/drawable/ic_fingerprint.xml59
-rw-r--r--packages/SystemUI/res/layout/status_bar_notification_row.xml1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java9
-rw-r--r--packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java7
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java145
-rw-r--r--services/core/java/com/android/server/TestNetworkService.java41
-rw-r--r--services/core/java/com/android/server/VcnManagementService.java210
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java30
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java4
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java10
-rw-r--r--services/core/java/com/android/server/audio/PlaybackActivityMonitor.java28
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java2
-rw-r--r--services/core/java/com/android/server/compat/CompatChange.java12
-rw-r--r--services/core/java/com/android/server/compat/CompatConfig.java8
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompat.java15
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java35
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java35
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java131
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsService.java5
-rw-r--r--services/core/java/com/android/server/powerstats/ProtoStreamUtils.java42
-rw-r--r--services/core/java/com/android/server/recoverysystem/RecoverySystemService.java57
-rw-r--r--services/core/java/com/android/server/vcn/Android.bp4
-rw-r--r--services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java314
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java55
-rw-r--r--services/core/jni/Android.bp1
-rw-r--r--services/core/jni/OWNERS1
-rw-r--r--services/core/jni/com_android_server_powerstats_PowerStatsService.cpp440
-rw-r--r--services/core/jni/onload.cpp2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java9
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java27
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java51
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java71
-rw-r--r--telephony/java/android/telephony/ims/ImsRcsManager.java9
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl9
-rw-r--r--test-mock/src/android/test/mock/MockContext.java5
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java15
-rw-r--r--tests/vcn/java/android/net/vcn/VcnConfigTest.java83
-rw-r--r--tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java143
-rw-r--r--tests/vcn/java/com/android/server/VcnManagementServiceTest.java206
-rw-r--r--tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java333
-rw-r--r--tools/aapt2/util/Util.cpp4
-rw-r--r--tools/validatekeymaps/Main.cpp4
129 files changed, 4342 insertions, 1190 deletions
diff --git a/Android.bp b/Android.bp
index 598f4fcaf620..f8e5e8e6f587 100644
--- a/Android.bp
+++ b/Android.bp
@@ -349,6 +349,7 @@ filegroup {
":framework-telecomm-sources",
":framework-telephony-common-sources",
":framework-telephony-sources",
+ ":framework-vcn-util-sources",
":framework-wifi-annotations",
":framework-wifi-non-updatable-sources",
":PacProcessor-aidl-sources",
@@ -1373,7 +1374,6 @@ filegroup {
// TODO(b/145644363): move this to under StubLibraries.bp or ApiDocs.bp
metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " +
- "--ignore-classes-on-classpath " +
"--hide-package com.android.server " +
"--hide-package android.audio.policy.configuration.V7_0 " +
"--error UnhiddenSystemApi " +
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 3d6bdbf19264..5f4f3c2f175b 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -96,7 +96,9 @@ droidstubs {
arg_files: [
"core/res/AndroidManifest.xml",
],
- args: metalava_framework_docs_args,
+ args: metalava_framework_docs_args +
+ // Needed for hidden libcore annotations for now.
+ " --ignore-classes-on-classpath ",
write_sdk_values: true,
}
@@ -106,7 +108,10 @@ droidstubs {
arg_files: [
"core/res/AndroidManifest.xml",
],
- args: metalava_framework_docs_args + " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
+ args: metalava_framework_docs_args +
+ // Needed for hidden libcore annotations for now.
+ " --ignore-classes-on-classpath " +
+ " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
write_sdk_values: true,
}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 12ee889387ec..05967558178e 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -50,7 +50,9 @@ stubs_defaults {
":art.module.public.api{.public.stubs.source}",
"**/package.html",
],
- sdk_version: "core_platform",
+ sdk_version: "none",
+ system_modules: "none",
+ java_version: "1.8",
arg_files: ["core/res/AndroidManifest.xml"],
// TODO(b/147699819, b/169090544): remove below aidl includes.
aidl: {
@@ -80,6 +82,7 @@ stubs_defaults {
"android.hardware.usb.gadget-V1.0-java",
"android.hardware.vibrator-V1.3-java",
"framework-protos",
+ "stable.core.platform.api.stubs",
// There are a few classes from modules used as type arguments that
// need to be resolved by metalava. For now, we can use a previously
// finalized stub library to resolve them. If a new class gets added,
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index 8323d0ba2415..437180d3d1be 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -71,7 +71,7 @@ Result<ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am, const
}
// next, try to parse as a package:type/name string
- if (auto resid = am.GetResourceId(res, "", fallback_package)) {
+ if (auto resid = am.GetResourceId(res, "", fallback_package); resid.ok()) {
return *resid;
}
@@ -94,7 +94,7 @@ void PrintValue(AssetManager2* const am, const AssetManager2::SelectedValue& val
case Res_value::TYPE_STRING: {
const ResStringPool* pool = am->GetStringPoolForCookie(value.cookie);
out->append("\"");
- if (auto str = pool->string8ObjectAt(value.data)) {
+ if (auto str = pool->string8ObjectAt(value.data); str.ok()) {
out->append(*str);
}
} break;
diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp
index 7c55b64566f2..4030b83b3a41 100644
--- a/cmds/idmap2/libidmap2/XmlParser.cpp
+++ b/cmds/idmap2/libidmap2/XmlParser.cpp
@@ -98,7 +98,7 @@ Result<std::string> XmlParser::Node::GetAttributeStringValue(const std::string&
switch ((*value).dataType) {
case Res_value::TYPE_STRING: {
- if (auto str = parser_.getStrings().string8ObjectAt((*value).data)) {
+ if (auto str = parser_.getStrings().string8ObjectAt((*value).data); str.ok()) {
return std::string(str->string());
}
break;
diff --git a/core/api/current.txt b/core/api/current.txt
index 1c50e14d0caa..1ed78bb759d2 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -10134,6 +10134,7 @@ package android.content {
method public abstract android.content.Context createDisplayContext(@NonNull android.view.Display);
method public abstract android.content.Context createPackageContext(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public android.content.Context createWindowContext(int, @Nullable android.os.Bundle);
+ method @NonNull public android.content.Context createWindowContext(@NonNull android.view.Display, int, @Nullable android.os.Bundle);
method public abstract String[] databaseList();
method public abstract boolean deleteDatabase(String);
method public abstract boolean deleteFile(String);
@@ -19931,7 +19932,6 @@ package android.media {
public final class AudioPlaybackConfiguration implements android.os.Parcelable {
method public int describeContents();
method public android.media.AudioAttributes getAudioAttributes();
- method @Nullable public android.media.AudioDeviceInfo getAudioDevice();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioPlaybackConfiguration> CREATOR;
}
@@ -33635,8 +33635,8 @@ package android.provider {
}
protected static interface ContactsContract.DataColumns {
- field public static final String CARRIER_PRESENCE = "carrier_presence";
- field public static final int CARRIER_PRESENCE_VT_CAPABLE = 1; // 0x1
+ field @Deprecated public static final String CARRIER_PRESENCE = "carrier_presence";
+ field @Deprecated public static final int CARRIER_PRESENCE_VT_CAPABLE = 1; // 0x1
field public static final String DATA1 = "data1";
field public static final String DATA10 = "data10";
field public static final String DATA11 = "data11";
@@ -40689,7 +40689,7 @@ package android.telephony {
field public static final String KEY_USE_ACS_FOR_RCS_BOOL = "use_acs_for_rcs_bool";
field public static final String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool";
field public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
- field public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
+ field @Deprecated public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
field public static final String KEY_USE_RCS_SIP_OPTIONS_BOOL = "use_rcs_sip_options_bool";
field public static final String KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL = "use_wfc_home_network_mode_in_roaming_network_bool";
field public static final String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
@@ -40729,9 +40729,12 @@ package android.telephony {
}
public static final class CarrierConfigManager.Ims {
+ field public static final String KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL = "ims.enable_presence_capability_exchange_bool";
+ field public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL = "ims.enable_presence_group_subscribe_bool";
field public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = "ims.enable_presence_publish_bool";
field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool";
field public static final String KEY_PREFIX = "ims.";
+ field public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = "ims.rcs_bulk_capability_exchange_bool";
field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int";
}
@@ -42296,6 +42299,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean doesSwitchMultiSimConfigTriggerReboot();
method public int getActiveModemCount();
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo();
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getCallComposerStatus();
method public int getCallState();
method public int getCardIdForDefaultEuicc();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @WorkerThread public android.os.PersistableBundle getCarrierConfig();
@@ -42392,6 +42396,7 @@ package android.telephony {
method @Deprecated public String sendEnvelopeWithStatus(String);
method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
method public void sendVisualVoicemailSms(String, int, String, android.app.PendingIntent);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallComposerStatus(int);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabledForReason(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setForbiddenPlmns(@NonNull java.util.List<java.lang.String>);
@@ -42432,6 +42437,8 @@ package android.telephony {
field public static final int APPTYPE_USIM = 2; // 0x2
field public static final int AUTHTYPE_EAP_AKA = 129; // 0x81
field public static final int AUTHTYPE_EAP_SIM = 128; // 0x80
+ field public static final int CALL_COMPOSER_STATUS_OFF = 0; // 0x0
+ field public static final int CALL_COMPOSER_STATUS_ON = 1; // 0x1
field public static final int CALL_STATE_IDLE = 0; // 0x0
field public static final int CALL_STATE_OFFHOOK = 2; // 0x2
field public static final int CALL_STATE_RINGING = 1; // 0x1
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 85da7c37805e..5679c8958bbc 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -8062,7 +8062,7 @@ package android.os {
method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void prepareForUnattendedUpdate(@NonNull android.content.Context, @NonNull String, @Nullable android.content.IntentSender) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener, android.os.Handler) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener) throws java.io.IOException;
- method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException;
+ method @Deprecated @RequiresPermission(android.Manifest.permission.RECOVERY) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException;
method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, boolean) throws java.io.IOException;
method @RequiresPermission(allOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootWipeAb(android.content.Context, java.io.File, String) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 124cf71edc9c..700d8ff36446 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2468,8 +2468,9 @@ class ContextImpl extends Context {
return context;
}
+ @NonNull
@Override
- public @NonNull WindowContext createWindowContext(int type, Bundle options) {
+ public WindowContext createWindowContext(int type, @NonNull Bundle options) {
if (getDisplay() == null) {
throw new UnsupportedOperationException("WindowContext can only be created from "
+ "other visual contexts, such as Activity or one created with "
@@ -2478,13 +2479,26 @@ class ContextImpl extends Context {
return new WindowContext(this, type, options);
}
- ContextImpl createBaseWindowContext(IBinder token) {
+ @NonNull
+ @Override
+ public WindowContext createWindowContext(@NonNull Display display, int type,
+ @NonNull Bundle options) {
+ if (display == null) {
+ throw new IllegalArgumentException("Display must not be null");
+ }
+ return new WindowContext(this, display, type, options);
+ }
+
+ ContextImpl createBaseWindowContext(IBinder token, Display display) {
ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag,
mSplitName, token, mUser, mFlags, mClassLoader, null);
// Window contexts receive configurations directly from the server and as such do not
// need to override their display in ResourcesManager.
context.mForceDisplayOverrideInResources = false;
context.mContextType = CONTEXT_TYPE_WINDOW_CONTEXT;
+ if (display != null) {
+ context.mDisplay = display;
+ }
return context;
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 99785e1e73f4..50853a3bcf9c 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -729,6 +729,23 @@ public final class LoadedApk {
}
}
+ private StrictMode.VmPolicy allowVmViolations() {
+ if (mActivityThread == null) {
+ // When LoadedApk is used without an ActivityThread (usually in a
+ // zygote context), don't call into StrictMode, as it initializes
+ // the binder subsystem, which we don't want.
+ return null;
+ }
+
+ return StrictMode.allowVmViolations();
+ }
+
+ private void setVmPolicy(StrictMode.VmPolicy policy) {
+ if (mActivityThread != null && policy != null) {
+ StrictMode.setVmPolicy(policy);
+ }
+ }
+
private void createOrUpdateClassLoaderLocked(List<String> addedPaths) {
if (mPackageName.equals("android")) {
// Note: This branch is taken for system server and we don't need to setup
@@ -984,13 +1001,20 @@ public final class LoadedApk {
// Temporarily disable logging of disk reads on the Looper thread as this is necessary -
// and the loader will access the directory anyway if we don't check it.
- StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
+ StrictMode.ThreadPolicy oldThreadPolicy = allowThreadDiskReads();
+
+ // Also disable logging of access to /data/user before CE storage is unlocked. The check
+ // below will return false (because the directory name we pass will not match the
+ // encrypted one), but that's correct.
+ StrictMode.VmPolicy oldVmPolicy = allowVmViolations();
+
try {
// We are constructing a classloader for a different package. It is likely,
// but not certain, that we can't acccess its app data dir - so check.
return new File(mDataDir).canExecute();
} finally {
- setThreadPolicy(oldPolicy);
+ setThreadPolicy(oldThreadPolicy);
+ setVmPolicy(oldVmPolicy);
}
}
diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java
index 5f72bac89d7b..14ed414da9d0 100644
--- a/core/java/android/app/WindowContext.java
+++ b/core/java/android/app/WindowContext.java
@@ -26,6 +26,7 @@ import android.content.ContextWrapper;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.view.Display;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerImpl;
@@ -59,13 +60,27 @@ public class WindowContext extends ContextWrapper {
* @hide
*/
public WindowContext(@NonNull Context base, int type, @Nullable Bundle options) {
+ this(base, null /* display */, type, options);
+ }
+
+ /**
+ * Default constructor. Will generate a {@link WindowTokenClient} and attach this context to
+ * the token.
+ *
+ * @param base Base {@link Context} for this new instance.
+ * @param display the {@link Display} to override.
+ * @param type Window type to be used with this context.
+ * @hide
+ */
+ public WindowContext(@NonNull Context base, @Nullable Display display, int type,
+ @Nullable Bundle options) {
// Correct base context will be built once the token is resolved, so passing 'null' here.
super(null /* base */);
mWms = WindowManagerGlobal.getWindowManagerService();
mToken = new WindowTokenClient();
- final ContextImpl contextImpl = createBaseWindowContext(base, mToken);
+ final ContextImpl contextImpl = createBaseWindowContext(base, mToken, display);
attachBaseContext(contextImpl);
contextImpl.setOuterContext(this);
@@ -93,9 +108,10 @@ public class WindowContext extends ContextWrapper {
Reference.reachabilityFence(this);
}
- private static ContextImpl createBaseWindowContext(Context outer, IBinder token) {
+ private static ContextImpl createBaseWindowContext(Context outer, IBinder token,
+ Display display) {
final ContextImpl contextImpl = ContextImpl.getImpl(outer);
- return contextImpl.createBaseWindowContext(token);
+ return contextImpl.createBaseWindowContext(token, display);
}
@Override
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index a0d2977cf09a..ce2fd4fb60b2 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -231,13 +231,7 @@ public abstract class DevicePolicyManagerInternal {
* Returns the profile owner component for the given user, or {@code null} if there is not one.
*/
@Nullable
- public abstract ComponentName getProfileOwnerAsUser(@UserIdInt int userId);
-
- /**
- * Returns the user id of the device owner, or {@link UserHandle#USER_NULL} if there is not one.
- */
- @UserIdInt
- public abstract int getDeviceOwnerUserId();
+ public abstract ComponentName getProfileOwnerAsUser(int userHandle);
/**
* Returns whether the given package is a device owner or a profile owner in the calling user.
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 1713a0c158c3..406fe8d5023c 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2967,6 +2967,16 @@ public final class BluetoothAdapter {
}
});
}
+ synchronized (mBluetoothConnectionCallbackExecutorMap) {
+ if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
+ try {
+ mService.registerBluetoothConnectionCallback(mConnectionCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onBluetoothServiceUp: Failed to register bluetooth"
+ + "connection callback", e);
+ }
+ }
+ }
}
public void onBluetoothServiceDown() {
@@ -3616,25 +3626,25 @@ public final class BluetoothAdapter {
return false;
}
- // If the callback map is empty, we register the service-to-app callback
- if (mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
- try {
- mServiceLock.readLock().lock();
- if (mService != null) {
- if (!mService.registerBluetoothConnectionCallback(mConnectionCallback)) {
- return false;
+ synchronized (mBluetoothConnectionCallbackExecutorMap) {
+ // If the callback map is empty, we register the service-to-app callback
+ if (mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ if (!mService.registerBluetoothConnectionCallback(mConnectionCallback)) {
+ return false;
+ }
}
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ mBluetoothConnectionCallbackExecutorMap.remove(callback);
+ } finally {
+ mServiceLock.readLock().unlock();
}
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- mBluetoothConnectionCallbackExecutorMap.remove(callback);
- } finally {
- mServiceLock.readLock().unlock();
}
- }
- // Adds the passed in callback to our map of callbacks to executors
- synchronized (mBluetoothConnectionCallbackExecutorMap) {
+ // Adds the passed in callback to our map of callbacks to executors
if (mBluetoothConnectionCallbackExecutorMap.containsKey(callback)) {
throw new IllegalArgumentException("This callback has already been registered");
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ac576a80b5d2..abe7fdae0bf7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5989,22 +5989,22 @@ public abstract class Context {
* Creating a window context is an expensive operation. Misuse of this API may lead to a huge
* performance drop. The best practice is to use the same window context when possible.
* An approach is to create one window context with specific window type and display and
- * use it everywhere it's needed..
+ * use it everywhere it's needed.
* </p>
*
* @param type Window type in {@link WindowManager.LayoutParams}
- * @param options Bundle used to pass window-related options.
- * @return A {@link Context} that can be used to create windows.
- * @throws UnsupportedOperationException if this is called on a non-UI context, such as
- * {@link android.app.Application Application} or {@link android.app.Service Service}.
+ * @param options A bundle used to pass window-related options
+ * @return A {@link Context} that can be used to create
+ * non-{@link android.app.Activity activity} windows.
*
* @see #getSystemService(String)
* @see #getSystemService(Class)
* @see #WINDOW_SERVICE
* @see #LAYOUT_INFLATER_SERVICE
* @see #WALLPAPER_SERVICE
- * @throws UnsupportedOperationException if this {@link Context} does not attach to a display or
- * the current number of window contexts without adding any view by
+ * @throws UnsupportedOperationException if this {@link Context} does not attach to a display,
+ * such as {@link android.app.Application Application} or {@link android.app.Service Service},
+ * or the current number of window contexts without adding any view by
* {@link WindowManager#addView} <b>exceeds five</b>.
*/
@UiContext
@@ -6014,6 +6014,32 @@ public abstract class Context {
}
/**
+ * A special version of {@link #createWindowContext(int, Bundle)} which also takes
+ * {@link Display}. The only difference between this API and
+ * {@link #createWindowContext(int, Bundle)} is that this API can create window context from
+ * any context even if the context which is not associated to a {@link Display} instance.
+ *
+ * @param display The {@link Display} to associate with
+ * @param type Window type in {@link WindowManager.LayoutParams}
+ * @param options A bundle used to pass window-related options.
+ * @return A {@link Context} that can be used to create
+ * non-{@link android.app.Activity activity} windows.
+ * @throws IllegalArgumentException if the {@link Display} is {@code null}.
+ *
+ * @see #getSystemService(String)
+ * @see #getSystemService(Class)
+ * @see #WINDOW_SERVICE
+ * @see #LAYOUT_INFLATER_SERVICE
+ * @see #WALLPAPER_SERVICE
+ */
+ @UiContext
+ @NonNull
+ public Context createWindowContext(@NonNull Display display, @WindowType int type,
+ @Nullable Bundle options) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+
+ /**
* Return a new Context object for the current Context but attribute to a different tag.
* In complex apps attribution tagging can be used to distinguish between separate logical
* parts.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 56da3cb0eb02..e450c08de280 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -988,6 +988,13 @@ public class ContextWrapper extends Context {
}
@Override
+ @NonNull
+ public Context createWindowContext(@NonNull Display display, @WindowType int type,
+ @Nullable Bundle options) {
+ return mBase.createWindowContext(display, type, options);
+ }
+
+ @Override
public @NonNull Context createAttributionContext(@Nullable String attributionTag) {
return mBase.createAttributionContext(attributionTag);
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 6831eca32f72..df9a7c2cb586 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1700,8 +1700,12 @@ public class InputMethodService extends AbstractInputMethodService {
if (config.orientation != Configuration.ORIENTATION_LANDSCAPE) {
return false;
}
- if (mInputEditorInfo != null
- && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0) {
+ if ((mInputEditorInfo != null
+ && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0)
+ // If app window has portrait orientation, regardless of what display orientation
+ // is, IME shouldn't use fullscreen-mode.
+ || (mInputEditorInfo.internalImeOptions
+ & EditorInfo.IME_FLAG_APP_WINDOW_PORTRAIT) != 0) {
return false;
}
return true;
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index fb0128363310..95a2f2efeb7d 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -31,6 +31,7 @@ import android.net.ISocketKeepaliveCallback;
import android.net.ProxyInfo;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.INetworkActivityListener;
import android.os.Messenger;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
@@ -233,4 +234,10 @@ interface IConnectivityManager
in PersistableBundle extras);
void systemReady();
+
+ void registerNetworkActivityListener(in INetworkActivityListener l);
+
+ void unregisterNetworkActivityListener(in INetworkActivityListener l);
+
+ boolean isDefaultNetworkActive();
}
diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java
index 148acf130857..d4a3fa7411b1 100644
--- a/core/java/android/net/vcn/VcnConfig.java
+++ b/core/java/android/net/vcn/VcnConfig.java
@@ -15,30 +15,104 @@
*/
package android.net.vcn;
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
/**
* This class represents a configuration for a Virtual Carrier Network.
*
+ * <p>Each {@link VcnGatewayConnectionConfig} instance added represents a connection that will be
+ * brought up on demand based on active {@link NetworkRequest}(s).
+ *
+ * @see VcnManager for more information on the Virtual Carrier Network feature
* @hide
*/
public final class VcnConfig implements Parcelable {
@NonNull private static final String TAG = VcnConfig.class.getSimpleName();
- private VcnConfig() {
+ private static final String GATEWAY_CONNECTION_CONFIGS_KEY = "mGatewayConnectionConfigs";
+ @NonNull private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs;
+
+ private VcnConfig(@NonNull Set<VcnGatewayConnectionConfig> tunnelConfigs) {
+ mGatewayConnectionConfigs = Collections.unmodifiableSet(tunnelConfigs);
+
validate();
}
- // TODO: Implement getters, validators, etc
/**
- * Validates this configuration.
+ * Deserializes a VcnConfig from a PersistableBundle.
*
* @hide
*/
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public VcnConfig(@NonNull PersistableBundle in) {
+ final PersistableBundle gatewayConnectionConfigsBundle =
+ in.getPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY);
+ mGatewayConnectionConfigs =
+ new ArraySet<>(
+ PersistableBundleUtils.toList(
+ gatewayConnectionConfigsBundle, VcnGatewayConnectionConfig::new));
+
+ validate();
+ }
+
private void validate() {
- // TODO: implement validation logic
+ Preconditions.checkCollectionNotEmpty(
+ mGatewayConnectionConfigs, "gatewayConnectionConfigs");
+ }
+
+ /** Retrieves the set of configured tunnels. */
+ @NonNull
+ public Set<VcnGatewayConnectionConfig> getGatewayConnectionConfigs() {
+ return Collections.unmodifiableSet(mGatewayConnectionConfigs);
+ }
+
+ /**
+ * Serializes this object to a PersistableBundle.
+ *
+ * @hide
+ */
+ @NonNull
+ public PersistableBundle toPersistableBundle() {
+ final PersistableBundle result = new PersistableBundle();
+
+ final PersistableBundle gatewayConnectionConfigsBundle =
+ PersistableBundleUtils.fromList(
+ new ArrayList<>(mGatewayConnectionConfigs),
+ VcnGatewayConnectionConfig::toPersistableBundle);
+ result.putPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY, gatewayConnectionConfigsBundle);
+
+ return result;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mGatewayConnectionConfigs);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (!(other instanceof VcnConfig)) {
+ return false;
+ }
+
+ final VcnConfig rhs = (VcnConfig) other;
+ return mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs);
}
// Parcelable methods
@@ -49,15 +123,16 @@ public final class VcnConfig implements Parcelable {
}
@Override
- public void writeToParcel(Parcel out, int flags) {}
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(toPersistableBundle(), flags);
+ }
@NonNull
public static final Parcelable.Creator<VcnConfig> CREATOR =
new Parcelable.Creator<VcnConfig>() {
@NonNull
public VcnConfig createFromParcel(Parcel in) {
- // TODO: Ensure all methods are pulled from the parcels
- return new VcnConfig();
+ return new VcnConfig((PersistableBundle) in.readParcelable(null));
}
@NonNull
@@ -68,7 +143,23 @@ public final class VcnConfig implements Parcelable {
/** This class is used to incrementally build {@link VcnConfig} objects. */
public static class Builder {
- // TODO: Implement this builder
+ @NonNull
+ private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs = new ArraySet<>();
+
+ /**
+ * Adds a configuration for an individual gateway connection.
+ *
+ * @param gatewayConnectionConfig the configuration for an individual gateway connection
+ * @return this {@link Builder} instance, for chaining
+ */
+ @NonNull
+ public Builder addGatewayConnectionConfig(
+ @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) {
+ Objects.requireNonNull(gatewayConnectionConfig, "gatewayConnectionConfig was null");
+
+ mGatewayConnectionConfigs.add(gatewayConnectionConfig);
+ return this;
+ }
/**
* Builds and validates the VcnConfig.
@@ -77,7 +168,7 @@ public final class VcnConfig implements Parcelable {
*/
@NonNull
public VcnConfig build() {
- return new VcnConfig();
+ return new VcnConfig(mGatewayConnectionConfigs);
}
}
}
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 8160edc87440..039360a69a3a 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -15,7 +15,27 @@
*/
package android.net.vcn;
+import static android.net.NetworkCapabilities.NetCapability;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.NetworkCapabilities;
+import android.os.PersistableBundle;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
/**
* This class represents a configuration for a connection to a Virtual Carrier Network gateway.
@@ -49,38 +69,399 @@ import android.annotation.NonNull;
* <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_MCX}
* </ul>
*
+ * <p>The meteredness and roaming of the VCN {@link Network} will be determined by that of the
+ * underlying Network(s).
+ *
* @hide
*/
public final class VcnGatewayConnectionConfig {
- private VcnGatewayConnectionConfig() {
+ // TODO: Use MIN_MTU_V6 once it is public, @hide
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int MIN_MTU_V6 = 1280;
+
+ private static final Set<Integer> ALLOWED_CAPABILITIES;
+
+ static {
+ Set<Integer> allowedCaps = new ArraySet<>();
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_MMS);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_SUPL);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_DUN);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_FOTA);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_IMS);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_CBS);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_IA);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_RCS);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_XCAP);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_EIMS);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_MCX);
+
+ ALLOWED_CAPABILITIES = Collections.unmodifiableSet(allowedCaps);
+ }
+
+ private static final int DEFAULT_MAX_MTU = 1500;
+
+ /**
+ * The maximum number of retry intervals that may be specified.
+ *
+ * <p>Limited to ensure an upper bound on config sizes.
+ */
+ private static final int MAX_RETRY_INTERVAL_COUNT = 10;
+
+ /**
+ * The minimum allowable repeating retry interval
+ *
+ * <p>To ensure the device is not constantly being woken up, this retry interval MUST be greater
+ * than this value.
+ *
+ * @see {@link Builder#setRetryInterval()}
+ */
+ private static final long MINIMUM_REPEATING_RETRY_INTERVAL_MS = TimeUnit.MINUTES.toMillis(15);
+
+ private static final long[] DEFAULT_RETRY_INTERVALS_MS =
+ new long[] {
+ TimeUnit.SECONDS.toMillis(1),
+ TimeUnit.SECONDS.toMillis(2),
+ TimeUnit.SECONDS.toMillis(5),
+ TimeUnit.SECONDS.toMillis(30),
+ TimeUnit.MINUTES.toMillis(1),
+ TimeUnit.MINUTES.toMillis(5),
+ TimeUnit.MINUTES.toMillis(15)
+ };
+
+ private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities";
+ @NonNull private final Set<Integer> mExposedCapabilities;
+
+ private static final String UNDERLYING_CAPABILITIES_KEY = "mUnderlyingCapabilities";
+ @NonNull private final Set<Integer> mUnderlyingCapabilities;
+
+ // TODO: Add Ike/ChildSessionParams as a subclass - maybe VcnIkeGatewayConnectionConfig
+
+ private static final String MAX_MTU_KEY = "mMaxMtu";
+ private final int mMaxMtu;
+
+ private static final String RETRY_INTERVAL_MS_KEY = "mRetryIntervalsMs";
+ @NonNull private final long[] mRetryIntervalsMs;
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public VcnGatewayConnectionConfig(
+ @NonNull Set<Integer> exposedCapabilities,
+ @NonNull Set<Integer> underlyingCapabilities,
+ @NonNull long[] retryIntervalsMs,
+ @IntRange(from = MIN_MTU_V6) int maxMtu) {
+ mExposedCapabilities = exposedCapabilities;
+ mUnderlyingCapabilities = underlyingCapabilities;
+ mRetryIntervalsMs = retryIntervalsMs;
+ mMaxMtu = maxMtu;
+
+ validate();
+ }
+
+ /** @hide */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public VcnGatewayConnectionConfig(@NonNull PersistableBundle in) {
+ final PersistableBundle exposedCapsBundle =
+ in.getPersistableBundle(EXPOSED_CAPABILITIES_KEY);
+ final PersistableBundle underlyingCapsBundle =
+ in.getPersistableBundle(UNDERLYING_CAPABILITIES_KEY);
+
+ mExposedCapabilities = new ArraySet<>(PersistableBundleUtils.toList(
+ exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
+ mUnderlyingCapabilities = new ArraySet<>(PersistableBundleUtils.toList(
+ underlyingCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
+ mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY);
+ mMaxMtu = in.getInt(MAX_MTU_KEY);
+
validate();
}
- // TODO: Implement getters, validators, etc
+ private void validate() {
+ Preconditions.checkArgument(
+ mExposedCapabilities != null && !mExposedCapabilities.isEmpty(),
+ "exposedCapsBundle was null or empty");
+ for (Integer cap : getAllExposedCapabilities()) {
+ checkValidCapability(cap);
+ }
+
+ Preconditions.checkArgument(
+ mUnderlyingCapabilities != null && !mUnderlyingCapabilities.isEmpty(),
+ "underlyingCapabilities was null or empty");
+ for (Integer cap : getAllUnderlyingCapabilities()) {
+ checkValidCapability(cap);
+ }
+
+ Objects.requireNonNull(mRetryIntervalsMs, "retryIntervalsMs was null");
+ validateRetryInterval(mRetryIntervalsMs);
+
+ Preconditions.checkArgument(
+ mMaxMtu >= MIN_MTU_V6, "maxMtu must be at least IPv6 min MTU (1280)");
+ }
+
+ private static void checkValidCapability(int capability) {
+ Preconditions.checkArgument(
+ ALLOWED_CAPABILITIES.contains(capability),
+ "NetworkCapability " + capability + "out of range");
+ }
+
+ private static void validateRetryInterval(@Nullable long[] retryIntervalsMs) {
+ Preconditions.checkArgument(
+ retryIntervalsMs != null
+ && retryIntervalsMs.length > 0
+ && retryIntervalsMs.length <= MAX_RETRY_INTERVAL_COUNT,
+ "retryIntervalsMs was null, empty or exceed max interval count");
+
+ final long repeatingInterval = retryIntervalsMs[retryIntervalsMs.length - 1];
+ if (repeatingInterval < MINIMUM_REPEATING_RETRY_INTERVAL_MS) {
+ throw new IllegalArgumentException(
+ "Repeating retry interval was too short, must be a minimum of 15 minutes: "
+ + repeatingInterval);
+ }
+ }
/**
- * Validates this configuration
+ * Returns all exposed capabilities.
*
* @hide
*/
- private void validate() {
- // TODO: implement validation logic
+ @NonNull
+ public Set<Integer> getAllExposedCapabilities() {
+ return Collections.unmodifiableSet(mExposedCapabilities);
+ }
+
+ /**
+ * Checks if this config is configured to support/expose a specific capability.
+ *
+ * @param capability the capability to check for
+ */
+ public boolean hasExposedCapability(@NetCapability int capability) {
+ checkValidCapability(capability);
+
+ return mExposedCapabilities.contains(capability);
+ }
+
+ /**
+ * Returns all capabilities required of underlying networks.
+ *
+ * @hide
+ */
+ @NonNull
+ public Set<Integer> getAllUnderlyingCapabilities() {
+ return Collections.unmodifiableSet(mUnderlyingCapabilities);
}
- // Parcelable methods
+ /**
+ * Checks if this config requires an underlying network to have the specified capability.
+ *
+ * @param capability the capability to check for
+ */
+ public boolean requiresUnderlyingCapability(@NetCapability int capability) {
+ checkValidCapability(capability);
+
+ return mUnderlyingCapabilities.contains(capability);
+ }
- /** This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects */
+ /** Retrieves the configured retry intervals. */
+ @NonNull
+ public long[] getRetryIntervalsMs() {
+ return Arrays.copyOf(mRetryIntervalsMs, mRetryIntervalsMs.length);
+ }
+
+ /** Retrieves the maximum MTU allowed for this Gateway Connection. */
+ @IntRange(from = MIN_MTU_V6)
+ public int getMaxMtu() {
+ return mMaxMtu;
+ }
+
+ /**
+ * Converts this config to a PersistableBundle.
+ *
+ * @hide
+ */
+ @NonNull
+ @VisibleForTesting(visibility = Visibility.PROTECTED)
+ public PersistableBundle toPersistableBundle() {
+ final PersistableBundle result = new PersistableBundle();
+
+ final PersistableBundle exposedCapsBundle =
+ PersistableBundleUtils.fromList(
+ new ArrayList<>(mExposedCapabilities),
+ PersistableBundleUtils.INTEGER_SERIALIZER);
+ final PersistableBundle underlyingCapsBundle =
+ PersistableBundleUtils.fromList(
+ new ArrayList<>(mUnderlyingCapabilities),
+ PersistableBundleUtils.INTEGER_SERIALIZER);
+
+ result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle);
+ result.putPersistableBundle(UNDERLYING_CAPABILITIES_KEY, underlyingCapsBundle);
+ result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs);
+ result.putInt(MAX_MTU_KEY, mMaxMtu);
+
+ return result;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mExposedCapabilities,
+ mUnderlyingCapabilities,
+ Arrays.hashCode(mRetryIntervalsMs),
+ mMaxMtu);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (!(other instanceof VcnGatewayConnectionConfig)) {
+ return false;
+ }
+
+ final VcnGatewayConnectionConfig rhs = (VcnGatewayConnectionConfig) other;
+ return mExposedCapabilities.equals(rhs.mExposedCapabilities)
+ && mUnderlyingCapabilities.equals(rhs.mUnderlyingCapabilities)
+ && Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs)
+ && mMaxMtu == rhs.mMaxMtu;
+ }
+
+ /** This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects. */
public static class Builder {
- // TODO: Implement this builder
+ @NonNull private final Set<Integer> mExposedCapabilities = new ArraySet();
+ @NonNull private final Set<Integer> mUnderlyingCapabilities = new ArraySet();
+ @NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
+ private int mMaxMtu = DEFAULT_MAX_MTU;
+
+ // TODO: (b/175829816) Consider VCN-exposed capabilities that may be transport dependent.
+ // Consider the case where the VCN might only expose MMS on WiFi, but defer to MMS
+ // when on Cell.
+
+ /**
+ * Add a capability that this VCN Gateway Connection will support.
+ *
+ * @param exposedCapability the app-facing capability to be exposed by this VCN Gateway
+ * Connection (i.e., the capabilities that this VCN Gateway Connection will support).
+ * @return this {@link Builder} instance, for chaining
+ * @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway
+ * Connection
+ */
+ public Builder addExposedCapability(@NetCapability int exposedCapability) {
+ checkValidCapability(exposedCapability);
+
+ mExposedCapabilities.add(exposedCapability);
+ return this;
+ }
+
+ /**
+ * Remove a capability that this VCN Gateway Connection will support.
+ *
+ * @param exposedCapability the app-facing capability to not be exposed by this VCN Gateway
+ * Connection (i.e., the capabilities that this VCN Gateway Connection will support)
+ * @return this {@link Builder} instance, for chaining
+ * @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway
+ * Connection
+ */
+ public Builder removeExposedCapability(@NetCapability int exposedCapability) {
+ checkValidCapability(exposedCapability);
+
+ mExposedCapabilities.remove(exposedCapability);
+ return this;
+ }
+
+ /**
+ * Require a capability for Networks underlying this VCN Gateway Connection.
+ *
+ * @param underlyingCapability the capability that a network MUST have in order to be an
+ * underlying network for this VCN Gateway Connection.
+ * @return this {@link Builder} instance, for chaining
+ * @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying
+ * networks
+ */
+ public Builder addRequiredUnderlyingCapability(@NetCapability int underlyingCapability) {
+ checkValidCapability(underlyingCapability);
+
+ mUnderlyingCapabilities.add(underlyingCapability);
+ return this;
+ }
+
+ /**
+ * Remove a requirement of a capability for Networks underlying this VCN Gateway Connection.
+ *
+ * <p>Calling this method will allow Networks that do NOT have this capability to be
+ * selected as an underlying network for this VCN Gateway Connection. However, underlying
+ * networks MAY still have the removed capability.
+ *
+ * @param underlyingCapability the capability that a network DOES NOT need to have in order
+ * to be an underlying network for this VCN Gateway Connection.
+ * @return this {@link Builder} instance, for chaining
+ * @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying
+ * networks
+ */
+ public Builder removeRequiredUnderlyingCapability(@NetCapability int underlyingCapability) {
+ checkValidCapability(underlyingCapability);
+
+ mUnderlyingCapabilities.remove(underlyingCapability);
+ return this;
+ }
+
+ /**
+ * Set the retry interval between VCN establishment attempts upon successive failures.
+ *
+ * <p>The last retry interval will be repeated until safe mode is entered, or a connection
+ * is successfully established, at which point the retry timers will be reset. For power
+ * reasons, the last (repeated) retry interval MUST be at least 15 minutes.
+ *
+ * <p>Retry intervals MAY be subject to system power saving modes. That is to say that if
+ * the system enters a power saving mode, the retry may not occur until the device leaves
+ * the specified power saving mode. Intervals are sequential, and intervals will NOT be
+ * skipped if system power saving results in delaying retries (even if it exceed multiple
+ * retry intervals).
+ *
+ * <p>Each Gateway Connection will retry according to the retry intervals configured, but if
+ * safe mode is enabled, all Gateway Connection(s) will be disabled.
+ *
+ * @param retryIntervalsMs an array of between 1 and 10 millisecond intervals after which
+ * the VCN will attempt to retry a session initiation. The last (repeating) retry
+ * interval must be at least 15 minutes. Defaults to: {@code [1s, 2s, 5s, 30s, 1m, 5m,
+ * 15m]}
+ * @return this {@link Builder} instance, for chaining
+ * @see VcnManager for additional discussion on fail-safe mode
+ */
+ @NonNull
+ public Builder setRetryInterval(@NonNull long[] retryIntervalsMs) {
+ validateRetryInterval(retryIntervalsMs);
+
+ mRetryIntervalsMs = retryIntervalsMs;
+ return this;
+ }
+
+ /**
+ * Sets the maximum MTU allowed for this VCN Gateway Connection.
+ *
+ * <p>This MTU is applied to the VCN Gateway Connection exposed Networks, and represents the
+ * MTU of the virtualized network.
+ *
+ * <p>The system may reduce the MTU below the maximum specified based on signals such as the
+ * MTU of the underlying networks (and adjusted for Gateway Connection overhead).
+ *
+ * @param maxMtu the maximum MTU allowed for this Gateway Connection. Must be greater than
+ * the IPv6 minimum MTU of 1280. Defaults to 1500.
+ * @return this {@link Builder} instance, for chaining
+ */
+ @NonNull
+ public Builder setMaxMtu(@IntRange(from = MIN_MTU_V6) int maxMtu) {
+ Preconditions.checkArgument(
+ maxMtu >= MIN_MTU_V6, "maxMtu must be at least IPv6 min MTU (1280)");
+
+ mMaxMtu = maxMtu;
+ return this;
+ }
/**
- * Builds and validates the VcnGatewayConnectionConfig
+ * Builds and validates the VcnGatewayConnectionConfig.
*
* @return an immutable VcnGatewayConnectionConfig instance
*/
@NonNull
public VcnGatewayConnectionConfig build() {
- return new VcnGatewayConnectionConfig();
+ return new VcnGatewayConnectionConfig(
+ mExposedCapabilities, mUnderlyingCapabilities, mRetryIntervalsMs, mMaxMtu);
}
}
}
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 6769b9e46e4c..19c183f9fe9c 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -23,10 +23,37 @@ import android.annotation.SystemService;
import android.content.Context;
import android.os.ParcelUuid;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+
+import java.io.IOException;
/**
* VcnManager publishes APIs for applications to configure and manage Virtual Carrier Networks.
*
+ * <p>A VCN creates a virtualization layer to allow MVNOs to aggregate heterogeneous physical
+ * networks, unifying them as a single carrier network. This enables infrastructure flexibility on
+ * the part of MVNOs without impacting user connectivity, abstracting the physical network
+ * technologies as an implementation detail of their public network.
+ *
+ * <p>Each VCN virtualizes an Carrier's network by building tunnels to a carrier's core network over
+ * carrier-managed physical links and supports a IP mobility layer to ensure seamless transitions
+ * between the underlying networks. Each VCN is configured based on a Subscription Group (see {@link
+ * android.telephony.SubscriptionManager}) and aggregates all networks that are brought up based on
+ * a profile or suggestion in the specified Subscription Group.
+ *
+ * <p>The VCN can be configured to expose one or more {@link android.net.Network}(s), each with
+ * different capabilities, allowing for APN virtualization.
+ *
+ * <p>If a tunnel fails to connect, or otherwise encounters a fatal error, the VCN will attempt to
+ * reestablish the connection. If the tunnel still has not reconnected after a system-determined
+ * timeout, the VCN Safe Mode (see below) will be entered.
+ *
+ * <p>The VCN Safe Mode ensures that users (and carriers) have a fallback to restore system
+ * connectivity to update profiles, diagnose issues, contact support, or perform other remediation
+ * tasks. In Safe Mode, the system will allow underlying cellular networks to be used as default.
+ * Additionally, during Safe Mode, the VCN will continue to retry the connections, and will
+ * automatically exit Safe Mode if all active tunnels connect successfully.
+ *
* @hide
*/
@SystemService(Context.VCN_MANAGEMENT_SERVICE)
@@ -63,15 +90,20 @@ public final class VcnManager {
* @param config the configuration parameters for the VCN
* @throws SecurityException if the caller does not have carrier privileges, or is not running
* as the primary user
+ * @throws IOException if the configuration failed to be persisted. A caller encountering this
+ * exception should attempt to retry (possibly after a delay).
* @hide
*/
@RequiresPermission("carrier privileges") // TODO (b/72967236): Define a system-wide constant
- public void setVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) {
+ public void setVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config)
+ throws IOException {
requireNonNull(subscriptionGroup, "subscriptionGroup was null");
requireNonNull(config, "config was null");
try {
mService.setVcnConfig(subscriptionGroup, config);
+ } catch (ServiceSpecificException e) {
+ throw new IOException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -88,14 +120,18 @@ public final class VcnManager {
* @param subscriptionGroup the subscription group that the configuration should be applied to
* @throws SecurityException if the caller does not have carrier privileges, or is not running
* as the primary user
+ * @throws IOException if the configuration failed to be cleared. A caller encountering this
+ * exception should attempt to retry (possibly after a delay).
* @hide
*/
@RequiresPermission("carrier privileges") // TODO (b/72967236): Define a system-wide constant
- public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) {
+ public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) throws IOException {
requireNonNull(subscriptionGroup, "subscriptionGroup was null");
try {
mService.clearVcnConfig(subscriptionGroup);
+ } catch (ServiceSpecificException e) {
+ throw new IOException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/os/IRecoverySystem.aidl b/core/java/android/os/IRecoverySystem.aidl
index 5f8b93283269..205288303b1e 100644
--- a/core/java/android/os/IRecoverySystem.aidl
+++ b/core/java/android/os/IRecoverySystem.aidl
@@ -30,5 +30,6 @@ interface IRecoverySystem {
boolean requestLskf(in String packageName, in IntentSender sender);
boolean clearLskf(in String packageName);
boolean isLskfCaptured(in String packageName);
+ boolean rebootWithLskfAssumeSlotSwitch(in String packageName, in String reason);
boolean rebootWithLskf(in String packageName, in String reason, in boolean slotSwitch);
}
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 5f3cfa35fbd0..93c1690e3813 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -687,8 +687,8 @@ public class RecoverySystem {
}
/**
- * Request that the device reboot and apply the update that has been prepared. Callers are
- * recommended to use {@link #rebootAndApply(Context, String, boolean)} instead.
+ * Request that the device reboot and apply the update that has been prepared. This API is
+ * deprecated, and is expected to be used by OTA only on devices running Android 11.
*
* @param context the Context to use.
* @param updateToken this parameter is deprecated and won't be used. See details in
@@ -699,18 +699,18 @@ public class RecoverySystem {
* unattended reboot or if the {@code updateToken} did not match the previously
* given token
* @hide
+ * @deprecated Use {@link #rebootAndApply(Context, String, boolean)} instead
*/
@SystemApi
- @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY,
- android.Manifest.permission.REBOOT})
+ @RequiresPermission(android.Manifest.permission.RECOVERY)
public static void rebootAndApply(@NonNull Context context, @NonNull String updateToken,
@NonNull String reason) throws IOException {
if (updateToken == null) {
throw new NullPointerException("updateToken == null");
}
RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
- // OTA is the sole user before S, and a slot switch is required for ota update.
- if (!rs.rebootWithLskf(context.getPackageName(), reason, true)) {
+ // OTA is the sole user, who expects a slot switch.
+ if (!rs.rebootWithLskfAssumeSlotSwitch(context.getPackageName(), reason)) {
throw new IOException("system not prepared to apply update");
}
}
@@ -738,7 +738,7 @@ public class RecoverySystem {
*
* @param context the Context to use.
* @param reason the reboot reason to give to the {@link PowerManager}
- * @param slotSwitch true if the caller intends to switch the slot on an A/B device.
+ * @param slotSwitch true if the caller expects the slot to be switched on A/B devices.
* @throws IOException if the reboot couldn't proceed because the device wasn't ready for an
* unattended reboot.
* @hide
@@ -1395,6 +1395,21 @@ public class RecoverySystem {
}
}
+
+ /**
+ * Calls the recovery system service to reboot and apply update. This is the legacy API and
+ * expects a slot switch for A/B devices.
+ *
+ */
+ private boolean rebootWithLskfAssumeSlotSwitch(String packageName, String reason)
+ throws IOException {
+ try {
+ return mService.rebootWithLskfAssumeSlotSwitch(packageName, reason);
+ } catch (RemoteException e) {
+ throw new IOException("could not reboot for update");
+ }
+ }
+
/**
* Internally, recovery treats each line of the command file as a separate
* argv, so we only need to protect against newlines and nulls.
diff --git a/core/java/android/os/incremental/OWNERS b/core/java/android/os/incremental/OWNERS
new file mode 100644
index 000000000000..3795493b861f
--- /dev/null
+++ b/core/java/android/os/incremental/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 554432
+alexbuy@google.com
+schfan@google.com
+toddke@google.com
+zyy@google.com
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index b2b8db19ac17..376d9421e8d3 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -4313,13 +4313,23 @@ public final class ContactsContract {
* <P>
* Type: INTEGER (A bitmask of CARRIER_PRESENCE_* fields)
* </P>
+ *
+ * @deprecated The contacts database will only show presence
+ * information on devices where
+ * {@link android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL} is true,
+ * otherwise use {@link android.telephony.ims.RcsUceAdapter}.
*/
+ @Deprecated
public static final String CARRIER_PRESENCE = "carrier_presence";
/**
* Indicates that the entry is Video Telephony (VT) capable on the
* current carrier. An allowed bitmask of {@link #CARRIER_PRESENCE}.
+ *
+ * @deprecated Same as {@link DataColumns#CARRIER_PRESENCE}.
+ *
*/
+ @Deprecated
public static final int CARRIER_PRESENCE_VT_CAPABLE = 0x01;
/**
diff --git a/core/java/android/service/attestation/OWNERS b/core/java/android/service/attestation/OWNERS
new file mode 100644
index 000000000000..b9e7b996e5d8
--- /dev/null
+++ b/core/java/android/service/attestation/OWNERS
@@ -0,0 +1,2 @@
+chaviw@google.com
+ogunwale@google.com
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 7dbf69369996..1cf25a77fa38 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -28,6 +28,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.content.res.Configuration;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.LocaleList;
@@ -293,6 +294,13 @@ public class EditorInfo implements InputType, Parcelable {
public static final int IME_FLAG_FORCE_ASCII = 0x80000000;
/**
+ * Flag of {@link #internalImeOptions}: flag is set when app window containing this
+ * {@link EditorInfo} is using {@link Configuration#ORIENTATION_PORTRAIT} mode.
+ * @hide
+ */
+ public static final int IME_FLAG_APP_WINDOW_PORTRAIT = 0x80000000;
+
+ /**
* Generic unspecified type for {@link #imeOptions}.
*/
public static final int IME_NULL = 0x00000000;
@@ -312,6 +320,7 @@ public class EditorInfo implements InputType, Parcelable {
* 1 1 IME_ACTION_NEXT
* 11 IME_ACTION_DONE
* 111 IME_ACTION_PREVIOUS
+ * 1 IME_FLAG_APP_WINDOW_PORTRAIT
* 1 IME_FLAG_NO_PERSONALIZED_LEARNING
* 1 IME_FLAG_NO_FULLSCREEN
* 1 IME_FLAG_NAVIGATE_PREVIOUS
@@ -343,6 +352,20 @@ public class EditorInfo implements InputType, Parcelable {
public String privateImeOptions = null;
/**
+ * Masks for {@link internalImeOptions}
+ *
+ * <pre>
+ * 1 IME_FLAG_APP_WINDOW_PORTRAIT
+ * |-------|-------|-------|-------|</pre>
+ */
+
+ /**
+ * Same as {@link android.R.attr#imeOptions} but for framework's internal-use only.
+ * @hide
+ */
+ public int internalImeOptions = IME_NULL;
+
+ /**
* In some cases an IME may be able to display an arbitrary label for
* a command the user can perform, which you can specify here. This is
* typically used as the label for the action to use in-line as a replacement
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 12c91fac0c65..977a0e89a143 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -17,6 +17,7 @@
package android.widget;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.ContentInfo.FLAG_CONVERT_TO_PLAIN_TEXT;
import static android.view.ContentInfo.SOURCE_AUTOFILL;
import static android.view.ContentInfo.SOURCE_CLIPBOARD;
@@ -8738,6 +8739,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION;
}
}
+ if (getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT) {
+ outAttrs.internalImeOptions |= EditorInfo.IME_FLAG_APP_WINDOW_PORTRAIT;
+ }
if (isMultilineInputType(outAttrs.inputType)) {
// Multi-line text editors should always show an enter key.
outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION;
diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java
index 8a1d4a0b4823..1254647379b5 100644
--- a/core/java/android/window/DisplayAreaOrganizer.java
+++ b/core/java/android/window/DisplayAreaOrganizer.java
@@ -164,15 +164,19 @@ public class DisplayAreaOrganizer extends WindowOrganizer {
}
/**
- * Creates a persistent task display area. It will be added to be the top most task display area
- * in the root.
+ * Creates a persistent {@link com.android.server.wm.TaskDisplayArea}.
*
* The new created TDA is organized by the organizer, and will be deleted on calling
* {@link #deleteTaskDisplayArea(WindowContainerToken)} or {@link #unregisterOrganizer()}.
*
- * @param displayId the display to create the new task display area in.
- * @param rootFeatureId the root display area to create the new task display area in. Caller can
- * use {@link #FEATURE_ROOT} as the root of the logical display.
+ * @param displayId the display to create the new TDA in.
+ * @param parentFeatureId the parent to create the new TDA in. If it is a
+ * {@link com.android.server.wm.RootDisplayArea}, the new TDA will be
+ * placed as the topmost TDA. If it is another TDA, the new TDA will be
+ * placed as the topmost child.
+ * Caller can use {@link #FEATURE_ROOT} as the root of the logical
+ * display, or {@link #FEATURE_DEFAULT_TASK_CONTAINER} as the default
+ * TDA.
* @param name the name for the new task display area.
* @return the new created task display area.
* @throws IllegalArgumentException if failed to create a new task display area.
@@ -181,11 +185,11 @@ public class DisplayAreaOrganizer extends WindowOrganizer {
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
@CallSuper
@NonNull
- public DisplayAreaAppearedInfo createTaskDisplayArea(int displayId, int rootFeatureId,
+ public DisplayAreaAppearedInfo createTaskDisplayArea(int displayId, int parentFeatureId,
@NonNull String name) {
try {
return getController().createTaskDisplayArea(
- mInterface, displayId, rootFeatureId, name);
+ mInterface, displayId, parentFeatureId, name);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/window/IDisplayAreaOrganizerController.aidl b/core/java/android/window/IDisplayAreaOrganizerController.aidl
index 26fa434506cf..6c097bbd80bb 100644
--- a/core/java/android/window/IDisplayAreaOrganizerController.aidl
+++ b/core/java/android/window/IDisplayAreaOrganizerController.aidl
@@ -40,21 +40,25 @@ interface IDisplayAreaOrganizerController {
void unregisterOrganizer(in IDisplayAreaOrganizer organizer);
/**
- * Creates a persistent task display area. It will be added to be the top most task display area
- * in the root.
+ * Creates a persistent {@link com.android.server.wm.TaskDisplayArea}.
*
* The new created TDA is organized by the organizer, and will be deleted on calling
* {@link #deleteTaskDisplayArea(WindowContainerToken)} or {@link #unregisterOrganizer()}.
*
- * @param displayId the display to create the new task display area in.
- * @param rootFeatureId the root display area to create the new task display area in. Caller can
- * use {@link #FEATURE_ROOT} as the root of the logical display.
+ * @param displayId the display to create the new TDA in.
+ * @param parentFeatureId the parent to create the new TDA in. If it is a
+ * {@link com.android.server.wm.RootDisplayArea}, the new TDA will be
+ * placed as the topmost TDA. If it is another TDA, the new TDA will be
+ * placed as the topmost child.
+ * Caller can use {@link #FEATURE_ROOT} as the root of the logical
+ * display, or {@link #FEATURE_DEFAULT_TASK_CONTAINER} as the default
+ * TDA.
* @param name the name for the new task display area.
* @return the new created task display area.
* @throws IllegalArgumentException if failed to create a new task display area.
*/
DisplayAreaAppearedInfo createTaskDisplayArea(in IDisplayAreaOrganizer organizer, int displayId,
- int rootFeatureId, in String name);
+ int parentFeatureId, in String name);
/**
* Deletes a persistent task display area. It can only be one that created by an organizer.
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index e595db384cb9..c4168141f184 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -78,13 +78,16 @@ public class KernelWakelockReader {
boolean useSystemSuspend = (new File(sSysClassWakeupDir)).exists();
if (useSystemSuspend) {
- // Get both kernel and native wakelock stats from SystemSuspend
- updateVersion(staleStats);
- if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
- Slog.w(TAG, "Failed to get wakelock stats from SystemSuspend");
- return null;
+ // static read/write lock protection for sKernelWakelockUpdateVersion
+ synchronized (KernelWakelockReader.class) {
+ // Get both kernel and native wakelock stats from SystemSuspend
+ updateVersion(staleStats);
+ if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
+ Slog.w(TAG, "Failed to get wakelock stats from SystemSuspend");
+ return null;
+ }
+ return removeOldStats(staleStats);
}
- return removeOldStats(staleStats);
} else {
Arrays.fill(mKernelWakelockBuffer, (byte) 0);
int len = 0;
@@ -141,14 +144,17 @@ public class KernelWakelockReader {
}
}
- updateVersion(staleStats);
- // Get native wakelock stats from SystemSuspend
- if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
- Slog.w(TAG, "Failed to get Native wakelock stats from SystemSuspend");
+ // static read/write lock protection for sKernelWakelockUpdateVersion
+ synchronized (KernelWakelockReader.class) {
+ updateVersion(staleStats);
+ // Get native wakelock stats from SystemSuspend
+ if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
+ Slog.w(TAG, "Failed to get Native wakelock stats from SystemSuspend");
+ }
+ // Get kernel wakelock stats
+ parseProcWakelocks(mKernelWakelockBuffer, len, wakeup_sources, staleStats);
+ return removeOldStats(staleStats);
}
- // Get kernel wakelock stats
- parseProcWakelocks(mKernelWakelockBuffer, len, wakeup_sources, staleStats);
- return removeOldStats(staleStats);
}
}
diff --git a/core/res/Android.bp b/core/res/Android.bp
index f94a2b08e6c3..9ee5e5e18185 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -19,6 +19,13 @@ android_app {
sdk_version: "core_platform",
certificate: "platform",
+ // Disable dexpreopt and verify_uses_libraries check as the app
+ // contains no Java code to be dexpreopted.
+ enforce_uses_libs: false,
+ dex_preopt: {
+ enabled: false,
+ },
+
// Soong special-cases framework-res to install this alongside
// the libraries at /system/framework/framework-res.apk.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 62a8ae539222..28f5e354b83f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -393,6 +393,7 @@
<protected-broadcast android:name="android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST" />
<protected-broadcast android:name="android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION" />
<protected-broadcast android:name="android.net.wifi.action.PASSPOINT_LAUNCH_OSU_VIEW" />
+ <protected-broadcast android:name="android.net.wifi.action.REFRESH_USER_PROVISIONING" />
<protected-broadcast android:name="android.net.wifi.action.WIFI_NETWORK_SUGGESTION_POST_CONNECTION" />
<protected-broadcast android:name="android.net.wifi.action.WIFI_SCAN_AVAILABILITY_CHANGED" />
<protected-broadcast android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" />
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index 40ecf9a83981..66fdfff990f8 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -16,6 +16,9 @@
package android.widget;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -30,6 +33,7 @@ import android.text.GetChars;
import android.text.Layout;
import android.text.PrecomputedText;
import android.view.View;
+import android.view.inputmethod.EditorInfo;
import android.widget.TextView.BufferType;
import androidx.test.InstrumentationRegistry;
@@ -254,6 +258,24 @@ public class TextViewTest {
assertEquals("", mTextView.getTransformed().toString());
}
+ @Test
+ @UiThreadTest
+ public void testPortraitDoesntSupportFullscreenIme() {
+ mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ mTextView = new NullSetTextTextView(mActivity);
+ mTextView.requestFocus();
+ assertEquals("IME_FLAG_NO_FULLSCREEN should be set",
+ mTextView.getImeOptions(),
+ mTextView.getImeOptions() & EditorInfo.IME_FLAG_NO_FULLSCREEN);
+
+ mTextView.clearFocus();
+ mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ mTextView = new NullSetTextTextView(mActivity);
+ mTextView.requestFocus();
+ assertEquals("IME_FLAG_NO_FULLSCREEN should not be set",
+ 0, mTextView.getImeOptions() & EditorInfo.IME_FLAG_NO_FULLSCREEN);
+ }
+
private String createLongText() {
int size = 600 * 1000;
final StringBuilder builder = new StringBuilder(size);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 67f4c8a16da6..bd4154210cbf 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -38,6 +38,7 @@ import org.junit.runners.Suite;
BatteryStatsTimeBaseTest.class,
BatteryStatsTimerTest.class,
BatteryStatsUidTest.class,
+ BatteryUsageStatsTest.class,
BatteryStatsUserLifecycleTests.class,
BluetoothPowerCalculatorTest.class,
BstatsCpuTimesValidationTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
new file mode 100644
index 000000000000..96e9c4af21c2
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryUsageStats;
+import android.os.Parcel;
+import android.os.SystemBatteryConsumer;
+import android.os.UidBatteryConsumer;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BatteryUsageStatsTest {
+
+ @Test
+ public void testBuilder() {
+ BatteryUsageStats batteryUsageStats = buildBatteryUsageStats();
+ validateBatteryUsageStats(batteryUsageStats);
+ }
+
+ @Test
+ public void testParcelability() {
+ final BatteryUsageStats outBatteryUsageStats = buildBatteryUsageStats();
+ final Parcel outParcel = Parcel.obtain();
+ outParcel.writeParcelable(outBatteryUsageStats, 0);
+ final byte[] bytes = outParcel.marshall();
+ outParcel.recycle();
+
+ final Parcel inParcel = Parcel.obtain();
+ inParcel.unmarshall(bytes, 0, bytes.length);
+ inParcel.setDataPosition(0);
+ final BatteryUsageStats inBatteryUsageStats =
+ inParcel.readParcelable(getClass().getClassLoader());
+ assertThat(inBatteryUsageStats).isNotNull();
+ validateBatteryUsageStats(inBatteryUsageStats);
+ }
+
+ private BatteryUsageStats buildBatteryUsageStats() {
+ final MockClocks clocks = new MockClocks();
+ final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks);
+ final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(2000);
+
+ final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(1, 1, true);
+ builder.setConsumedPower(100);
+ builder.setDischargePercentage(20);
+
+ final UidBatteryConsumer.Builder uidBatteryConsumerBuilder =
+ builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid);
+ uidBatteryConsumerBuilder.setPackageWithHighestDrain("foo");
+ uidBatteryConsumerBuilder.setConsumedPower(200);
+ uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, 300);
+ uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 400);
+ uidBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 500);
+ uidBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
+ + BatteryConsumer.POWER_COMPONENT_CPU, 510);
+ uidBatteryConsumerBuilder.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, 600);
+ uidBatteryConsumerBuilder.setUsageDurationMillis(
+ BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, 700);
+ uidBatteryConsumerBuilder.setUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 800);
+
+ final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder =
+ builder.getOrCreateSystemBatteryConsumerBuilder(
+ SystemBatteryConsumer.DRAIN_TYPE_CAMERA);
+ systemBatteryConsumerBuilder.setConsumedPower(10000);
+ systemBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 10100);
+ systemBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200);
+ systemBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
+ + BatteryConsumer.POWER_COMPONENT_CPU, 10210);
+ systemBatteryConsumerBuilder.setUsageDurationMillis(
+ BatteryConsumer.TIME_COMPONENT_CPU, 10300);
+ systemBatteryConsumerBuilder.setUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 10400);
+
+ return builder.build();
+ }
+
+ public void validateBatteryUsageStats(BatteryUsageStats batteryUsageStats) {
+ assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(100);
+ assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(20);
+
+ final List<UidBatteryConsumer> uidBatteryConsumers =
+ batteryUsageStats.getUidBatteryConsumers();
+ for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
+ if (uidBatteryConsumer.getUid() == 2000) {
+ assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo("foo");
+ assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(200);
+ assertThat(uidBatteryConsumer.getConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_USAGE)).isEqualTo(300);
+ assertThat(uidBatteryConsumer.getConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(400);
+ assertThat(uidBatteryConsumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(500);
+ assertThat(uidBatteryConsumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
+ + BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(510);
+ assertThat(uidBatteryConsumer.getUsageDurationMillis(
+ BatteryConsumer.TIME_COMPONENT_CPU)).isEqualTo(600);
+ assertThat(uidBatteryConsumer.getUsageDurationMillis(
+ BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND)).isEqualTo(700);
+ assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(800);
+ } else {
+ fail("Unexpected UID " + uidBatteryConsumer.getUid());
+ }
+ }
+
+ final List<SystemBatteryConsumer> systemBatteryConsumers =
+ batteryUsageStats.getSystemBatteryConsumers();
+ for (SystemBatteryConsumer systemBatteryConsumer : systemBatteryConsumers) {
+ if (systemBatteryConsumer.getDrainType() == SystemBatteryConsumer.DRAIN_TYPE_CAMERA) {
+ assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(10000);
+ assertThat(systemBatteryConsumer.getConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10100);
+ assertThat(systemBatteryConsumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(10200);
+ assertThat(systemBatteryConsumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
+ + BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10210);
+ assertThat(systemBatteryConsumer.getUsageDurationMillis(
+ BatteryConsumer.TIME_COMPONENT_CPU)).isEqualTo(10300);
+ assertThat(systemBatteryConsumer.getUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(10400);
+ } else {
+ fail("Unexpected drain type " + systemBatteryConsumer.getDrainType());
+ }
+ }
+ }
+}
diff --git a/data/keyboards/keyboards.mk b/data/keyboards/keyboards.mk
index 68cbd29859bb..c7ce8cd6693a 100644
--- a/data/keyboards/keyboards.mk
+++ b/data/keyboards/keyboards.mk
@@ -14,13 +14,9 @@
# Warning: this is actually a product definition, to be inherited from
-include $(LOCAL_PATH)/common.mk
+PRODUCT_COPY_FILES := \
+ $(call find-copy-subdir-files,*.kl,$(LOCAL_PATH),system/usr/keylayout) \
+ $(call find-copy-subdir-files,*.kcm,$(LOCAL_PATH),system/usr/keychars) \
+ $(call find-copy-subdir-files,*.idc,$(LOCAL_PATH),system/usr/idc)
-PRODUCT_COPY_FILES := $(foreach file,$(framework_keylayouts),\
- $(file):system/usr/keylayout/$(notdir $(file)))
-PRODUCT_COPY_FILES += $(foreach file,$(framework_keycharmaps),\
- $(file):system/usr/keychars/$(notdir $(file)))
-
-PRODUCT_COPY_FILES += $(foreach file,$(framework_keyconfigs),\
- $(file):system/usr/idc/$(notdir $(file)))
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
index 0775a1a99886..56f7ea8725ee 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
@@ -41,7 +41,7 @@ import javax.crypto.spec.IvParameterSpec;
*
* @hide
*/
-public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {
+public abstract class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {
private static final int BLOCK_SIZE_BYTES = 8;
@@ -73,12 +73,22 @@ public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {
public NoPadding() {
super(KeymasterDefs.KM_PAD_NONE);
}
+
+ @Override
+ protected final String getTransform() {
+ return "DESede/ECB/NoPadding";
+ }
}
public static class PKCS7Padding extends ECB {
public PKCS7Padding() {
super(KeymasterDefs.KM_PAD_PKCS7);
}
+
+ @Override
+ protected final String getTransform() {
+ return "DESede/ECB/PKCS7Padding";
+ }
}
}
@@ -91,12 +101,23 @@ public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {
public NoPadding() {
super(KeymasterDefs.KM_PAD_NONE);
}
+
+ @Override
+ protected final String getTransform() {
+ return "DESede/CBC/NoPadding";
+ }
+
}
public static class PKCS7Padding extends CBC {
public PKCS7Padding() {
super(KeymasterDefs.KM_PAD_PKCS7);
}
+
+ @Override
+ protected final String getTransform() {
+ return "DESede/CBC/PKCS7Padding";
+ }
}
}
@@ -288,7 +309,7 @@ public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {
if (parameters != null) {
for (KeyParameter p : parameters) {
if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
- returnedIv = p.blob;
+ returnedIv = p.value.getBlob();
break;
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
index bc56f015f3bd..64da83778a45 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
@@ -64,6 +64,11 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC
}
@Override
+ protected final String getTransform() {
+ return "AES/GCM/NoPadding";
+ }
+
+ @Override
protected final void resetAll() {
mTagLengthBits = DEFAULT_TAG_LENGTH_BITS;
super.resetAll();
@@ -325,7 +330,7 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC
if (parameters != null) {
for (KeyParameter p : parameters) {
if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
- returnedIv = p.blob;
+ returnedIv = p.value.getBlob();
break;
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java
index dd943d422e62..9ad6f3adbd33 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java
@@ -254,13 +254,13 @@ class AndroidKeyStoreBCWorkaroundProvider extends Provider {
private void putAsymmetricCipherImpl(String transformation, String implClass) {
put("Cipher." + transformation, implClass);
put("Cipher." + transformation + " SupportedKeyClasses",
- KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME);
+ KEYSTORE_PRIVATE_KEY_CLASS_NAME);
}
private void putSignatureImpl(String algorithm, String implClass) {
put("Signature." + algorithm, implClass);
put("Signature." + algorithm + " SupportedKeyClasses",
- KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME);
+ KEYSTORE_PRIVATE_KEY_CLASS_NAME);
}
public static String[] getSupportedEcdsaSignatureDigests() {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
index a3b04abfba3f..2ee952cbc5fb 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
@@ -43,6 +43,7 @@ import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
+import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
@@ -57,6 +58,8 @@ import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.ShortBufferException;
+import javax.crypto.spec.OAEPParameterSpec;
+import javax.crypto.spec.PSource;
import javax.crypto.spec.SecretKeySpec;
/**
@@ -99,6 +102,8 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
*/
private Exception mCachedException;
+ private Cipher mCipher;
+
AndroidKeyStoreCipherSpiBase() {
mOperation = null;
mEncrypting = false;
@@ -110,6 +115,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
mAdditionalAuthenticationDataStreamer = null;
mAdditionalAuthenticationDataStreamerClosed = false;
mCachedException = null;
+ mCipher = null;
}
@Override
@@ -117,6 +123,45 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
throws InvalidKeyException {
resetAll();
+ if (!(key instanceof AndroidKeyStorePrivateKey
+ || key instanceof AndroidKeyStoreSecretKey)) {
+ try {
+ mCipher = Cipher.getInstance(getTransform());
+ String transform = getTransform();
+
+ if ("RSA/ECB/OAEPWithSHA-224AndMGF1Padding".equals(transform)) {
+ OAEPParameterSpec spec =
+ new OAEPParameterSpec("SHA-224", "MGF1",
+ new MGF1ParameterSpec("SHA1"), PSource.PSpecified.DEFAULT);
+ mCipher.init(opmode, key, spec, random);
+ } else if ("RSA/ECB/OAEPWithSHA-256AndMGF1Padding".equals(transform)) {
+ OAEPParameterSpec spec =
+ new OAEPParameterSpec("SHA-256", "MGF1",
+ new MGF1ParameterSpec("SHA1"), PSource.PSpecified.DEFAULT);
+ mCipher.init(opmode, key, spec, random);
+
+ } else if ("RSA/ECB/OAEPWithSHA-384AndMGF1Padding".equals(transform)) {
+ OAEPParameterSpec spec =
+ new OAEPParameterSpec("SHA-384", "MGF1",
+ new MGF1ParameterSpec("SHA1"), PSource.PSpecified.DEFAULT);
+ mCipher.init(opmode, key, spec, random);
+
+ } else if ("RSA/ECB/OAEPWithSHA-512AndMGF1Padding".equals(transform)) {
+ OAEPParameterSpec spec =
+ new OAEPParameterSpec("SHA-512", "MGF1",
+ new MGF1ParameterSpec("SHA1"), PSource.PSpecified.DEFAULT);
+ mCipher.init(opmode, key, spec, random);
+ } else {
+ mCipher.init(opmode, key, random);
+ }
+ return;
+ } catch (NoSuchAlgorithmException
+ | NoSuchPaddingException
+ | InvalidAlgorithmParameterException e) {
+ throw new InvalidKeyException(e);
+ }
+ }
+
boolean success = false;
try {
init(opmode, key, random);
@@ -139,6 +184,17 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
resetAll();
+ if (!(key instanceof AndroidKeyStorePrivateKey
+ || key instanceof AndroidKeyStoreSecretKey)) {
+ try {
+ mCipher = Cipher.getInstance(getTransform());
+ mCipher.init(opmode, key, params, random);
+ return;
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+ throw new InvalidKeyException(e);
+ }
+ }
+
boolean success = false;
try {
init(opmode, key, random);
@@ -157,6 +213,17 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
resetAll();
+ if (!(key instanceof AndroidKeyStorePrivateKey
+ || key instanceof AndroidKeyStoreSecretKey)) {
+ try {
+ mCipher = Cipher.getInstance(getTransform());
+ mCipher.init(opmode, key, params, random);
+ return;
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+ throw new InvalidKeyException(e);
+ }
+ }
+
boolean success = false;
try {
init(opmode, key, random);
@@ -214,6 +281,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
mAdditionalAuthenticationDataStreamer = null;
mAdditionalAuthenticationDataStreamerClosed = false;
mCachedException = null;
+ mCipher = null;
}
/**
@@ -320,6 +388,10 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
@Override
protected final byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
+ if (mCipher != null) {
+ return mCipher.update(input, inputOffset, inputLen);
+ }
+
if (mCachedException != null) {
return null;
}
@@ -371,6 +443,9 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
@Override
protected final int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output,
int outputOffset) throws ShortBufferException {
+ if (mCipher != null) {
+ return mCipher.update(input, inputOffset, inputLen, output);
+ }
byte[] outputCopy = engineUpdate(input, inputOffset, inputLen);
if (outputCopy == null) {
return 0;
@@ -387,6 +462,10 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
@Override
protected final int engineUpdate(ByteBuffer input, ByteBuffer output)
throws ShortBufferException {
+ if (mCipher != null) {
+ return mCipher.update(input, output);
+ }
+
if (input == null) {
throw new NullPointerException("input == null");
}
@@ -423,6 +502,11 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
@Override
protected final void engineUpdateAAD(byte[] input, int inputOffset, int inputLen) {
+ if (mCipher != null) {
+ mCipher.updateAAD(input, inputOffset, inputLen);
+ return;
+ }
+
if (mCachedException != null) {
return;
}
@@ -459,6 +543,11 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
@Override
protected final void engineUpdateAAD(ByteBuffer src) {
+ if (mCipher != null) {
+ mCipher.updateAAD(src);
+ return;
+ }
+
if (src == null) {
throw new IllegalArgumentException("src == null");
}
@@ -486,6 +575,10 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
@Override
protected final byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
+ if (mCipher != null) {
+ return mCipher.doFinal(input, inputOffset, inputLen);
+ }
+
if (mCachedException != null) {
throw (IllegalBlockSizeException)
new IllegalBlockSizeException().initCause(mCachedException);
@@ -522,6 +615,10 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
protected final int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
int outputOffset) throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
+ if (mCipher != null) {
+ return mCipher.doFinal(input, inputOffset, inputLen, output);
+ }
+
byte[] outputCopy = engineDoFinal(input, inputOffset, inputLen);
if (outputCopy == null) {
return 0;
@@ -538,6 +635,10 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
@Override
protected final int engineDoFinal(ByteBuffer input, ByteBuffer output)
throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
+ if (mCipher != null) {
+ return mCipher.doFinal(input, output);
+ }
+
if (input == null) {
throw new NullPointerException("input == null");
}
@@ -575,6 +676,10 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
@Override
protected final byte[] engineWrap(Key key)
throws IllegalBlockSizeException, InvalidKeyException {
+ if (mCipher != null) {
+ return mCipher.wrap(key);
+ }
+
if (mKey == null) {
throw new IllegalStateException("Not initilized");
}
@@ -656,6 +761,10 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
@Override
protected final Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException {
+ if (mCipher != null) {
+ return mCipher.unwrap(wrappedKey, wrappedKeyAlgorithm, wrappedKeyType);
+ }
+
if (mKey == null) {
throw new IllegalStateException("Not initilized");
}
@@ -902,4 +1011,6 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
*/
protected abstract void loadAlgorithmSpecificParametersFromBeginResult(
KeyParameter[] parameters);
+
+ protected abstract String getTransform();
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
index d1ef1df817e6..8289671de212 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
@@ -44,6 +44,11 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature
}
@Override
+ protected String getAlgorithm() {
+ return "NONEwithECDSA";
+ }
+
+ @Override
protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
KeyStoreOperation operation) {
return new TruncateToFieldSizeMessageStreamer(
@@ -113,30 +118,50 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature
public SHA1() {
super(KeymasterDefs.KM_DIGEST_SHA1);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA1withECDSA";
+ }
}
public final static class SHA224 extends AndroidKeyStoreECDSASignatureSpi {
public SHA224() {
super(KeymasterDefs.KM_DIGEST_SHA_2_224);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA224withECDSA";
+ }
}
public final static class SHA256 extends AndroidKeyStoreECDSASignatureSpi {
public SHA256() {
super(KeymasterDefs.KM_DIGEST_SHA_2_256);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA256withECDSA";
+ }
}
public final static class SHA384 extends AndroidKeyStoreECDSASignatureSpi {
public SHA384() {
super(KeymasterDefs.KM_DIGEST_SHA_2_384);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA384withECDSA";
+ }
}
public final static class SHA512 extends AndroidKeyStoreECDSASignatureSpi {
public SHA512() {
super(KeymasterDefs.KM_DIGEST_SHA_2_512);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA512withECDSA";
+ }
}
private final int mKeymasterDigest;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index b2e32a3175e3..403da189262d 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -31,9 +31,7 @@ import android.system.keystore2.KeyEntryResponse;
import android.system.keystore2.KeyMetadata;
import android.system.keystore2.ResponseCode;
-import java.security.KeyFactory;
import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.ProviderException;
import java.security.PublicKey;
@@ -42,8 +40,6 @@ import java.security.Signature;
import java.security.UnrecoverableKeyException;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import javax.crypto.Mac;
@@ -237,28 +233,11 @@ public class AndroidKeyStoreProvider extends Provider {
throw new UnrecoverableKeyException("Failed to obtain X.509 form of public key."
+ " Keystore has no public certificate stored.");
}
- final byte[] x509EncodedPublicKey = metadata.certificate;
+ final byte[] x509PublicCert = metadata.certificate;
- String jcaKeyAlgorithm;
- try {
- jcaKeyAlgorithm = KeyProperties.KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm(
- algorithm);
- } catch (IllegalArgumentException e) {
- throw (UnrecoverableKeyException)
- new UnrecoverableKeyException("Failed to load private key")
- .initCause(e);
- }
+ PublicKey publicKey = AndroidKeyStoreSpi.toCertificate(x509PublicCert).getPublicKey();
- PublicKey publicKey;
- try {
- KeyFactory keyFactory = KeyFactory.getInstance(jcaKeyAlgorithm);
- publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(x509EncodedPublicKey));
- } catch (NoSuchAlgorithmException e) {
- throw new ProviderException(
- "Failed to obtain " + jcaKeyAlgorithm + " KeyFactory", e);
- } catch (InvalidKeySpecException e) {
- throw new ProviderException("Invalid X.509 encoding of public key", e);
- }
+ String jcaKeyAlgorithm = publicKey.getAlgorithm();
KeyStoreSecurityLevel securityLevel = iSecurityLevel;
if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(jcaKeyAlgorithm)) {
@@ -358,7 +337,7 @@ public class AndroidKeyStoreProvider extends Provider {
KeyDescriptor descriptor = new KeyDescriptor();
if (namespace == KeyProperties.NAMESPACE_APPLICATION) {
- descriptor.nspace = 0; // ignored;
+ descriptor.nspace = KeyProperties.NAMESPACE_APPLICATION; // ignored;
descriptor.domain = Domain.APP;
} else {
descriptor.nspace = namespace;
@@ -387,10 +366,10 @@ public class AndroidKeyStoreProvider extends Provider {
for (Authorization a : response.metadata.authorizations) {
switch (a.keyParameter.tag) {
case KeymasterDefs.KM_TAG_ALGORITHM:
- keymasterAlgorithm = a.keyParameter.integer;
+ keymasterAlgorithm = a.keyParameter.value.getAlgorithm();
break;
case KeymasterDefs.KM_TAG_DIGEST:
- if (keymasterDigest == -1) keymasterDigest = a.keyParameter.integer;
+ if (keymasterDigest == -1) keymasterDigest = a.keyParameter.value.getDigest();
break;
}
}
@@ -407,7 +386,7 @@ public class AndroidKeyStoreProvider extends Provider {
keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) {
return makeAndroidKeyStorePublicKeyFromKeyEntryResponse(descriptor, response.metadata,
new KeyStoreSecurityLevel(response.iSecurityLevel),
- keymasterAlgorithm);
+ keymasterAlgorithm).getPrivateKey();
} else {
throw new UnrecoverableKeyException("Key algorithm unknown");
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
index 951f91887894..6ff9432905ed 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
@@ -158,7 +158,7 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
}
/**
- * RSA cipher with OAEP encryption padding. Only SHA-1 based MGF1 is supported as MGF.
+ * RSA cipher with OAEP encryption padding.
*/
abstract static class OAEPWithMGF1Padding extends AndroidKeyStoreRSACipherSpi {
@@ -316,6 +316,25 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
protected final int getAdditionalEntropyAmountForFinish() {
return (isEncrypting()) ? mDigestOutputSizeBytes : 0;
}
+
+ @Override
+ protected final String getTransform() {
+ switch (mKeymasterDigest) {
+ case KeymasterDefs.KM_DIGEST_SHA1:
+ return "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
+ case KeymasterDefs.KM_DIGEST_SHA_2_224:
+ return "RSA/ECB/OAEPWithSHA-224AndMGF1Padding";
+ case KeymasterDefs.KM_DIGEST_SHA_2_256:
+ return "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
+ case KeymasterDefs.KM_DIGEST_SHA_2_384:
+ return "RSA/ECB/OAEPWithSHA-384AndMGF1Padding";
+ case KeymasterDefs.KM_DIGEST_SHA_2_512:
+ return "RSA/ECB/OAEPWithSHA-512AndMGF1Padding";
+ default:
+ return "RSA/ECB/OAEPPadding";
+ }
+ }
+
}
public static class OAEPWithSHA1AndMGF1Padding extends OAEPWithMGF1Padding {
@@ -358,6 +377,11 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
}
@Override
+ protected String getTransform() {
+ return "RSA/ECB/" + KeyProperties.EncryptionPadding.fromKeymaster(mKeymasterPadding);
+ }
+
+ @Override
protected final void initKey(int opmode, Key key) throws InvalidKeyException {
if (key == null) {
throw new InvalidKeyException("Unsupported key: null");
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
index ab7559116a41..931c2f864eba 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
@@ -48,42 +48,70 @@ abstract class AndroidKeyStoreRSASignatureSpi extends AndroidKeyStoreSignatureSp
public NONEWithPKCS1Padding() {
super(KeymasterDefs.KM_DIGEST_NONE);
}
+ @Override
+ protected String getAlgorithm() {
+ return "NONEwithRSA";
+ }
}
public static final class MD5WithPKCS1Padding extends PKCS1Padding {
public MD5WithPKCS1Padding() {
super(KeymasterDefs.KM_DIGEST_MD5);
}
+ @Override
+ protected String getAlgorithm() {
+ return "MD5withRSA";
+ }
}
public static final class SHA1WithPKCS1Padding extends PKCS1Padding {
public SHA1WithPKCS1Padding() {
super(KeymasterDefs.KM_DIGEST_SHA1);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA1withRSA";
+ }
}
public static final class SHA224WithPKCS1Padding extends PKCS1Padding {
public SHA224WithPKCS1Padding() {
super(KeymasterDefs.KM_DIGEST_SHA_2_224);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA224withRSA";
+ }
}
public static final class SHA256WithPKCS1Padding extends PKCS1Padding {
public SHA256WithPKCS1Padding() {
super(KeymasterDefs.KM_DIGEST_SHA_2_256);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA256withRSA";
+ }
}
public static final class SHA384WithPKCS1Padding extends PKCS1Padding {
public SHA384WithPKCS1Padding() {
super(KeymasterDefs.KM_DIGEST_SHA_2_384);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA384withRSA";
+ }
}
public static final class SHA512WithPKCS1Padding extends PKCS1Padding {
public SHA512WithPKCS1Padding() {
super(KeymasterDefs.KM_DIGEST_SHA_2_512);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA512withRSA";
+ }
}
abstract static class PSSPadding extends AndroidKeyStoreRSASignatureSpi {
@@ -103,30 +131,50 @@ abstract class AndroidKeyStoreRSASignatureSpi extends AndroidKeyStoreSignatureSp
public SHA1WithPSSPadding() {
super(KeymasterDefs.KM_DIGEST_SHA1);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA1withRSA/PSS";
+ }
}
public static final class SHA224WithPSSPadding extends PSSPadding {
public SHA224WithPSSPadding() {
super(KeymasterDefs.KM_DIGEST_SHA_2_224);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA224withRSA/PSS";
+ }
}
public static final class SHA256WithPSSPadding extends PSSPadding {
public SHA256WithPSSPadding() {
super(KeymasterDefs.KM_DIGEST_SHA_2_256);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA256withRSA/PSS";
+ }
}
public static final class SHA384WithPSSPadding extends PSSPadding {
public SHA384WithPSSPadding() {
super(KeymasterDefs.KM_DIGEST_SHA_2_384);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA384withRSA/PSS";
+ }
}
public static final class SHA512WithPSSPadding extends PSSPadding {
public SHA512WithPSSPadding() {
super(KeymasterDefs.KM_DIGEST_SHA_2_512);
}
+ @Override
+ protected String getAlgorithm() {
+ return "SHA512withRSA/PSS";
+ }
}
private final int mKeymasterDigest;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
index 9d3b9704d711..74503e1eecfa 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -102,7 +102,8 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
insideSecureHardware =
KeyStore2ParameterUtils.isSecureHardware(a.securityLevel);
securityLevel = a.securityLevel;
- origin = KeyProperties.Origin.fromKeymaster(a.keyParameter.integer);
+ origin = KeyProperties.Origin.fromKeymaster(
+ a.keyParameter.value.getOrigin());
break;
case KeymasterDefs.KM_TAG_KEY_SIZE:
long keySizeUnsigned = KeyStore2ParameterUtils.getUnsignedInt(a);
@@ -113,45 +114,51 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
keySize = (int) keySizeUnsigned;
break;
case KeymasterDefs.KM_TAG_PURPOSE:
- purposes |= KeyProperties.Purpose.fromKeymaster(a.keyParameter.integer);
+ purposes |= KeyProperties.Purpose.fromKeymaster(
+ a.keyParameter.value.getKeyPurpose());
break;
case KeymasterDefs.KM_TAG_PADDING:
+ int paddingMode = a.keyParameter.value.getPaddingMode();
try {
- if (a.keyParameter.integer == KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN
- || a.keyParameter.integer == KeymasterDefs.KM_PAD_RSA_PSS) {
+ if (paddingMode == KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN
+ || paddingMode == KeymasterDefs.KM_PAD_RSA_PSS) {
@KeyProperties.SignaturePaddingEnum String padding =
KeyProperties.SignaturePadding.fromKeymaster(
- a.keyParameter.integer);
+ paddingMode);
signaturePaddingsList.add(padding);
} else {
@KeyProperties.EncryptionPaddingEnum String jcaPadding =
KeyProperties.EncryptionPadding.fromKeymaster(
- a.keyParameter.integer);
+ paddingMode);
encryptionPaddingsList.add(jcaPadding);
}
} catch (IllegalArgumentException e) {
throw new ProviderException("Unsupported padding: "
- + a.keyParameter.integer);
+ + paddingMode);
}
break;
case KeymasterDefs.KM_TAG_DIGEST:
- digestsList.add(KeyProperties.Digest.fromKeymaster(a.keyParameter.integer));
+ digestsList.add(KeyProperties.Digest.fromKeymaster(
+ a.keyParameter.value.getDigest()));
break;
case KeymasterDefs.KM_TAG_BLOCK_MODE:
blockModesList.add(
- KeyProperties.BlockMode.fromKeymaster(a.keyParameter.integer)
+ KeyProperties.BlockMode.fromKeymaster(
+ a.keyParameter.value.getBlockMode())
);
break;
case KeymasterDefs.KM_TAG_USER_AUTH_TYPE:
+ int authenticatorType = a.keyParameter.value.getHardwareAuthenticatorType();
if (KeyStore2ParameterUtils.isSecureHardware(a.securityLevel)) {
- keymasterHwEnforcedUserAuthenticators = a.keyParameter.integer;
+ keymasterHwEnforcedUserAuthenticators = authenticatorType;
} else {
- keymasterSwEnforcedUserAuthenticators = a.keyParameter.integer;
+ keymasterSwEnforcedUserAuthenticators = authenticatorType;
}
break;
case KeymasterDefs.KM_TAG_USER_SECURE_ID:
keymasterSecureUserIds.add(
- KeymasterArguments.toUint64(a.keyParameter.longInteger));
+ KeymasterArguments.toUint64(
+ a.keyParameter.value.getLongInteger()));
break;
case KeymasterDefs.KM_TAG_ACTIVE_DATETIME:
keyValidityStart = KeyStore2ParameterUtils.getDate(a);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
index 9b4f01e744f7..96da1e00ade8 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
@@ -30,10 +30,12 @@ import libcore.util.EmptyArray;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.InvalidParameterException;
+import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.ProviderException;
import java.security.PublicKey;
import java.security.SecureRandom;
+import java.security.Signature;
import java.security.SignatureException;
import java.security.SignatureSpi;
import java.util.ArrayList;
@@ -76,6 +78,13 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
*/
private Exception mCachedException;
+ /**
+ * This signature object is used for public key operations, i.e, signatrue verification.
+ * The Android Keystore backend does not perform public key operations and defers to the
+ * Highest priority provider.
+ */
+ private Signature mSignature;
+
AndroidKeyStoreSignatureSpiBase() {
mOperation = null;
mOperationChallenge = 0;
@@ -84,6 +93,7 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
appRandom = null;
mMessageStreamer = null;
mCachedException = null;
+ mSignature = null;
}
@Override
@@ -123,27 +133,13 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
protected final void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
resetAll();
- boolean success = false;
try {
- if (publicKey == null) {
- throw new InvalidKeyException("Unsupported key: null");
- }
- AndroidKeyStoreKey keystoreKey;
- if (publicKey instanceof AndroidKeyStorePublicKey) {
- keystoreKey = (AndroidKeyStorePublicKey) publicKey;
- } else {
- throw new InvalidKeyException("Unsupported public key type: " + publicKey);
- }
- mSigning = false;
- initKey(keystoreKey);
- appRandom = null;
- ensureKeystoreOperationInitialized();
- success = true;
- } finally {
- if (!success) {
- resetAll();
- }
+ mSignature = Signature.getInstance(getAlgorithm());
+ } catch (NoSuchAlgorithmException e) {
+ throw new InvalidKeyException(e);
}
+
+ mSignature.initVerify(publicKey);
}
/**
@@ -251,6 +247,11 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
@Override
protected final void engineUpdate(byte[] b, int off, int len) throws SignatureException {
+ if (mSignature != null) {
+ mSignature.update(b, off, len);
+ return;
+ }
+
if (mCachedException != null) {
throw new SignatureException(mCachedException);
}
@@ -337,39 +338,10 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
@Override
protected final boolean engineVerify(byte[] signature) throws SignatureException {
- if (mCachedException != null) {
- throw new SignatureException(mCachedException);
- }
-
- try {
- ensureKeystoreOperationInitialized();
- } catch (InvalidKeyException e) {
- throw new SignatureException(e);
- }
-
- boolean verified;
- try {
- byte[] output = mMessageStreamer.doFinal(
- EmptyArray.BYTE, 0, 0,
- signature);
- if (output.length != 0) {
- throw new ProviderException(
- "Signature verification unexpected produced output: " + output.length
- + " bytes");
- }
- verified = true;
- } catch (KeyStoreException e) {
- switch (e.getErrorCode()) {
- case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
- verified = false;
- break;
- default:
- throw new SignatureException(e);
- }
+ if (mSignature != null) {
+ return mSignature.verify(signature);
}
-
- resetWhilePreservingInitState();
- return verified;
+ throw new IllegalStateException("Not initialised.");
}
@Override
@@ -392,6 +364,13 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
}
/**
+ * Implementations need to report the algorithm they implement so that we can delegate to the
+ * highest priority provider.
+ * @return Algorithm string.
+ */
+ protected abstract String getAlgorithm();
+
+ /**
* Returns {@code true} if this signature is initialized for signing, {@code false} if this
* signature is initialized for verification.
*/
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index c42c7b2c41b0..5e7f6482ebed 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -219,7 +219,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
return null;
}
- private static X509Certificate toCertificate(byte[] bytes) {
+ static X509Certificate toCertificate(byte[] bytes) {
try {
final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
return (X509Certificate) certFactory.generateCertificate(
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
index 4d4b0d8f183b..5c048a127cb3 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
@@ -42,7 +42,7 @@ import javax.crypto.spec.IvParameterSpec;
*
* @hide
*/
-class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSpiBase {
+abstract class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSpiBase {
abstract static class ECB extends AndroidKeyStoreUnauthenticatedAESCipherSpi {
protected ECB(int keymasterPadding) {
@@ -53,12 +53,22 @@ class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSp
public NoPadding() {
super(KeymasterDefs.KM_PAD_NONE);
}
+
+ @Override
+ protected final String getTransform() {
+ return "AES/ECB/NoPadding";
+ }
}
public static class PKCS7Padding extends ECB {
public PKCS7Padding() {
super(KeymasterDefs.KM_PAD_PKCS7);
}
+
+ @Override
+ protected final String getTransform() {
+ return "AES/ECB/PKCS7Padding";
+ }
}
}
@@ -71,12 +81,22 @@ class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSp
public NoPadding() {
super(KeymasterDefs.KM_PAD_NONE);
}
+
+ @Override
+ protected final String getTransform() {
+ return "AES/CBC/NoPadding";
+ }
}
public static class PKCS7Padding extends CBC {
public PKCS7Padding() {
super(KeymasterDefs.KM_PAD_PKCS7);
}
+
+ @Override
+ protected final String getTransform() {
+ return "AES/CBC/PKCS7Padding";
+ }
}
}
@@ -89,6 +109,11 @@ class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSp
public NoPadding() {
super(KeymasterDefs.KM_PAD_NONE);
}
+
+ @Override
+ protected final String getTransform() {
+ return "AES/CTR/NoPadding";
+ }
}
}
@@ -275,7 +300,7 @@ class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSp
if (parameters != null) {
for (KeyParameter p : parameters) {
if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
- returnedIv = p.blob;
+ returnedIv = p.value.getBlob();
break;
}
}
diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
index 18c786aa3093..4c8ab8d6c713 100644
--- a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
@@ -19,7 +19,9 @@ package android.security.keystore2;
import android.annotation.NonNull;
import android.hardware.biometrics.BiometricManager;
import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameterValue;
import android.hardware.security.keymint.SecurityLevel;
+import android.hardware.security.keymint.Tag;
import android.security.GateKeeper;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyProperties;
@@ -50,7 +52,7 @@ public abstract class KeyStore2ParameterUtils {
}
KeyParameter p = new KeyParameter();
p.tag = tag;
- p.boolValue = true;
+ p.value = KeyParameterValue.boolValue(true);
return p;
}
@@ -62,14 +64,40 @@ public abstract class KeyStore2ParameterUtils {
* @hide
*/
static @NonNull KeyParameter makeEnum(int tag, int v) {
- int type = KeymasterDefs.getTagType(tag);
- if (type != KeymasterDefs.KM_ENUM && type != KeymasterDefs.KM_ENUM_REP) {
- throw new IllegalArgumentException("Not an enum or repeatable enum tag: " + tag);
+ KeyParameter kp = new KeyParameter();
+ kp.tag = tag;
+ switch (tag) {
+ case Tag.PURPOSE:
+ kp.value = KeyParameterValue.keyPurpose(v);
+ break;
+ case Tag.ALGORITHM:
+ kp.value = KeyParameterValue.algorithm(v);
+ break;
+ case Tag.BLOCK_MODE:
+ kp.value = KeyParameterValue.blockMode(v);
+ break;
+ case Tag.DIGEST:
+ kp.value = KeyParameterValue.digest(v);
+ break;
+ case Tag.EC_CURVE:
+ kp.value = KeyParameterValue.ecCurve(v);
+ break;
+ case Tag.ORIGIN:
+ kp.value = KeyParameterValue.origin(v);
+ break;
+ case Tag.PADDING:
+ kp.value = KeyParameterValue.paddingMode(v);
+ break;
+ case Tag.USER_AUTH_TYPE:
+ kp.value = KeyParameterValue.hardwareAuthenticatorType(v);
+ break;
+ case Tag.HARDWARE_TYPE:
+ kp.value = KeyParameterValue.securityLevel(v);
+ break;
+ default:
+ throw new IllegalArgumentException("Not an enum or repeatable enum tag: " + tag);
}
- KeyParameter p = new KeyParameter();
- p.tag = tag;
- p.integer = v;
- return p;
+ return kp;
}
/**
@@ -86,7 +114,7 @@ public abstract class KeyStore2ParameterUtils {
}
KeyParameter p = new KeyParameter();
p.tag = tag;
- p.integer = v;
+ p.value = KeyParameterValue.integer(v);
return p;
}
@@ -104,7 +132,7 @@ public abstract class KeyStore2ParameterUtils {
}
KeyParameter p = new KeyParameter();
p.tag = tag;
- p.longInteger = v;
+ p.value = KeyParameterValue.longInteger(v);
return p;
}
@@ -121,7 +149,7 @@ public abstract class KeyStore2ParameterUtils {
}
KeyParameter p = new KeyParameter();
p.tag = tag;
- p.blob = b;
+ p.value = KeyParameterValue.blob(b);
return p;
}
@@ -138,9 +166,10 @@ public abstract class KeyStore2ParameterUtils {
}
KeyParameter p = new KeyParameter();
p.tag = tag;
- p.longInteger = date.getTime();
- if (p.longInteger < 0) {
- throw new IllegalArgumentException("Date tag value out of range: " + p.longInteger);
+ p.value = KeyParameterValue.dateTime(date.getTime());
+ if (p.value.getDateTime() < 0) {
+ throw new IllegalArgumentException("Date tag value out of range: "
+ + p.value.getDateTime());
}
return p;
}
@@ -160,24 +189,24 @@ public abstract class KeyStore2ParameterUtils {
throw new IllegalArgumentException("Not an int tag: " + param.keyParameter.tag);
}
// KM_UINT is 32 bits wide so we must suppress sign extension.
- return ((long) param.keyParameter.integer) & 0xffffffffL;
+ return ((long) param.keyParameter.value.getInteger()) & 0xffffffffL;
}
static @NonNull Date getDate(@NonNull Authorization param) {
if (KeymasterDefs.getTagType(param.keyParameter.tag) != KeymasterDefs.KM_DATE) {
throw new IllegalArgumentException("Not a date tag: " + param.keyParameter.tag);
}
- if (param.keyParameter.longInteger < 0) {
+ if (param.keyParameter.value.getDateTime() < 0) {
throw new IllegalArgumentException("Date Value too large: "
- + param.keyParameter.longInteger);
+ + param.keyParameter.value.getDateTime());
}
- return new Date(param.keyParameter.longInteger);
+ return new Date(param.keyParameter.value.getDateTime());
}
static void forEachSetFlag(int flags, Consumer<Integer> consumer) {
int offset = 0;
while (flags != 0) {
- if ((flags & 1) == 0) {
+ if ((flags & 1) == 1) {
consumer.accept(1 << offset);
}
offset += 1;
diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
index 3b11854bf7cb..f87a3d25f90c 100644
--- a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
@@ -57,7 +57,7 @@ abstract class KeyStoreCryptoOperationUtils {
for (Authorization p : key.getAuthorizations()) {
switch(p.keyParameter.tag) {
case KeymasterDefs.KM_TAG_USER_SECURE_ID:
- keySids.add(p.keyParameter.longInteger);
+ keySids.add(p.keyParameter.value.getLongInteger());
break;
default:
break;
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
index d580104ade19..c546a4d18027 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
@@ -20,7 +20,6 @@ import android.app.Instrumentation
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.FIND_TIMEOUT
-import com.android.server.wm.flicker.helpers.waitForIME
import com.android.wm.shell.flicker.TEST_APP_IME_ACTIVITY_LABEL
import com.android.wm.shell.flicker.testapp.Components
import org.junit.Assert
@@ -39,14 +38,9 @@ open class ImeAppHelper(
Assert.assertNotNull("Text field not found, this usually happens when the device " +
"was left in an unknown state (e.g. in split screen)", editText)
editText.click()
- if (!uiDevice.waitForIME()) {
- Assert.fail("IME did not appear")
- }
}
fun closeIME() {
uiDevice.pressBack()
- // Using only the AccessibilityInfo it is not possible to identify if the IME is active
- uiDevice.waitForIdle(1000)
}
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index 6b44ce6ace0c..866d654362ca 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -25,8 +25,10 @@ import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.closePipWindow
import com.android.server.wm.flicker.helpers.hasPipWindow
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.IME_WINDOW_NAME
import com.android.wm.shell.flicker.helpers.ImeAppHelper
+import com.android.wm.shell.flicker.testapp.Components
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -46,6 +48,8 @@ class PipKeyboardTest(
rotation: Int
) : PipTestBase(rotationName, rotation) {
private val keyboardApp = ImeAppHelper(instrumentation)
+ private val keyboardComponent = Components.ImeActivity().componentName
+ private val helper = WindowManagerStateHelper()
private val keyboardScenario: FlickerBuilder
get() = FlickerBuilder(instrumentation).apply {
@@ -64,6 +68,8 @@ class PipKeyboardTest(
// UiAutomator doesn't support to launch the multiple Activities in a task.
// So use launchActivity() for the Keyboard Activity.
keyboardApp.launchViaIntent()
+ helper.waitForAppTransitionIdle()
+ helper.waitForFullScreenApp(keyboardComponent)
}
}
teardown {
@@ -88,9 +94,11 @@ class PipKeyboardTest(
transitions {
// open the soft keyboard
keyboardApp.openIME()
+ helper.waitImeWindowShown()
// then close it again
keyboardApp.closeIME()
+ helper.waitImeWindowGone()
}
assertions {
windowManagerTrace {
@@ -112,11 +120,13 @@ class PipKeyboardTest(
transitions {
// open the soft keyboard
keyboardApp.openIME()
+ helper.waitImeWindowShown()
}
teardown {
eachRun {
// close the keyboard
keyboardApp.closeIME()
+ helper.waitImeWindowGone()
}
}
assertions {
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index a545b3d5e134..bec80a7d605e 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -670,7 +670,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
}
auto entry_flags = type_spec->GetFlagsForEntryIndex(entry_idx);
- if (UNLIKELY(!entry_flags)) {
+ if (UNLIKELY(!entry_flags.has_value())) {
return base::unexpected(entry_flags.error());
}
type_flags |= entry_flags.value();
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 4010e4e10317..bce70e2aae9e 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -897,12 +897,12 @@ base::expected<StringPiece, NullOrIOError> ResStringPool::string8At(size_t idx)
// Decode the UTF-16 length. This is not used if we're not
// converting to UTF-16 from UTF-8.
const base::expected<size_t, IOError> u16len = decodeLength(&str);
- if (UNLIKELY(!u16len)) {
+ if (UNLIKELY(!u16len.has_value())) {
return base::unexpected(u16len.error());
}
const base::expected<size_t, IOError> u8len = decodeLength(&str);
- if (UNLIKELY(!u8len)) {
+ if (UNLIKELY(!u8len.has_value())) {
return base::unexpected(u8len.error());
}
diff --git a/libs/androidfw/ResourceUtils.cpp b/libs/androidfw/ResourceUtils.cpp
index a34aa7239250..87fb2c038c9f 100644
--- a/libs/androidfw/ResourceUtils.cpp
+++ b/libs/androidfw/ResourceUtils.cpp
@@ -56,7 +56,8 @@ base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName(
.package_len = package_name.size(),
};
- if (base::expected<StringPiece, NullOrIOError> type_str = type_string_ref.string8()) {
+ if (base::expected<StringPiece, NullOrIOError> type_str = type_string_ref.string8();
+ type_str.ok()) {
name.type = type_str->data();
name.type_len = type_str->size();
} else if (UNLIKELY(IsIOError(type_str))) {
@@ -64,7 +65,8 @@ base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName(
}
if (name.type == nullptr) {
- if (base::expected<StringPiece16, NullOrIOError> type16_str = type_string_ref.string16()) {
+ if (base::expected<StringPiece16, NullOrIOError> type16_str = type_string_ref.string16();
+ type16_str.ok()) {
name.type16 = type16_str->data();
name.type_len = type16_str->size();
} else if (!type16_str.has_value()) {
@@ -72,7 +74,8 @@ base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName(
}
}
- if (base::expected<StringPiece, NullOrIOError> entry_str = entry_string_ref.string8()) {
+ if (base::expected<StringPiece, NullOrIOError> entry_str = entry_string_ref.string8();
+ entry_str.ok()) {
name.entry = entry_str->data();
name.entry_len = entry_str->size();
} else if (UNLIKELY(IsIOError(entry_str))) {
@@ -80,7 +83,8 @@ base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName(
}
if (name.entry == nullptr) {
- if (base::expected<StringPiece16, NullOrIOError> entry16_str = entry_string_ref.string16()) {
+ if (base::expected<StringPiece16, NullOrIOError> entry16_str = entry_string_ref.string16();
+ entry16_str.ok()) {
name.entry16 = entry16_str->data();
name.entry_len = entry16_str->size();
} else if (!entry16_str.has_value()) {
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index b9f449c20677..17305a501cd0 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -21,7 +21,6 @@ import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Binder;
import android.os.IBinder;
@@ -48,8 +47,6 @@ public final class AudioPlaybackConfiguration implements Parcelable {
public static final int PLAYER_PIID_INVALID = -1;
/** @hide */
public static final int PLAYER_UPID_INVALID = -1;
- /** @hide */
- public static final int PLAYER_DEVICEID_INVALID = 0;
// information about the implementation
/**
@@ -161,11 +158,6 @@ public final class AudioPlaybackConfiguration implements Parcelable {
*/
@SystemApi
public static final int PLAYER_STATE_STOPPED = 4;
- /**
- * @hide
- * The state used to update device id, does not actually change the state of the player
- */
- public static final int PLAYER_UPDATE_DEVICE_ID = 5;
/** @hide */
@IntDef({
@@ -174,8 +166,7 @@ public final class AudioPlaybackConfiguration implements Parcelable {
PLAYER_STATE_IDLE,
PLAYER_STATE_STARTED,
PLAYER_STATE_PAUSED,
- PLAYER_STATE_STOPPED,
- PLAYER_UPDATE_DEVICE_ID
+ PLAYER_STATE_STOPPED
})
@Retention(RetentionPolicy.SOURCE)
public @interface PlayerState {}
@@ -193,8 +184,6 @@ public final class AudioPlaybackConfiguration implements Parcelable {
private int mPlayerState;
private AudioAttributes mPlayerAttr; // never null
- private int mDeviceId;
-
/**
* Never use without initializing parameters afterwards
*/
@@ -212,7 +201,6 @@ public final class AudioPlaybackConfiguration implements Parcelable {
mPlayerType = pic.mPlayerType;
mClientUid = uid;
mClientPid = pid;
- mDeviceId = PLAYER_DEVICEID_INVALID;
mPlayerState = PLAYER_STATE_IDLE;
mPlayerAttr = pic.mAttributes;
if ((sPlayerDeathMonitor != null) && (pic.mIPlayer != null)) {
@@ -253,7 +241,6 @@ public final class AudioPlaybackConfiguration implements Parcelable {
in.mPlayerAttr.getAllowedCapturePolicy() == ALLOW_CAPTURE_BY_ALL
? ALLOW_CAPTURE_BY_ALL : ALLOW_CAPTURE_BY_NONE)
.build();
- anonymCopy.mDeviceId = in.mDeviceId;
// anonymized data
anonymCopy.mPlayerType = PLAYER_TYPE_UNKNOWN;
anonymCopy.mClientUid = PLAYER_UPID_INVALID;
@@ -291,25 +278,6 @@ public final class AudioPlaybackConfiguration implements Parcelable {
}
/**
- * Returns information about the {@link AudioDeviceInfo} used for this playback.
- * @return the audio playback device or null if the device is not available at the time of query
- */
- public @Nullable AudioDeviceInfo getAudioDevice() {
- if (mDeviceId == PLAYER_DEVICEID_INVALID) {
- return null;
- }
- // TODO(175802592): change this to AudioManager.getDeviceForPortId() when available
- AudioDeviceInfo[] devices =
- AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_OUTPUTS);
- for (int i = 0; i < devices.length; i++) {
- if (devices[i].getId() == mDeviceId) {
- return devices[i];
- }
- }
- return null;
- }
-
- /**
* @hide
* Return the type of player linked to this configuration.
* <br>Note that player types not exposed in the system API will be represented as
@@ -391,29 +359,13 @@ public final class AudioPlaybackConfiguration implements Parcelable {
* @hide
* Handle a player state change
* @param event
- * @param deviceId active device id or {@Code PLAYER_DEVICEID_INVALID}
- * <br>Note device id is valid for {@code PLAYER_UPDATE_DEVICE_ID} or
- * <br>{@code PLAYER_STATE_STARTED} events, as the device id will be reset to none when
- * <br>pausing or stopping playback. It will be set to active device when playback starts or
- * <br>it will be changed when PLAYER_UPDATE_DEVICE_ID is sent. The latter can happen if the
- * <br>device changes in the middle of playback.
* @return true if the state changed, false otherwise
*/
- public boolean handleStateEvent(int event, int deviceId) {
- boolean changed = false;
+ public boolean handleStateEvent(int event) {
+ final boolean changed;
synchronized (this) {
-
- // Do not update if it is only device id update
- if (event != PLAYER_UPDATE_DEVICE_ID) {
- changed = (mPlayerState != event);
- mPlayerState = event;
- }
-
- if (event == PLAYER_STATE_STARTED || event == PLAYER_UPDATE_DEVICE_ID) {
- changed = changed || (mDeviceId != deviceId);
- mDeviceId = deviceId;
- }
-
+ changed = (mPlayerState != event);
+ mPlayerState = event;
if (changed && (event == PLAYER_STATE_RELEASED) && (mIPlayerShell != null)) {
mIPlayerShell.release();
mIPlayerShell = null;
@@ -484,7 +436,7 @@ public final class AudioPlaybackConfiguration implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(mPlayerIId, mDeviceId, mPlayerType, mClientUid, mClientPid);
+ return Objects.hash(mPlayerIId, mPlayerType, mClientUid, mClientPid);
}
@Override
@@ -495,7 +447,6 @@ public final class AudioPlaybackConfiguration implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mPlayerIId);
- dest.writeInt(mDeviceId);
dest.writeInt(mPlayerType);
dest.writeInt(mClientUid);
dest.writeInt(mClientPid);
@@ -510,7 +461,6 @@ public final class AudioPlaybackConfiguration implements Parcelable {
private AudioPlaybackConfiguration(Parcel in) {
mPlayerIId = in.readInt();
- mDeviceId = in.readInt();
mPlayerType = in.readInt();
mClientUid = in.readInt();
mClientPid = in.readInt();
@@ -528,7 +478,6 @@ public final class AudioPlaybackConfiguration implements Parcelable {
AudioPlaybackConfiguration that = (AudioPlaybackConfiguration) o;
return ((mPlayerIId == that.mPlayerIId)
- && (mDeviceId == that.mDeviceId)
&& (mPlayerType == that.mPlayerType)
&& (mClientUid == that.mClientUid)
&& (mClientPid == that.mClientPid));
@@ -537,7 +486,6 @@ public final class AudioPlaybackConfiguration implements Parcelable {
@Override
public String toString() {
return "AudioPlaybackConfiguration piid:" + mPlayerIId
- + " deviceId:" + mDeviceId
+ " type:" + toLogFriendlyPlayerType(mPlayerType)
+ " u/pid:" + mClientUid + "/" + mClientPid
+ " state:" + toLogFriendlyPlayerState(mPlayerState)
@@ -623,7 +571,6 @@ public final class AudioPlaybackConfiguration implements Parcelable {
case PLAYER_STATE_STARTED: return "started";
case PLAYER_STATE_PAUSED: return "paused";
case PLAYER_STATE_STOPPED: return "stopped";
- case PLAYER_UPDATE_DEVICE_ID: return "device";
default:
return "unknown player state - FIXME";
}
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index e9a18e982ccc..e3b6fba18eb0 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1824,7 +1824,6 @@ public class AudioTrack extends PlayerBase
@Override
protected void finalize() {
- tryToDisableNativeRoutingCallback();
baseRelease();
native_finalize();
}
@@ -2718,15 +2717,9 @@ public class AudioTrack extends PlayerBase
}
private void startImpl() {
- synchronized (mRoutingChangeListeners) {
- if (!mEnableSelfRoutingMonitor) {
- testEnableNativeRoutingCallbacksLocked();
- mEnableSelfRoutingMonitor = true;
- }
- }
synchronized(mPlayStateLock) {
+ baseStart();
native_start();
- baseStart(native_getRoutedDeviceId());
if (mPlayState == PLAYSTATE_PAUSED_STOPPING) {
mPlayState = PLAYSTATE_STOPPING;
} else {
@@ -2764,7 +2757,6 @@ public class AudioTrack extends PlayerBase
mPlayStateLock.notify();
}
}
- tryToDisableNativeRoutingCallback();
}
/**
@@ -3504,21 +3496,12 @@ public class AudioTrack extends PlayerBase
return null;
}
- private void tryToDisableNativeRoutingCallback() {
- synchronized (mRoutingChangeListeners) {
- if (mEnableSelfRoutingMonitor) {
- mEnableSelfRoutingMonitor = false;
- testDisableNativeRoutingCallbacksLocked();
- }
- }
- }
-
/*
* Call BEFORE adding a routing callback handler.
*/
@GuardedBy("mRoutingChangeListeners")
private void testEnableNativeRoutingCallbacksLocked() {
- if (mRoutingChangeListeners.size() == 0 && !mEnableSelfRoutingMonitor) {
+ if (mRoutingChangeListeners.size() == 0) {
native_enableDeviceCallback();
}
}
@@ -3528,7 +3511,7 @@ public class AudioTrack extends PlayerBase
*/
@GuardedBy("mRoutingChangeListeners")
private void testDisableNativeRoutingCallbacksLocked() {
- if (mRoutingChangeListeners.size() == 0 && !mEnableSelfRoutingMonitor) {
+ if (mRoutingChangeListeners.size() == 0) {
native_disableDeviceCallback();
}
}
@@ -3545,9 +3528,6 @@ public class AudioTrack extends PlayerBase
private ArrayMap<AudioRouting.OnRoutingChangedListener,
NativeRoutingEventHandlerDelegate> mRoutingChangeListeners = new ArrayMap<>();
- @GuardedBy("mRoutingChangeListeners")
- private boolean mEnableSelfRoutingMonitor;
-
/**
* Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
* changes on this AudioTrack.
@@ -3647,7 +3627,6 @@ public class AudioTrack extends PlayerBase
*/
private void broadcastRoutingChange() {
AudioManager.resetAudioPortGeneration();
- baseUpdateDeviceId(getRoutedDevice());
synchronized (mRoutingChangeListeners) {
for (NativeRoutingEventHandlerDelegate delegate : mRoutingChangeListeners.values()) {
delegate.notifyClient();
diff --git a/media/java/android/media/HwAudioSource.java b/media/java/android/media/HwAudioSource.java
index bbf632a406ec..01a02f1d9f05 100644
--- a/media/java/android/media/HwAudioSource.java
+++ b/media/java/android/media/HwAudioSource.java
@@ -22,8 +22,6 @@ import android.annotation.SystemApi;
import com.android.internal.util.Preconditions;
-import java.util.ArrayList;
-
/**
* The HwAudioSource represents the audio playback directly from a source audio device.
* It currently supports {@link HwAudioSource#start()} and {@link HwAudioSource#stop()} only
@@ -132,32 +130,10 @@ public class HwAudioSource extends PlayerBase {
*/
public void start() {
Preconditions.checkState(!isPlaying(), "HwAudioSource is currently playing");
+ baseStart();
mNativeHandle = AudioSystem.startAudioSource(
mAudioDeviceInfo.getPort().activeConfig(),
mAudioAttributes);
- // FIXME: b/174876389 clean up device id reporting
- baseStart(getDeviceId());
- }
-
- private int getDeviceId() {
- ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
- if (AudioManager.listAudioPatches(patches) != AudioManager.SUCCESS) {
- return 0;
- }
-
- for (int i = 0; i < patches.size(); i++) {
- AudioPatch patch = patches.get(i);
- AudioPortConfig[] sources = patch.sources();
- AudioPortConfig[] sinks = patch.sinks();
- if ((sources != null) && (sources.length > 0)) {
- for (int c = 0; c < sources.length; c++) {
- if (sources[c].port().id() == mAudioDeviceInfo.getId()) {
- return sinks[c].port().id();
- }
- }
- }
- }
- return 0;
}
/**
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index b6bb3a3df38b..bad0d19b723e 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -63,7 +63,7 @@ interface IAudioService {
oneway void playerAttributes(in int piid, in AudioAttributes attr);
- oneway void playerEvent(in int piid, in int event, in int deviceId);
+ oneway void playerEvent(in int piid, in int event);
oneway void releasePlayer(in int piid);
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index e7e83ebb001f..9657b25e7c18 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1865,10 +1865,6 @@ public final class MediaCodecInfo {
&& aligned.mMaxMacroBlockRate >= otherAligned.mMaxMacroBlockRate);
}
- /* package private */ boolean isEqualDimension(@NonNull PerformancePoint other) {
- return mWidth == other.mWidth && mHeight == other.mHeight;
- }
-
private @NonNull Size getCommonBlockSize(@NonNull PerformancePoint other) {
return new Size(
Math.max(mBlockSize.getWidth(), other.mBlockSize.getWidth()) * 16,
@@ -2010,8 +2006,11 @@ public final class MediaCodecInfo {
* codecs are active, should use that highest pixel count, and add the frame rates of
* each individual codec.
* <p class=note>
- * Supported resolution could be further restricted for 32-bit processes due to
- * the limited virtual memory space.
+ * 32-bit processes will not support resolutions larger than 4096x4096 due to
+ * the limited address space, but performance points will be presented as is.
+ * In other words, even though a component publishes a performance point for
+ * a resolution higher than 4096x4096, it does not mean that the resolution is supported
+ * for 32-bit processes.
*/
@Nullable
public List<PerformancePoint> getSupportedPerformancePoints() {
@@ -2215,28 +2214,6 @@ public final class MediaCodecInfo {
(a.getMaxFrameRate() != b.getMaxFrameRate()) ?
(a.getMaxFrameRate() < b.getMaxFrameRate() ? -1 : 1) : 0));
- // remove redundant points
- for (int i = 1; i < ret.size(); ++i) {
- PerformancePoint a = ret.get(i);
- for (int j = 0; j < i; ++j) {
- PerformancePoint b = ret.get(j);
- if (b.isEqualDimension(a) && b.covers(a)) {
- ret.set(i, null);
- break;
- }
- }
- }
- int newSize = 0;
- for (int i = 0; i < ret.size(); ++i) {
- PerformancePoint a = ret.get(i);
- if (a == null) {
- continue;
- }
- ret.set(newSize, a);
- ++newSize;
- }
- ret.setSize(newSize);
-
return Collections.unmodifiableList(ret);
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index ceac2c9d5e05..f8311cd580a9 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1352,6 +1352,7 @@ public class MediaPlayer extends PlayerBase
}
private void startImpl() {
+ baseStart();
stayAwake(true);
_start();
}
@@ -1377,6 +1378,7 @@ public class MediaPlayer extends PlayerBase
public void stop() throws IllegalStateException {
stayAwake(false);
_stop();
+ baseStop();
}
private native void _stop() throws IllegalStateException;
@@ -1390,6 +1392,7 @@ public class MediaPlayer extends PlayerBase
public void pause() throws IllegalStateException {
stayAwake(false);
_pause();
+ basePause();
}
private native void _pause() throws IllegalStateException;
@@ -1494,60 +1497,13 @@ public class MediaPlayer extends PlayerBase
return null;
}
-
- /**
- * Sends device list change notification to all listeners.
- */
- private void broadcastRoutingChange() {
- AudioManager.resetAudioPortGeneration();
- synchronized (mRoutingChangeListeners) {
- // Prevent the case where an event is triggered by registering a routing change
- // listener via the media player.
- if (mEnableSelfRoutingMonitor) {
- baseUpdateDeviceId(getRoutedDevice());
- }
- for (NativeRoutingEventHandlerDelegate delegate
- : mRoutingChangeListeners.values()) {
- delegate.notifyClient();
- }
- }
- }
-
/*
- * Call BEFORE adding a routing callback handler.
+ * Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
*/
@GuardedBy("mRoutingChangeListeners")
- private void testEnableNativeRoutingCallbacksLocked() {
- if (mRoutingChangeListeners.size() == 0 && !mEnableSelfRoutingMonitor) {
- native_enableDeviceCallback(true);
- }
- }
-
- private void tryToEnableNativeRoutingCallback() {
- synchronized (mRoutingChangeListeners) {
- if (!mEnableSelfRoutingMonitor) {
- testEnableNativeRoutingCallbacksLocked();
- mEnableSelfRoutingMonitor = true;
- }
- }
- }
-
- private void tryToDisableNativeRoutingCallback() {
- synchronized (mRoutingChangeListeners) {
- if (mEnableSelfRoutingMonitor) {
- mEnableSelfRoutingMonitor = false;
- testDisableNativeRoutingCallbacksLocked();
- }
- }
- }
-
- /*
- * Call AFTER removing a routing callback handler.
- */
- @GuardedBy("mRoutingChangeListeners")
- private void testDisableNativeRoutingCallbacksLocked() {
- if (mRoutingChangeListeners.size() == 0 && !mEnableSelfRoutingMonitor) {
- native_enableDeviceCallback(false);
+ private void enableNativeRoutingCallbacksLocked(boolean enabled) {
+ if (mRoutingChangeListeners.size() == 0) {
+ native_enableDeviceCallback(enabled);
}
}
@@ -1560,9 +1516,6 @@ public class MediaPlayer extends PlayerBase
private ArrayMap<AudioRouting.OnRoutingChangedListener,
NativeRoutingEventHandlerDelegate> mRoutingChangeListeners = new ArrayMap<>();
- @GuardedBy("mRoutingChangeListeners")
- private boolean mEnableSelfRoutingMonitor;
-
/**
* Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
* changes on this MediaPlayer.
@@ -1576,7 +1529,7 @@ public class MediaPlayer extends PlayerBase
Handler handler) {
synchronized (mRoutingChangeListeners) {
if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
- testEnableNativeRoutingCallbacksLocked();
+ enableNativeRoutingCallbacksLocked(true);
mRoutingChangeListeners.put(
listener, new NativeRoutingEventHandlerDelegate(this, listener,
handler != null ? handler : mEventHandler));
@@ -1595,8 +1548,8 @@ public class MediaPlayer extends PlayerBase
synchronized (mRoutingChangeListeners) {
if (mRoutingChangeListeners.containsKey(listener)) {
mRoutingChangeListeners.remove(listener);
+ enableNativeRoutingCallbacksLocked(false);
}
- testDisableNativeRoutingCallbacksLocked();
}
}
@@ -3348,7 +3301,6 @@ public class MediaPlayer extends PlayerBase
@Override
protected void finalize() {
- tryToDisableNativeRoutingCallback();
baseRelease();
native_finalize();
}
@@ -3463,8 +3415,6 @@ public class MediaPlayer extends PlayerBase
case MEDIA_STOPPED:
{
- tryToDisableNativeRoutingCallback();
- baseStop();
TimeProvider timeProvider = mTimeProvider;
if (timeProvider != null) {
timeProvider.onStopped();
@@ -3473,16 +3423,8 @@ public class MediaPlayer extends PlayerBase
break;
case MEDIA_STARTED:
- {
- baseStart(native_getRoutedDeviceId());
- tryToEnableNativeRoutingCallback();
- }
- // fall through
case MEDIA_PAUSED:
{
- if (msg.what == MEDIA_PAUSED) {
- basePause();
- }
TimeProvider timeProvider = mTimeProvider;
if (timeProvider != null) {
timeProvider.onPaused(msg.what == MEDIA_PAUSED);
@@ -3648,8 +3590,14 @@ public class MediaPlayer extends PlayerBase
break;
case MEDIA_AUDIO_ROUTING_CHANGED:
- broadcastRoutingChange();
- return;
+ AudioManager.resetAudioPortGeneration();
+ synchronized (mRoutingChangeListeners) {
+ for (NativeRoutingEventHandlerDelegate delegate
+ : mRoutingChangeListeners.values()) {
+ delegate.notifyClient();
+ }
+ }
+ return;
case MEDIA_TIME_DISCONTINUITY:
final OnMediaTimeDiscontinuityListener mediaTimeListener;
@@ -3855,7 +3803,6 @@ public class MediaPlayer extends PlayerBase
@Override
public void onCompletion(MediaPlayer mp) {
baseStop();
- tryToDisableNativeRoutingCallback();
}
};
diff --git a/media/java/android/media/OWNERS b/media/java/android/media/OWNERS
index cbc9ab7e4adf..cf06fad3e203 100644
--- a/media/java/android/media/OWNERS
+++ b/media/java/android/media/OWNERS
@@ -6,3 +6,4 @@ lajos@google.com
olly@google.com
andrewlewis@google.com
sungsoo@google.com
+jmtrivi@google.com
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index 58ae279d4df1..da69c6cbbb5c 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -90,8 +90,6 @@ public abstract class PlayerBase {
private float mPanMultiplierR = 1.0f;
@GuardedBy("mLock")
private float mVolMultiplier = 1.0f;
- @GuardedBy("mLock")
- private int mDeviceId;
/**
* Constructor. Must be given audio attributes, as they are required for AppOps.
@@ -154,35 +152,14 @@ public abstract class PlayerBase {
}
}
- void baseUpdateDeviceId(@Nullable AudioDeviceInfo deviceInfo) {
- int deviceId = 0;
- if (deviceInfo != null) {
- deviceId = deviceInfo.getId();
- }
- int piid;
- synchronized (mLock) {
- piid = mPlayerIId;
- mDeviceId = deviceId;
- }
- try {
- getService().playerEvent(piid,
- AudioPlaybackConfiguration.PLAYER_UPDATE_DEVICE_ID, deviceId);
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to audio service, "
- + deviceId
- + " device id will not be tracked for piid=" + piid, e);
- }
- }
-
- private void updateState(int state, int deviceId) {
+ private void updateState(int state) {
final int piid;
synchronized (mLock) {
mState = state;
piid = mPlayerIId;
- mDeviceId = deviceId;
}
try {
- getService().playerEvent(piid, state, deviceId);
+ getService().playerEvent(piid, state);
} catch (RemoteException e) {
Log.e(TAG, "Error talking to audio service, "
+ AudioPlaybackConfiguration.toLogFriendlyPlayerState(state)
@@ -190,11 +167,9 @@ public abstract class PlayerBase {
}
}
- void baseStart(int deviceId) {
- if (DEBUG) {
- Log.v(TAG, "baseStart() piid=" + mPlayerIId + " deviceId=" + deviceId);
- }
- updateState(AudioPlaybackConfiguration.PLAYER_STATE_STARTED, deviceId);
+ void baseStart() {
+ if (DEBUG) { Log.v(TAG, "baseStart() piid=" + mPlayerIId); }
+ updateState(AudioPlaybackConfiguration.PLAYER_STATE_STARTED);
synchronized (mLock) {
if (isRestricted_sync()) {
playerSetVolume(true/*muting*/,0, 0);
@@ -216,12 +191,12 @@ public abstract class PlayerBase {
void basePause() {
if (DEBUG) { Log.v(TAG, "basePause() piid=" + mPlayerIId); }
- updateState(AudioPlaybackConfiguration.PLAYER_STATE_PAUSED, 0);
+ updateState(AudioPlaybackConfiguration.PLAYER_STATE_PAUSED);
}
void baseStop() {
if (DEBUG) { Log.v(TAG, "baseStop() piid=" + mPlayerIId); }
- updateState(AudioPlaybackConfiguration.PLAYER_STATE_STOPPED, 0);
+ updateState(AudioPlaybackConfiguration.PLAYER_STATE_STOPPED);
}
void baseSetPan(float pan) {
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 797caf36203b..3f685a4c934e 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -303,8 +303,7 @@ public class SoundPool extends PlayerBase {
*/
public final int play(int soundID, float leftVolume, float rightVolume,
int priority, int loop, float rate) {
- // FIXME: b/174876164 implement device id for soundpool
- baseStart(0);
+ baseStart();
return _play(soundID, leftVolume, rightVolume, priority, loop, rate);
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 440d5bc2f167..6fa94f1b7a7b 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -337,8 +337,8 @@ C2DataIdInfo::C2DataIdInfo(uint32_t index, uint64_t value) : C2Param(kParamSize,
/////////////// MediaEvent ///////////////////////
-MediaEvent::MediaEvent(sp<Filter> filter, hidl_handle avHandle,
- uint64_t dataId, uint64_t dataSize, jobject obj) : mFilter(filter),
+MediaEvent::MediaEvent(sp<FilterClient> filterClient, hidl_handle avHandle,
+ uint64_t dataId, uint64_t dataSize, jobject obj) : mFilterClient(filterClient),
mDataId(dataId), mDataSize(dataSize), mBuffer(nullptr),
mDataIdRefCnt(0), mAvHandleRefCnt(0), mIonHandle(nullptr) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -359,12 +359,13 @@ MediaEvent::~MediaEvent() {
if (pC2Buffer != NULL) {
pC2Buffer->unregisterOnDestroyNotify(&DestroyCallback, this);
}
+ mFilterClient = NULL;
}
void MediaEvent::finalize() {
- if (mAvHandleRefCnt == 0) {
- mFilter->mFilterSp->releaseAvHandle(
- hidl_handle(mAvHandle), mDataIdRefCnt == 0 ? mDataId : 0);
+ if (mAvHandleRefCnt == 0 && mFilterClient != NULL) {
+ mFilterClient->releaseAvHandle(
+ mAvHandle, mDataIdRefCnt == 0 ? mDataId : 0);
native_handle_close(mAvHandle);
}
}
@@ -382,21 +383,25 @@ jobject MediaEvent::getLinearBlock() {
int numInts = 0;
int memIndex;
int dataSize;
+ SharedHandleInfo info = mFilterClient->getAvSharedHandleInfo();
+ native_handle_t* avSharedHandle = info.sharedHandle;
+ uint64_t avSharedMemSize = info.size;
+
if (mAvHandle->numFds == 0) {
- if (mFilter->mAvSharedHandle == NULL) {
+ if (avSharedHandle == NULL) {
ALOGE("Shared AV memory handle is not initialized.");
return NULL;
}
- if (mFilter->mAvSharedHandle->numFds == 0) {
+ if (avSharedHandle->numFds == 0) {
ALOGE("Shared AV memory handle is empty.");
return NULL;
}
- fd = mFilter->mAvSharedHandle->data[0];
- dataSize = mFilter->mAvSharedMemSize;
- numInts = mFilter->mAvSharedHandle->numInts;
+ fd = avSharedHandle->data[0];
+ dataSize = avSharedMemSize;
+ numInts = avSharedHandle->numInts;
if (numInts > 0) {
// If the first int in the shared native handle has value, use it as the index
- memIndex = mFilter->mAvSharedHandle->data[mFilter->mAvSharedHandle->numFds];
+ memIndex = avSharedHandle->data[avSharedHandle->numFds];
}
} else {
fd = mAvHandle->data[0];
@@ -407,11 +412,11 @@ jobject MediaEvent::getLinearBlock() {
// event has value, use it as the index
memIndex = mAvHandle->data[mAvHandle->numFds];
} else {
- if (mFilter->mAvSharedHandle != NULL) {
- numInts = mFilter->mAvSharedHandle->numInts;
+ if (avSharedHandle != NULL) {
+ numInts = avSharedHandle->numInts;
if (numInts > 0) {
// If the first int in the shared native handle has value, use it as the index
- memIndex = mFilter->mAvSharedHandle->data[mFilter->mAvSharedHandle->numFds];
+ memIndex = avSharedHandle->data[avSharedHandle->numFds];
}
}
}
@@ -462,9 +467,9 @@ uint64_t MediaEvent::getAudioHandle() {
return mDataId;
}
-/////////////// FilterCallback ///////////////////////
+/////////////// FilterClientCallbackImpl ///////////////////////
-jobjectArray FilterCallback::getSectionEvent(
+jobjectArray FilterClientCallbackImpl::getSectionEvent(
jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/SectionEvent");
@@ -486,7 +491,7 @@ jobjectArray FilterCallback::getSectionEvent(
return arr;
}
-jobjectArray FilterCallback::getMediaEvent(
+jobjectArray FilterClientCallbackImpl::getMediaEvent(
jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MediaEvent");
@@ -537,7 +542,7 @@ jobjectArray FilterCallback::getMediaEvent(
if (mediaEvent.avMemory.getNativeHandle() != NULL || mediaEvent.avDataId != 0) {
sp<MediaEvent> mediaEventSp =
- new MediaEvent(mFilter, mediaEvent.avMemory,
+ new MediaEvent(mFilterClient, mediaEvent.avMemory,
mediaEvent.avDataId, dataLength + offset, obj);
mediaEventSp->mAvHandleRefCnt++;
env->SetLongField(obj, eventContext, (jlong) mediaEventSp.get());
@@ -549,7 +554,7 @@ jobjectArray FilterCallback::getMediaEvent(
return arr;
}
-jobjectArray FilterCallback::getPesEvent(
+jobjectArray FilterClientCallbackImpl::getPesEvent(
jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/PesEvent");
@@ -570,7 +575,7 @@ jobjectArray FilterCallback::getPesEvent(
return arr;
}
-jobjectArray FilterCallback::getTsRecordEvent(
+jobjectArray FilterClientCallbackImpl::getTsRecordEvent(
jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events,
const std::vector<DemuxFilterEventExt::Event>& eventsExt) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -623,7 +628,7 @@ jobjectArray FilterCallback::getTsRecordEvent(
return arr;
}
-jobjectArray FilterCallback::getMmtpRecordEvent(
+jobjectArray FilterClientCallbackImpl::getMmtpRecordEvent(
jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events,
const std::vector<DemuxFilterEventExt::Event>& eventsExt) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -665,7 +670,7 @@ jobjectArray FilterCallback::getMmtpRecordEvent(
return arr;
}
-jobjectArray FilterCallback::getDownloadEvent(
+jobjectArray FilterClientCallbackImpl::getDownloadEvent(
jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/DownloadEvent");
@@ -689,7 +694,7 @@ jobjectArray FilterCallback::getDownloadEvent(
return arr;
}
-jobjectArray FilterCallback::getIpPayloadEvent(
+jobjectArray FilterClientCallbackImpl::getIpPayloadEvent(
jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/IpPayloadEvent");
@@ -705,7 +710,7 @@ jobjectArray FilterCallback::getIpPayloadEvent(
return arr;
}
-jobjectArray FilterCallback::getTemiEvent(
+jobjectArray FilterClientCallbackImpl::getTemiEvent(
jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/TemiEvent");
@@ -728,7 +733,7 @@ jobjectArray FilterCallback::getTemiEvent(
return arr;
}
-jobjectArray FilterCallback::getScramblingStatusEvent(
+jobjectArray FilterClientCallbackImpl::getScramblingStatusEvent(
jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/ScramblingStatusEvent");
@@ -740,7 +745,7 @@ jobjectArray FilterCallback::getScramblingStatusEvent(
return arr;
}
-jobjectArray FilterCallback::getIpCidChangeEvent(
+jobjectArray FilterClientCallbackImpl::getIpCidChangeEvent(
jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/IpCidChangeEvent");
@@ -752,7 +757,7 @@ jobjectArray FilterCallback::getIpCidChangeEvent(
return arr;
}
-jobjectArray FilterCallback::getRestartEvent(
+jobjectArray FilterClientCallbackImpl::getRestartEvent(
jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/RestartEvent");
@@ -764,9 +769,9 @@ jobjectArray FilterCallback::getRestartEvent(
return arr;
}
-Return<void> FilterCallback::onFilterEvent_1_1(const DemuxFilterEvent& filterEvent,
+void FilterClientCallbackImpl::onFilterEvent_1_1(const DemuxFilterEvent& filterEvent,
const DemuxFilterEventExt& filterEventExt) {
- ALOGD("FilterCallback::onFilterEvent_1_1");
+ ALOGD("FilterClientCallbackImpl::onFilterEvent_1_1");
JNIEnv *env = AndroidRuntime::getJNIEnv();
jobjectArray array;
@@ -848,14 +853,13 @@ Return<void> FilterCallback::onFilterEvent_1_1(const DemuxFilterEvent& filterEve
}
}
env->CallVoidMethod(
- mFilter->mFilterObj,
+ mFilterObj,
gFields.onFilterEventID,
array);
- return Void();
}
-Return<void> FilterCallback::onFilterEvent(const DemuxFilterEvent& filterEvent) {
- ALOGD("FilterCallback::onFilterEvent");
+void FilterClientCallbackImpl::onFilterEvent(const DemuxFilterEvent& filterEvent) {
+ ALOGD("FilterClientCallbackImpl::onFilterEvent");
std::vector<DemuxFilterEventExt::Event> emptyEventsExt;
DemuxFilterEventExt emptyFilterEventExt {
.events = emptyEventsExt,
@@ -863,49 +867,20 @@ Return<void> FilterCallback::onFilterEvent(const DemuxFilterEvent& filterEvent)
return onFilterEvent_1_1(filterEvent, emptyFilterEventExt);
}
-Return<void> FilterCallback::onFilterStatus(const DemuxFilterStatus status) {
- ALOGD("FilterCallback::onFilterStatus");
+void FilterClientCallbackImpl::onFilterStatus(const DemuxFilterStatus status) {
+ ALOGD("FilterClientCallbackImpl::onFilterStatus");
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->CallVoidMethod(
- mFilter->mFilterObj,
+ mFilterObj,
gFields.onFilterStatusID,
(jint)status);
- return Void();
-}
-
-void FilterCallback::setFilter(const sp<Filter> filter) {
- ALOGD("FilterCallback::setFilter");
- // JNI Object
- mFilter = filter;
-}
-
-FilterCallback::~FilterCallback() {}
-
-/////////////// Filter ///////////////////////
-
-Filter::Filter(sp<IFilter> sp, jobject obj) : mFilterSp(sp) {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- mFilterObj = env->NewWeakGlobalRef(obj);
-}
-
-Filter::~Filter() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
-
- env->DeleteWeakGlobalRef(mFilterObj);
- mFilterObj = NULL;
- EventFlag::deleteEventFlag(&mFilterMQEventFlag);
}
-int Filter::close() {
- Result r = mFilterSp->close();
- if (r == Result::SUCCESS) {
- EventFlag::deleteEventFlag(&mFilterMQEventFlag);
- }
- return (int)r;
-}
-
-sp<IFilter> Filter::getIFilter() {
- return mFilterSp;
+void FilterClientCallbackImpl::setFilter(jweak filterObj, sp<FilterClient> filterClient) {
+ ALOGD("FilterClientCallbackImpl::setFilter");
+ // Java Object
+ mFilterObj = filterObj;
+ mFilterClient = filterClient;
}
/////////////// TimeFilter ///////////////////////
@@ -927,22 +902,23 @@ sp<ITimeFilter> TimeFilter::getITimeFilter() {
return mTimeFilterSp;
}
-/////////////// FrontendCallback ///////////////////////
+/////////////// FrontendClientCallbackImpl ///////////////////////
-FrontendCallback::FrontendCallback(jweak tunerObj, FrontendId id) : mObject(tunerObj), mId(id) {}
+FrontendClientCallbackImpl::FrontendClientCallbackImpl(
+ jweak tunerObj, FrontendId id) : mObject(tunerObj), mId(id) {}
-Return<void> FrontendCallback::onEvent(FrontendEventType frontendEventType) {
- ALOGD("FrontendCallback::onEvent, type=%d", frontendEventType);
+void FrontendClientCallbackImpl::onEvent(FrontendEventType frontendEventType) {
+ ALOGD("FrontendClientCallbackImpl::onEvent, type=%d", frontendEventType);
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->CallVoidMethod(
mObject,
gFields.onFrontendEventID,
(jint)frontendEventType);
- return Void();
}
-Return<void> FrontendCallback::onScanMessage(FrontendScanMessageType type, const FrontendScanMessage& message) {
- ALOGD("FrontendCallback::onScanMessage, type=%d", type);
+void FrontendClientCallbackImpl::onScanMessage(
+ FrontendScanMessageType type, const FrontendScanMessage& message) {
+ ALOGD("FrontendClientCallbackImpl::onScanMessage, type=%d", type);
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass clazz = env->FindClass("android/media/tv/tuner/Tuner");
switch(type) {
@@ -1050,13 +1026,15 @@ Return<void> FrontendCallback::onScanMessage(FrontendScanMessageType type, const
mObject,
env->GetMethodID(clazz, "onDvbsStandard", "(I)V"),
standard);
- } else if (std.getDiscriminator() == FrontendScanMessage::Standard::hidl_discriminator::tStd) {
+ } else if (std.getDiscriminator() ==
+ FrontendScanMessage::Standard::hidl_discriminator::tStd) {
standard = (jint) std.tStd();
env->CallVoidMethod(
mObject,
env->GetMethodID(clazz, "onDvbtStandard", "(I)V"),
standard);
- } else if (std.getDiscriminator() == FrontendScanMessage::Standard::hidl_discriminator::sifStd) {
+ } else if (std.getDiscriminator() ==
+ FrontendScanMessage::Standard::hidl_discriminator::sifStd) {
standard = (jint) std.sifStd();
env->CallVoidMethod(
mObject,
@@ -1081,17 +1059,17 @@ Return<void> FrontendCallback::onScanMessage(FrontendScanMessageType type, const
}
env->CallVoidMethod(
mObject,
- env->GetMethodID(clazz, "onAtsc3PlpInfos", "([Landroid/media/tv/tuner/frontend/Atsc3PlpInfo;)V"),
+ env->GetMethodID(clazz, "onAtsc3PlpInfos",
+ "([Landroid/media/tv/tuner/frontend/Atsc3PlpInfo;)V"),
array);
break;
}
}
- return Void();
}
-Return<void> FrontendCallback::onScanMessageExt1_1(FrontendScanMessageTypeExt1_1 type,
+void FrontendClientCallbackImpl::onScanMessageExt1_1(FrontendScanMessageTypeExt1_1 type,
const FrontendScanMessageExt1_1& message) {
- ALOGD("FrontendCallback::onScanMessageExt1_1, type=%d", type);
+ ALOGD("FrontendClientCallbackImpl::onScanMessageExt1_1, type=%d", type);
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass clazz = env->FindClass("android/media/tv/tuner/Tuner");
switch(type) {
@@ -1165,7 +1143,6 @@ Return<void> FrontendCallback::onScanMessageExt1_1(FrontendScanMessageTypeExt1_1
default:
break;
}
- return Void();
}
/////////////// Tuner ///////////////////////
@@ -1202,7 +1179,11 @@ JTuner::~JTuner() {
env->DeleteWeakGlobalRef(mObject);
env->DeleteGlobalRef(mClass);
mTuner = NULL;
+ mFe = NULL;
+ mDemux = NULL;
mTunerClient = NULL;
+ mFeClient = NULL;
+ mDemuxClient = NULL;
mClass = NULL;
mObject = NULL;
}
@@ -1252,9 +1233,11 @@ jobject JTuner::getFrontendIds() {
return obj;
}
-jobject JTuner::openFrontendById(int id) {
+jobject JTuner::openFrontendByHandle(int feHandle) {
sp<IFrontend> fe;
Result res;
+ uint32_t id = getResourceIdFromHandle(feHandle);
+
mTuner->openFrontendById(id, [&](Result r, const sp<IFrontend>& frontend) {
fe = frontend;
res = r;
@@ -1269,11 +1252,25 @@ jobject JTuner::openFrontendById(int id) {
if (mDemux != NULL) {
mDemux->setFrontendDataSource(mFeId);
}
- sp<FrontendCallback> feCb = new FrontendCallback(mObject, id);
- fe->setCallback(feCb);
jint jId = (jint) id;
+ // TODO: Handle reopening frontend with different handle
+ sp<FrontendClient> feClient = mTunerClient->openFrontend(feHandle);
+ if (feClient == NULL) {
+ ALOGE("Failed to open frontend");
+ return NULL;
+ }
+ mFeClient = feClient;
+
+ mFeId = mFeClient->getId();
+ jId = (jint) id;
+ if (mDemuxClient != NULL) {
+ mDemuxClient->setFrontendDataSource(mFeClient);
+ }
+ sp<FrontendClientCallbackImpl> feClientCb = new FrontendClientCallbackImpl(mObject, id);
+ mFeClient->setCallback(feClientCb);
+
JNIEnv *env = AndroidRuntime::getJNIEnv();
// TODO: add more fields to frontend
return env->NewObject(
@@ -1589,30 +1586,19 @@ jobject JTuner::openLnbByName(jstring name) {
}
int JTuner::tune(const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1) {
- if (mFe == NULL) {
+ if (mFeClient == nullptr) {
ALOGE("frontend is not initialized");
return (int)Result::INVALID_STATE;
}
- Result result;
- sp<::android::hardware::tv::tuner::V1_1::IFrontend> fe_1_1 =
- ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe);
- if (fe_1_1 == NULL) {
- ALOGD("1.1 frontend is not found. Using 1.0 instead.");
- result = mFe->tune(settings);
- return (int)result;
- }
-
- result = fe_1_1->tune_1_1(settings, settingsExt1_1);
- return (int)result;
+ return (int) mFeClient->tune(settings,settingsExt1_1);
}
int JTuner::stopTune() {
- if (mFe == NULL) {
+ if (mFeClient == nullptr) {
ALOGE("frontend is not initialized");
return (int)Result::INVALID_STATE;
}
- Result result = mFe->stopTune();
- return (int)result;
+ return (int) mFeClient->stopTune();
}
int JTuner::scan(const FrontendSettings& settings, FrontendScanType scanType,
@@ -1662,28 +1648,47 @@ int JTuner::setLna(bool enable) {
}
Result JTuner::openDemux() {
- if (mTuner == nullptr) {
+ if (mTuner == nullptr || mTunerClient == nullptr) {
return Result::NOT_INITIALIZED;
}
- if (mDemux != nullptr) {
- return Result::SUCCESS;
+
+ Result res = Result::SUCCESS;
+
+ if (mDemux == nullptr) {
+ uint32_t id;
+ sp<IDemux> demuxSp;
+ mTuner->openDemux([&](Result r, uint32_t demuxId, const sp<IDemux>& demux) {
+ demuxSp = demux;
+ id = demuxId;
+ res = r;
+ ALOGD("open demux, id = %d", demuxId);
+ });
+ if (res == Result::SUCCESS) {
+ mDemux = demuxSp;
+ mDemuxId = id;
+ if (mFe != NULL) {
+ mDemux->setFrontendDataSource(mFeId);
+ }
+ } else {
+ return res;
+ }
}
- Result res;
- uint32_t id;
- sp<IDemux> demuxSp;
- mTuner->openDemux([&](Result r, uint32_t demuxId, const sp<IDemux>& demux) {
- demuxSp = demux;
- id = demuxId;
- res = r;
- ALOGD("open demux, id = %d", demuxId);
- });
- if (res == Result::SUCCESS) {
- mDemux = demuxSp;
- mDemuxId = id;
- if (mFe != NULL) {
- mDemux->setFrontendDataSource(mFeId);
+
+ // TODO: replace demux opening with mTunerClient->openDemux(handle)
+ // when DemuxClient is fully ready
+ if (mDemuxClient == nullptr) {
+ sp<DemuxClient> demuxClient = new DemuxClient();
+ if (demuxClient == NULL) {
+ ALOGE("Failed to open demux");
+ return Result::UNKNOWN_ERROR;
+ }
+ mDemuxClient = demuxClient;
+ mDemuxClient->setHidlDemux(mDemux);
+ if (mFeClient != NULL) {
+ mDemuxClient->setFrontendDataSource(mFeClient);
}
}
+
return res;
}
@@ -1701,23 +1706,31 @@ jint JTuner::close() {
return (jint) res;
}
}
+
+ if (mFeClient != NULL) {
+ res = mFeClient->close();
+ if (res != Result::SUCCESS) {
+ return (jint) res;
+ }
+ mFeClient = NULL;
+ }
+ if (mDemuxClient != NULL) {
+ res = mDemuxClient->close();
+ if (res != Result::SUCCESS) {
+ return (jint) res;
+ }
+ mDemuxClient = NULL;
+ }
return (jint) res;
}
-jobject JTuner::getAvSyncHwId(sp<Filter> filter) {
- if (mDemux == NULL) {
+jobject JTuner::getAvSyncHwId(sp<FilterClient> filterClient) {
+ if (mDemuxClient == NULL) {
return NULL;
}
- uint32_t avSyncHwId;
- Result res;
- sp<IFilter> iFilterSp = filter->getIFilter();
- mDemux->getAvSyncHwId(iFilterSp,
- [&](Result r, uint32_t id) {
- res = r;
- avSyncHwId = id;
- });
- if (res == Result::SUCCESS) {
+ int avSyncHwId = mDemuxClient->getAvSyncHwId(filterClient);
+ if (avSyncHwId >= 0) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass integerClazz = env->FindClass("java/lang/Integer");
jmethodID intInit = env->GetMethodID(integerClazz, "<init>", "(I)V");
@@ -1727,17 +1740,11 @@ jobject JTuner::getAvSyncHwId(sp<Filter> filter) {
}
jobject JTuner::getAvSyncTime(jint id) {
- if (mDemux == NULL) {
+ if (mDemuxClient == NULL) {
return NULL;
}
- uint64_t time;
- Result res;
- mDemux->getAvSyncTime(static_cast<uint32_t>(id),
- [&](Result r, uint64_t ts) {
- res = r;
- time = ts;
- });
- if (res == Result::SUCCESS) {
+ long time = mDemuxClient->getAvSyncTime((int)id);
+ if (time >= 0) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass longClazz = env->FindClass("java/lang/Long");
jmethodID longInit = env->GetMethodID(longClazz, "<init>", "(J)V");
@@ -1747,13 +1754,13 @@ jobject JTuner::getAvSyncTime(jint id) {
}
int JTuner::connectCiCam(jint id) {
- if (mDemux == NULL) {
+ if (mDemuxClient == NULL) {
Result r = openDemux();
if (r != Result::SUCCESS) {
return (int) r;
}
}
- Result r = mDemux->connectCiCam(static_cast<uint32_t>(id));
+ Result r = mDemuxClient->connectCiCam((int)id);
return (int) r;
}
@@ -1779,13 +1786,13 @@ int JTuner::linkCiCam(int id) {
}
int JTuner::disconnectCiCam() {
- if (mDemux == NULL) {
+ if (mDemuxClient == NULL) {
Result r = openDemux();
if (r != Result::SUCCESS) {
return (int) r;
}
}
- Result r = mDemux->disconnectCiCam();
+ Result r = mDemuxClient->disconnectCiCam();
return (int) r;
}
@@ -1832,34 +1839,25 @@ jobject JTuner::openDescrambler() {
}
jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) {
- if (mDemux == NULL) {
+ if (mDemux == NULL || mDemuxClient == NULL) {
if (openDemux() != Result::SUCCESS) {
return NULL;
}
}
- sp<IFilter> iFilterSp;
- sp<::android::hardware::tv::tuner::V1_1::IFilter> iFilterSp_1_1;
- sp<FilterCallback> callback = new FilterCallback();
- Result res;
- mDemux->openFilter(type, bufferSize, callback,
- [&](Result r, const sp<IFilter>& filter) {
- iFilterSp = filter;
- res = r;
- });
- if (res != Result::SUCCESS || iFilterSp == NULL) {
+ sp<FilterClient> filterClient;
+ sp<FilterClientCallbackImpl> callback = new FilterClientCallbackImpl();
+ filterClient = mDemuxClient->openFilter(type, bufferSize, callback);
+ if (filterClient == NULL) {
ALOGD("Failed to open filter, type = %d", type.mainType);
return NULL;
}
uint64_t fId;
- iFilterSp->getId([&](Result, uint32_t filterId) {
- fId = filterId;
- });
- iFilterSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(iFilterSp);
- if (iFilterSp_1_1 != NULL) {
- iFilterSp_1_1->getId64Bit([&](Result, uint64_t filterId64Bit) {
- fId = filterId64Bit;
- });
+ Result res = filterClient->getId64Bit(fId);
+ if (res != Result::SUCCESS) {
+ uint32_t id;
+ filterClient->getId(id);
+ fId = static_cast<uint64_t>(id);
}
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -1869,35 +1867,9 @@ jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) {
gFields.filterInitID,
(jlong) fId);
- sp<Filter> filterSp = new Filter(iFilterSp, filterObj);
- filterSp->incStrong(filterObj);
- env->SetLongField(filterObj, gFields.filterContext, (jlong)filterSp.get());
- filterSp->mIsMediaFilter = false;
- filterSp->mAvSharedHandle = NULL;
- callback->setFilter(filterSp);
-
- if (type.mainType == DemuxFilterMainType::MMTP) {
- if (type.subType.mmtpFilterType() == DemuxMmtpFilterType::AUDIO ||
- type.subType.mmtpFilterType() == DemuxMmtpFilterType::VIDEO) {
- filterSp->mIsMediaFilter = true;
- }
- }
-
- if (type.mainType == DemuxFilterMainType::TS) {
- if (type.subType.tsFilterType() == DemuxTsFilterType::AUDIO ||
- type.subType.tsFilterType() == DemuxTsFilterType::VIDEO) {
- filterSp->mIsMediaFilter = true;
- }
- }
-
- if (iFilterSp_1_1 != NULL && filterSp->mIsMediaFilter) {
- iFilterSp_1_1->getAvSharedHandle([&](Result r, hidl_handle avMemory, uint64_t avMemSize) {
- if (r == Result::SUCCESS) {
- filterSp->mAvSharedHandle = native_handle_clone(avMemory.getNativeHandle());
- filterSp->mAvSharedMemSize = avMemSize;
- }
- });
- }
+ filterClient->incStrong(filterObj);
+ env->SetLongField(filterObj, gFields.filterContext, (jlong)filterClient.get());
+ callback->setFilter(env->NewWeakGlobalRef(filterObj), filterClient);
return filterObj;
}
@@ -2616,6 +2588,15 @@ jint JTuner::closeFrontend() {
if (r == Result::SUCCESS) {
mFe = NULL;
mFe_1_1 = NULL;
+ } else {
+ return (jint) r;
+ }
+
+ if (mFeClient != NULL) {
+ r = mFeClient->close();
+ }
+ if (r == Result::SUCCESS) {
+ mFeClient = NULL;
}
return (jint) r;
}
@@ -2627,10 +2608,18 @@ jint JTuner::closeDemux() {
}
if (r == Result::SUCCESS) {
mDemux = NULL;
+ } else {
+ return (jint) r;
+ }
+
+ if (mDemuxClient != NULL) {
+ r = mDemuxClient->close();
+ }
+ if (r == Result::SUCCESS) {
+ mDemuxClient = NULL;
}
return (jint) r;
}
-
} // namespace android
////////////////////////////////////////////////////////////////////////////////
@@ -3271,8 +3260,8 @@ static FrontendSettingsExt1_1 getFrontendSettingsExt1_1(JNIEnv *env, int type, j
return settingsExt1_1;
}
-static sp<Filter> getFilter(JNIEnv *env, jobject filter) {
- return (Filter *)env->GetLongField(filter, gFields.filterContext);
+static sp<FilterClient> getFilterClient(JNIEnv *env, jobject filter) {
+ return (FilterClient *)env->GetLongField(filter, gFields.filterContext);
}
static DvrSettings getDvrSettings(JNIEnv *env, jobject settings, bool isRecorder) {
@@ -3383,7 +3372,7 @@ static void android_media_tv_Tuner_native_init(JNIEnv *env) {
static void android_media_tv_Tuner_native_setup(JNIEnv *env, jobject thiz) {
sp<JTuner> tuner = new JTuner(env, thiz);
- setTuner(env,thiz, tuner);
+ setTuner(env, thiz, tuner);
}
static jint android_media_tv_Tuner_native_get_tuner_version(JNIEnv *env, jobject thiz) {
@@ -3399,8 +3388,7 @@ static jobject android_media_tv_Tuner_get_frontend_ids(JNIEnv *env, jobject thiz
static jobject android_media_tv_Tuner_open_frontend_by_handle(
JNIEnv *env, jobject thiz, jint handle) {
sp<JTuner> tuner = getTuner(env, thiz);
- uint32_t id = getResourceIdFromHandle(handle);
- return tuner->openFrontendById(id);
+ return tuner->openFrontendByHandle(handle);
}
static int android_media_tv_Tuner_tune(JNIEnv *env, jobject thiz, jint type, jobject settings) {
@@ -3446,13 +3434,13 @@ static jobject android_media_tv_Tuner_get_frontend_status(
static jobject android_media_tv_Tuner_get_av_sync_hw_id(
JNIEnv *env, jobject thiz, jobject filter) {
- sp<Filter> filterSp = getFilter(env, filter);
- if (filterSp == NULL) {
- ALOGD("Failed to get sync ID. Filter not found");
+ sp<FilterClient> filterClient = getFilterClient(env, filter);
+ if (filterClient == NULL) {
+ ALOGD("Failed to get sync ID. Filter client not found");
return NULL;
}
sp<JTuner> tuner = getTuner(env, thiz);
- return tuner->getAvSyncHwId(filterSp);
+ return tuner->getAvSyncHwId(filterClient);
}
static jobject android_media_tv_Tuner_get_av_sync_time(JNIEnv *env, jobject thiz, jint id) {
@@ -3913,26 +3901,13 @@ static DemuxFilterSettings getFilterConfiguration(
}
static Result configureIpFilterContextId(
- JNIEnv *env, sp<IFilter> iFilterSp, jobject ipFilterConfigObj) {
+ JNIEnv *env, sp<FilterClient> filterClient, jobject ipFilterConfigObj) {
jclass clazz = env->FindClass(
"android/media/tv/tuner/filter/IpFilterConfiguration");
uint32_t cid = env->GetIntField(ipFilterConfigObj, env->GetFieldID(
clazz, "mIpFilterContextId", "I"));
- Result res = Result::SUCCESS;
- if (cid != static_cast<uint32_t>(Constant::INVALID_IP_FILTER_CONTEXT_ID)) {
- sp<::android::hardware::tv::tuner::V1_1::IFilter> iFilterSp_1_1;
- iFilterSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(iFilterSp);
- if (iFilterSp_1_1 != NULL) {
- res = iFilterSp_1_1->configureIpCid(cid);
- if (res != Result::SUCCESS) {
- return res;
- }
- } else {
- ALOGW("configureIpCid is not supported with the current HAL implementation.");
- }
- }
- return res;
+ return filterClient->configureIpFilterContextId(cid);
}
static jint copyData(JNIEnv *env, std::unique_ptr<MQ>& mq, EventFlag* flag, jbyteArray buffer,
@@ -3975,21 +3950,20 @@ static bool isAvFilterSettings(DemuxFilterSettings filterSettings) {
static jint android_media_tv_Tuner_configure_filter(
JNIEnv *env, jobject filter, int type, int subtype, jobject settings) {
ALOGD("configure filter type=%d, subtype=%d", type, subtype);
- sp<Filter> filterSp = getFilter(env, filter);
- sp<IFilter> iFilterSp = filterSp->getIFilter();
- if (iFilterSp == NULL) {
+ sp<FilterClient> filterClient = getFilterClient(env, filter);
+ if (filterClient == NULL) {
ALOGD("Failed to configure filter: filter not found");
return (jint) Result::NOT_INITIALIZED;
}
DemuxFilterSettings filterSettings = getFilterConfiguration(env, type, subtype, settings);
- Result res = iFilterSp->configure(filterSettings);
+ Result res = filterClient->configure(filterSettings);
if (res != Result::SUCCESS) {
return (jint) res;
}
if (static_cast<DemuxFilterMainType>(type) == DemuxFilterMainType::IP) {
- res = configureIpFilterContextId(env, iFilterSp, settings);
+ res = configureIpFilterContextId(env, filterClient, settings);
if (res != Result::SUCCESS) {
return (jint) res;
}
@@ -3997,49 +3971,19 @@ static jint android_media_tv_Tuner_configure_filter(
AvStreamType streamType;
if (isAvFilterSettings(filterSettings) && getAvStreamType(env, settings, streamType)) {
- sp<::android::hardware::tv::tuner::V1_1::IFilter> iFilterSp_1_1;
- iFilterSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(iFilterSp);
- if (iFilterSp_1_1 != NULL) {
- res = iFilterSp_1_1->configureAvStreamType(streamType);
- } else {
- ALOGW("configureAvStreamType is not supported with the current HAL implementation.");
- }
- if (res != Result::SUCCESS) {
- return (jint) res;
- }
- }
-
- MQDescriptorSync<uint8_t> filterMQDesc;
- Result getQueueDescResult = Result::UNKNOWN_ERROR;
- if (filterSp->mFilterMQ == NULL) {
- iFilterSp->getQueueDesc(
- [&](Result r, const MQDescriptorSync<uint8_t>& desc) {
- filterMQDesc = desc;
- getQueueDescResult = r;
- ALOGD("getFilterQueueDesc");
- });
- if (getQueueDescResult == Result::SUCCESS) {
- filterSp->mFilterMQ = std::make_unique<MQ>(filterMQDesc, true);
- EventFlag::createEventFlag(
- filterSp->mFilterMQ->getEventFlagWord(), &(filterSp->mFilterMQEventFlag));
- }
+ res = filterClient->configureAvStreamType(streamType);
}
- return (jint) getQueueDescResult;
+ return (jint) res;
}
static jint android_media_tv_Tuner_get_filter_id(JNIEnv* env, jobject filter) {
- sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter();
- if (iFilterSp == NULL) {
- ALOGD("Failed to get filter ID: filter not found");
+ sp<FilterClient> filterClient = getFilterClient(env, filter);
+ if (filterClient == NULL) {
+ ALOGD("Failed to get filter ID: filter client not found");
return (int) Result::NOT_INITIALIZED;
}
- Result res;
uint32_t id;
- iFilterSp->getId(
- [&](Result r, uint32_t filterId) {
- res = r;
- id = filterId;
- });
+ Result res = filterClient->getId(id);
if (res != Result::SUCCESS) {
return (jint) Constant::INVALID_FILTER_ID;
}
@@ -4047,30 +3991,13 @@ static jint android_media_tv_Tuner_get_filter_id(JNIEnv* env, jobject filter) {
}
static jlong android_media_tv_Tuner_get_filter_64bit_id(JNIEnv* env, jobject filter) {
- sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter();
- if (iFilterSp == NULL) {
- ALOGD("Failed to get filter ID: filter not found");
- return static_cast<jlong>(
- ::android::hardware::tv::tuner::V1_1::Constant64Bit::INVALID_FILTER_ID_64BIT);
+ sp<FilterClient> filterClient = getFilterClient(env, filter);
+ if (filterClient == NULL) {
+ ALOGD("Failed to get filter ID 64 bit: filter client not found");
+ return (int) Result::NOT_INITIALIZED;
}
-
- sp<::android::hardware::tv::tuner::V1_1::IFilter> iFilterSp_1_1;
- iFilterSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(iFilterSp);
- Result res;
uint64_t id;
-
- if (iFilterSp_1_1 != NULL) {
- iFilterSp_1_1->getId64Bit(
- [&](Result r, uint64_t filterId64Bit) {
- res = r;
- id = filterId64Bit;
- });
- } else {
- ALOGW("getId64Bit is not supported with the current HAL implementation.");
- return static_cast<jlong>(
- ::android::hardware::tv::tuner::V1_1::Constant64Bit::INVALID_FILTER_ID_64BIT);
- }
-
+ Result res = filterClient->getId64Bit(id);
return (res == Result::SUCCESS) ?
static_cast<jlong>(id) : static_cast<jlong>(
::android::hardware::tv::tuner::V1_1::Constant64Bit::INVALID_FILTER_ID_64BIT);
@@ -4078,96 +4005,96 @@ static jlong android_media_tv_Tuner_get_filter_64bit_id(JNIEnv* env, jobject fil
static jint android_media_tv_Tuner_configure_monitor_event(
JNIEnv* env, jobject filter, int monitorEventType) {
- sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter();
- if (iFilterSp == NULL) {
- ALOGD("Failed to configure scrambling event: filter not found");
- return (jint) Result::NOT_INITIALIZED;
- }
-
- sp<::android::hardware::tv::tuner::V1_1::IFilter> iFilterSp_1_1;
- iFilterSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(iFilterSp);
- Result res;
-
- if (iFilterSp_1_1 != NULL) {
- res = iFilterSp_1_1->configureMonitorEvent(monitorEventType);
- } else {
- ALOGW("configureScramblingEvent is not supported with the current HAL implementation.");
- return (jint) Result::INVALID_STATE;
+ sp<FilterClient> filterClient = getFilterClient(env, filter);
+ if (filterClient == NULL) {
+ ALOGD("Failed to configure scrambling event: filter client not found");
+ return (int) Result::NOT_INITIALIZED;
}
-
+ Result res = filterClient->configureMonitorEvent(monitorEventType);
return (jint) res;
}
static jint android_media_tv_Tuner_set_filter_data_source(
JNIEnv* env, jobject filter, jobject srcFilter) {
- sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter();
- if (iFilterSp == NULL) {
- ALOGD("Failed to set filter data source: filter not found");
- return (jint) Result::NOT_INITIALIZED;
+ sp<FilterClient> filterClient = getFilterClient(env, filter);
+ if (filterClient == NULL) {
+ ALOGD("Failed to set filter data source: filter client not found");
+ return (int) Result::NOT_INITIALIZED;
}
- Result r;
+ Result res;
if (srcFilter == NULL) {
- r = iFilterSp->setDataSource(NULL);
+ res = filterClient->setDataSource(NULL);
} else {
- sp<IFilter> srcSp = getFilter(env, srcFilter)->getIFilter();
- if (iFilterSp == NULL) {
+ sp<FilterClient> srcClient = getFilterClient(env, srcFilter);
+ if (srcClient == NULL) {
ALOGD("Failed to set filter data source: src filter not found");
return (jint) Result::INVALID_ARGUMENT;
}
- r = iFilterSp->setDataSource(srcSp);
+ res = filterClient->setDataSource(srcClient);
}
- return (jint) r;
+ return (jint) res;
}
static jint android_media_tv_Tuner_start_filter(JNIEnv *env, jobject filter) {
- sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter();
- if (iFilterSp == NULL) {
- ALOGD("Failed to start filter: filter not found");
- return (jint) Result::NOT_INITIALIZED;
+ sp<FilterClient> filterClient = getFilterClient(env, filter);
+ if (filterClient == NULL) {
+ ALOGD("Failed to start filter: filter client not found");
+ return (int) Result::NOT_INITIALIZED;
}
- Result r = iFilterSp->start();
- return (jint) r;
+ return (jint) filterClient->start();
}
static jint android_media_tv_Tuner_stop_filter(JNIEnv *env, jobject filter) {
- sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter();
- if (iFilterSp == NULL) {
- ALOGD("Failed to stop filter: filter not found");
- return (jint) Result::NOT_INITIALIZED;
+ sp<FilterClient> filterClient = getFilterClient(env, filter);
+ if (filterClient == NULL) {
+ ALOGD("Failed to stop filter: filter client not found");
+ return (int) Result::NOT_INITIALIZED;
}
- Result r = iFilterSp->stop();
- return (jint) r;
+ return (jint) filterClient->stop();
}
static jint android_media_tv_Tuner_flush_filter(JNIEnv *env, jobject filter) {
- sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter();
- if (iFilterSp == NULL) {
- ALOGD("Failed to flush filter: filter not found");
- return (jint) Result::NOT_INITIALIZED;
+ sp<FilterClient> filterClient = getFilterClient(env, filter);
+ if (filterClient == NULL) {
+ ALOGD("Failed to flush filter: filter client not found");
+ return (int) Result::NOT_INITIALIZED;
}
- Result r = iFilterSp->flush();
- return (jint) r;
+ return (jint) filterClient->flush();
}
static jint android_media_tv_Tuner_read_filter_fmq(
JNIEnv *env, jobject filter, jbyteArray buffer, jlong offset, jlong size) {
- sp<Filter> filterSp = getFilter(env, filter);
- if (filterSp == NULL) {
+ sp<FilterClient> filterClient = getFilterClient(env, filter);
+ if (filterClient == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
- "Failed to read filter FMQ: filter not found");
+ "Failed to read filter FMQ: filter client not found");
return 0;
}
- return copyData(env, filterSp->mFilterMQ, filterSp->mFilterMQEventFlag, buffer, offset, size);
+
+ jboolean isCopy;
+ jbyte *dst = env->GetByteArrayElements(buffer, &isCopy);
+ ALOGD("copyData, isCopy=%d", isCopy);
+ if (dst == nullptr) {
+ jniThrowRuntimeException(env, "Failed to GetByteArrayElements");
+ return 0;
+ }
+ int realReadSize = filterClient->read(reinterpret_cast<uint8_t*>(dst) + offset, size);
+ env->ReleaseByteArrayElements(buffer, dst, 0);
+ if (realReadSize < 0) {
+ return (jint) Result::UNKNOWN_ERROR;
+ }
+ return (jint) Result::SUCCESS;
}
static jint android_media_tv_Tuner_close_filter(JNIEnv *env, jobject filter) {
- sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter();
- if (iFilterSp == NULL) {
- ALOGD("Failed to close filter: filter not found");
- return (jint) Result::NOT_INITIALIZED;
+ sp<FilterClient> filterClient = getFilterClient(env, filter);
+ if (filterClient == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Failed to close filter: filter client not found");
+ return 0;
}
- Result r = iFilterSp->close();
- return (jint) r;
+
+ return (jint) filterClient->close();
}
static sp<TimeFilter> getTimeFilter(JNIEnv *env, jobject filter) {
@@ -4275,7 +4202,8 @@ static jint android_media_tv_Tuner_descrambler_add_pid(
if (descramblerSp == NULL) {
return (jint) Result::NOT_INITIALIZED;
}
- sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter();
+ // TODO: use filter client once descramblerClient is ready
+ sp<IFilter> iFilterSp = getFilterClient(env, filter)->getHalFilter();
Result result = descramblerSp->addPid(getDemuxPid((int)pidType, (int)pid), iFilterSp);
return (jint) result;
}
@@ -4286,7 +4214,8 @@ static jint android_media_tv_Tuner_descrambler_remove_pid(
if (descramblerSp == NULL) {
return (jint) Result::NOT_INITIALIZED;
}
- sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter();
+ // TODO: use filter client once descramblerClient is ready
+ sp<IFilter> iFilterSp = getFilterClient(env, filter)->getHalFilter();
Result result = descramblerSp->removePid(getDemuxPid((int)pidType, (int)pid), iFilterSp);
return (jint) result;
}
@@ -4358,12 +4287,13 @@ static jint android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobje
if (dvrSp == NULL) {
return (jint) Result::NOT_INITIALIZED;
}
- sp<Filter> filterSp = getFilter(env, filter);
- if (filterSp == NULL) {
+ sp<FilterClient> filterClient = getFilterClient(env, filter);
+ if (filterClient == NULL) {
return (jint) Result::INVALID_ARGUMENT;
}
sp<IDvr> iDvrSp = dvrSp->getIDvr();
- sp<IFilter> iFilterSp = filterSp->getIFilter();
+ // TODO: use filter client once dvrClient is ready
+ sp<IFilter> iFilterSp = filterClient->getHalFilter();
Result result = iDvrSp->attachFilter(iFilterSp);
return (jint) result;
}
@@ -4373,12 +4303,13 @@ static jint android_media_tv_Tuner_detach_filter(JNIEnv *env, jobject dvr, jobje
if (dvrSp == NULL) {
return (jint) Result::NOT_INITIALIZED;
}
- sp<Filter> filterSp = getFilter(env, filter);
- if (filterSp == NULL) {
+ sp<FilterClient> filterClient = getFilterClient(env, filter);
+ if (filterClient == NULL) {
return (jint) Result::INVALID_ARGUMENT;
}
sp<IDvr> iDvrSp = dvrSp->getIDvr();
- sp<IFilter> iFilterSp = filterSp->getIFilter();
+ // TODO: use filter client once dvrClient is ready
+ sp<IFilter> iFilterSp = filterClient->getHalFilter();
Result result = iDvrSp->detachFilter(iFilterSp);
return (jint) result;
}
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index bec834cac238..414922852377 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -34,6 +34,11 @@
#include <utils/Mutex.h>
#include <utils/RefBase.h>
+#include "tuner/DemuxClient.h"
+#include "tuner/FilterClient.h"
+#include "tuner/FilterClientCallback.h"
+#include "tuner/FrontendClient.h"
+#include "tuner/FrontendClientCallback.h"
#include "tuner/TunerClient.h"
#include "jni.h"
@@ -95,6 +100,7 @@ struct Lnb : public RefBase {
Lnb(sp<ILnb> sp, jobject obj);
~Lnb();
sp<ILnb> getILnb();
+ // TODO: remove after migrate to client lib
sp<ILnb> mLnbSp;
jweak mLnbObj;
};
@@ -115,6 +121,7 @@ struct Dvr : public RefBase {
jint close();
MQ& getDvrMQ();
sp<IDvr> getIDvr();
+ // TODO: remove after migrate to client lib
sp<IDvr> mDvrSp;
jweak mDvrObj;
std::unique_ptr<MQ> mDvrMQ;
@@ -123,29 +130,15 @@ struct Dvr : public RefBase {
int mFd;
};
-struct Filter : public RefBase {
- Filter(sp<IFilter> sp, jobject obj);
- ~Filter();
- int close();
- sp<IFilter> getIFilter();
- sp<IFilter> mFilterSp;
- std::unique_ptr<MQ> mFilterMQ;
- EventFlag* mFilterMQEventFlag;
- jweak mFilterObj;
- native_handle_t* mAvSharedHandle;
- uint64_t mAvSharedMemSize;
- bool mIsMediaFilter;
-};
-
struct MediaEvent : public RefBase {
- MediaEvent(sp<Filter> filter, hidl_handle avHandle, uint64_t dataId,
+ MediaEvent(sp<FilterClient> filterClient, hidl_handle avHandle, uint64_t dataId,
uint64_t dataSize, jobject obj);
~MediaEvent();
jobject getLinearBlock();
uint64_t getAudioHandle();
void finalize();
- sp<Filter> mFilter;
+ sp<FilterClient> mFilterClient;
native_handle_t* mAvHandle;
uint64_t mDataId;
uint64_t mDataSize;
@@ -159,16 +152,16 @@ struct MediaEvent : public RefBase {
std::weak_ptr<C2Buffer> mC2Buffer;
};
-struct FilterCallback : public IFilterCallback {
- ~FilterCallback();
- virtual Return<void> onFilterEvent_1_1(const DemuxFilterEvent& filterEvent,
+struct FilterClientCallbackImpl : public FilterClientCallback {
+ virtual void onFilterEvent_1_1(const DemuxFilterEvent& filterEvent,
const DemuxFilterEventExt& filterEventExt);
- virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent);
- virtual Return<void> onFilterStatus(const DemuxFilterStatus status);
+ virtual void onFilterEvent(const DemuxFilterEvent& filterEvent);
+ virtual void onFilterStatus(const DemuxFilterStatus status);
- void setFilter(const sp<Filter> filter);
+ void setFilter(jweak filterObj, sp<FilterClient> filterClient);
private:
- sp<Filter> mFilter;
+ jweak mFilterObj;
+ sp<FilterClient> mFilterClient;
jobjectArray getSectionEvent(
jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
jobjectArray getMediaEvent(
@@ -195,13 +188,13 @@ private:
jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt);
};
-struct FrontendCallback : public IFrontendCallback {
- FrontendCallback(jweak tunerObj, FrontendId id);
+struct FrontendClientCallbackImpl : public FrontendClientCallback {
+ FrontendClientCallbackImpl(jweak tunerObj, FrontendId id);
- virtual Return<void> onEvent(FrontendEventType frontendEventType);
- virtual Return<void> onScanMessage(
+ virtual void onEvent(FrontendEventType frontendEventType);
+ virtual void onScanMessage(
FrontendScanMessageType type, const FrontendScanMessage& message);
- virtual Return<void> onScanMessageExt1_1(
+ virtual void onScanMessageExt1_1(
FrontendScanMessageTypeExt1_1 type, const FrontendScanMessageExt1_1& messageExt);
jweak mObject;
@@ -212,22 +205,24 @@ struct TimeFilter : public RefBase {
TimeFilter(sp<ITimeFilter> sp, jweak obj);
~TimeFilter();
sp<ITimeFilter> getITimeFilter();
+ // TODO: remove after migrate to client lib
sp<ITimeFilter> mTimeFilterSp;
jweak mTimeFilterObj;
};
struct JTuner : public RefBase {
JTuner(JNIEnv *env, jobject thiz);
+ // TODO: modify after migrate to client lib
sp<ITuner> getTunerService();
int getTunerVersion();
- jobject getAvSyncHwId(sp<Filter> filter);
+ jobject getAvSyncHwId(sp<FilterClient> filter);
jobject getAvSyncTime(jint id);
int connectCiCam(jint id);
int linkCiCam(jint id);
int disconnectCiCam();
int unlinkCiCam(jint id);
jobject getFrontendIds();
- jobject openFrontendById(int id);
+ jobject openFrontendByHandle(int feHandle);
jint closeFrontendById(int id);
jobject getFrontendInfo(int id);
int tune(const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
@@ -257,15 +252,22 @@ protected:
private:
jclass mClass;
jweak mObject;
+ // TODO: remove after migrate to client lib
static sp<ITuner> mTuner;
static sp<::android::hardware::tv::tuner::V1_1::ITuner> mTuner_1_1;
static sp<TunerClient> mTunerClient;
+ // TODO: remove after migrate to client lib
sp<IFrontend> mFe;
+ // TODO: remove after migrate to client lib
sp<::android::hardware::tv::tuner::V1_1::IFrontend> mFe_1_1;
+ sp<FrontendClient> mFeClient;
int mFeId;
hidl_vec<LnbId> mLnbIds;
+ // TODO: remove after migrate to client lib
sp<ILnb> mLnb;
+ // TODO: remove after migrate to client lib
sp<IDemux> mDemux;
+ sp<DemuxClient> mDemuxClient;
uint32_t mDemuxId;
static jobject getAnalogFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
static jobject getAtsc3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
@@ -279,6 +281,9 @@ private:
static jobject getDtmbFrontendCaps(JNIEnv *env, int id);
bool isV1_1ExtendedStatusType(jint type);
+ static uint32_t getResourceIdFromHandle(jint handle) {
+ return (handle & 0x00ff0000) >> 16;
+ }
};
class C2DataIdInfo : public C2Param {
diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp
index 11acb5ebf52e..b237a242d03e 100644
--- a/media/jni/tuner/DemuxClient.cpp
+++ b/media/jni/tuner/DemuxClient.cpp
@@ -68,14 +68,12 @@ sp<FilterClient> DemuxClient::openFilter(DemuxFilterType type, int bufferSize,
sp<HidlFilterCallback> callback = new HidlFilterCallback(cb);
sp<IFilter> hidlFilter = openHidlFilter(type, bufferSize, callback);
if (hidlFilter != NULL) {
- sp<FilterClient> filterClient = new FilterClient();
+ sp<FilterClient> filterClient = new FilterClient(type);
filterClient->setHidlFilter(hidlFilter);
return filterClient;
}
}
- // TODO: handle share av memory handle
-
return NULL;
}
@@ -141,7 +139,7 @@ Result DemuxClient::disconnectCiCam() {
}
Result DemuxClient::close() {
- // pending aidl interface
+ // TODO: pending aidl interface
if (mDemux != NULL) {
Result res = mDemux->close();
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index a71cae60e464..0aab5fe7de86 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -22,6 +22,9 @@
#include "FilterClient.h"
using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
+using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
namespace android {
@@ -29,20 +32,25 @@ namespace android {
// TODO: pending aidl interface
// TODO: add filter callback
-FilterClient::FilterClient() {
+FilterClient::FilterClient(DemuxFilterType type) {
//mTunerFilter = tunerFilter;
+ mAvSharedHandle = NULL;
+ checkIsMediaFilter(type);
}
FilterClient::~FilterClient() {
//mTunerFilter = NULL;
mFilter = NULL;
mFilter_1_1 = NULL;
+ mAvSharedHandle = NULL;
+ mAvSharedMemSize = 0;
}
// TODO: remove after migration to Tuner Service is done.
void FilterClient::setHidlFilter(sp<IFilter> filter) {
mFilter = filter;
mFilter_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilter);
+ handleAvShareMemory();
}
int FilterClient::read(uint8_t* buffer, int size) {
@@ -59,6 +67,22 @@ int FilterClient::read(uint8_t* buffer, int size) {
return -1;
}
+SharedHandleInfo FilterClient::getAvSharedHandleInfo() {
+ SharedHandleInfo info{
+ .sharedHandle = NULL,
+ .size = 0,
+ };
+
+ // TODO: pending aidl interface
+
+ if (mFilter_1_1 != NULL) {
+ info.sharedHandle = mAvSharedHandle;
+ info.size = mAvSharedMemSize;
+ }
+
+ return info;
+}
+
Result FilterClient::configure(DemuxFilterSettings configure) {
// TODO: pending aidl interface
@@ -187,7 +211,11 @@ Result FilterClient::close() {
// TODO: pending aidl interface
if (mFilter != NULL) {
- return mFilter->close();
+ Result res = mFilter->close();
+ if (res == Result::SUCCESS) {
+ mFilter = NULL;
+ }
+ return res;
}
return Result::INVALID_STATE;
@@ -261,4 +289,31 @@ int FilterClient::copyData(uint8_t* buffer, int size) {
return size;
}
+
+void FilterClient::checkIsMediaFilter(DemuxFilterType type) {
+ if (type.mainType == DemuxFilterMainType::MMTP) {
+ if (type.subType.mmtpFilterType() == DemuxMmtpFilterType::AUDIO ||
+ type.subType.mmtpFilterType() == DemuxMmtpFilterType::VIDEO) {
+ mIsMediaFilter = true;
+ }
+ }
+
+ if (type.mainType == DemuxFilterMainType::TS) {
+ if (type.subType.tsFilterType() == DemuxTsFilterType::AUDIO ||
+ type.subType.tsFilterType() == DemuxTsFilterType::VIDEO) {
+ mIsMediaFilter = true;
+ }
+ }
+}
+
+void FilterClient::handleAvShareMemory() {
+ if (mFilter_1_1 != NULL && mIsMediaFilter) {
+ mFilter_1_1->getAvSharedHandle([&](Result r, hidl_handle avMemory, uint64_t avMemSize) {
+ if (r == Result::SUCCESS) {
+ mAvSharedHandle = native_handle_clone(avMemory.getNativeHandle());
+ mAvSharedMemSize = avMemSize;
+ }
+ });
+ }
+}
} // namespace android
diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h
index 4af74b06d00b..976b2f5a107d 100644
--- a/media/jni/tuner/FilterClient.h
+++ b/media/jni/tuner/FilterClient.h
@@ -34,6 +34,7 @@ using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hardware::hidl_handle;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
using ::android::hardware::tv::tuner::V1_0::IFilter;
using ::android::hardware::tv::tuner::V1_0::Result;
using ::android::hardware::tv::tuner::V1_1::AvStreamType;
@@ -45,6 +46,11 @@ using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
namespace android {
+struct SharedHandleInfo {
+ native_handle_t* sharedHandle;
+ uint64_t size;
+};
+
// TODO: pending aidl interface
/*class TunerFilterCallback : public BnTunerFilterCallback {
@@ -75,7 +81,7 @@ struct FilterClient : public RefBase {
public:
// TODO: pending aidl interface
- FilterClient();
+ FilterClient(DemuxFilterType type);
~FilterClient();
// TODO: remove after migration to Tuner Service is done.
@@ -89,6 +95,11 @@ public:
int read(uint8_t* buffer, int size);
/**
+ * Get the a/v shared memory handle information
+ */
+ SharedHandleInfo getAvSharedHandleInfo();
+
+ /**
* Configure the filter.
*/
Result configure(DemuxFilterSettings configure);
@@ -161,6 +172,8 @@ public:
private:
Result getFilterMq();
int copyData(uint8_t* buffer, int size);
+ void checkIsMediaFilter(DemuxFilterType type);
+ void handleAvShareMemory();
/**
* An AIDL Tuner Filter Singleton assigned at the first time when the Tuner Client
@@ -189,6 +202,10 @@ private:
sp<FilterClientCallback> mCallback;
//shared_ptr<TunerFilterCallback> mAidlCallback;
sp<HidlFilterCallback> mHidlCallback;
+
+ native_handle_t* mAvSharedHandle;
+ uint64_t mAvSharedMemSize;
+ bool mIsMediaFilter;
};
} // namespace android
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
index fc16eb6b4277..61af5a74b00a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
@@ -22,16 +22,6 @@ package com.android.settingslib.media;
public class MediaOutputSliceConstants {
/**
- * Key for the Media output setting.
- */
- public static final String KEY_MEDIA_OUTPUT = "media_output";
-
- /**
- * Key for the Media output group setting.
- */
- public static final String KEY_MEDIA_OUTPUT_GROUP = "media_output_group";
-
- /**
* Key for the Remote Media slice.
*/
public static final String KEY_REMOTE_MEDIA = "remote_media";
@@ -47,19 +37,6 @@ public class MediaOutputSliceConstants {
public static final String KEY_SESSION_INFO_ID = "key_session_info_id";
/**
- * Activity Action: Show a settings dialog containing {@link MediaDevice} to transfer media.
- */
- public static final String ACTION_MEDIA_OUTPUT =
- "com.android.settings.panel.action.MEDIA_OUTPUT";
-
- /**
- * Activity Action: Show a settings dialog containing {@link MediaDevice} to handle media group
- * operation.
- */
- public static final String ACTION_MEDIA_OUTPUT_GROUP =
- "com.android.settings.panel.action.MEDIA_OUTPUT_GROUP";
-
- /**
* A string extra specifying a media package name.
*/
public static final String EXTRA_PACKAGE_NAME =
@@ -72,12 +49,6 @@ public class MediaOutputSliceConstants {
"com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG";
/**
- * An intent action to dismiss media output dialog.
- */
- public static final String ACTION_DISMISS_MEDIA_OUTPUT_DIALOG =
- "com.android.systemui.action.DISMISS_MEDIA_OUTPUT_DIALOG";
-
- /**
* Settings package name.
*/
public static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
diff --git a/packages/SystemUI/res/drawable/ic_fingerprint.xml b/packages/SystemUI/res/drawable/ic_fingerprint.xml
new file mode 100644
index 000000000000..e5f33602164e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_fingerprint.xml
@@ -0,0 +1,59 @@
+<!--
+ Copyright (C) 2020 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
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="32dp"
+ android:height="32dp"
+ android:tint="?attr/wallpaperTextColor"
+ android:alpha=".75"
+ android:viewportHeight="32.0"
+ android:viewportWidth="32.0">
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M23.7,5.9c-0.1,0.0 -0.2,0.0 -0.3,-0.1C21.0,4.5 18.6,3.9 16.0,3.9c-2.5,0.0
+ -4.6,0.6 -6.9,1.9C8.8,6.0 8.3,5.9 8.1,5.5C7.9,5.2 8.0,4.7 8.4,4.5c2.5,-1.4 4.9,-2.1 7.7,
+ -2.1c2.8,0.0 5.4,0.7 8.0,2.1c0.4,0.2 0.5,0.6 0.3,1.0C24.2,5.7 24.0,5.9 23.7,5.9z" />
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M5.3,13.2c-0.1,0.0 -0.3,0.0 -0.4,-0.1c-0.3,-0.2 -0.4,-0.7 -0.2,-1.0c1.3,
+ -1.9 2.9,-3.4 4.9,-4.5c4.1,-2.2 9.3,-2.2 13.4,0.0c1.9,1.1 3.6,2.5 4.9,4.4c0.2,0.3 0.1,0.8
+ -0.2,1.0c-0.3,0.2 -0.8,0.1 -1.0,-0.2c-1.2,-1.7 -2.6,-3.0 -4.3,-4.0c-3.7,-2.0 -8.3,-2.0
+ -12.0,0.0c-1.7,0.9 -3.2,2.3 -4.3,4.0C5.7,13.1 5.5,13.2 5.3,13.2z" />
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M13.3,29.6c-0.2,0.0 -0.4,-0.1 -0.5,-0.2c-1.1,-1.2 -1.7,-2.0 -2.6,
+ -3.6c-0.9,-1.7 -1.4,-3.7 -1.4,-5.9c0.0,-4.1 3.3,-7.4 7.4,-7.4c4.1,0.0 7.4,3.3 7.4,7.4c0.0,
+ 0.4 -0.3,0.7 -0.7,0.7s-0.7,-0.3 -0.7,-0.7c0.0,-3.3 -2.7,-5.9 -5.9,-5.9c-3.3,0.0 -5.9,
+ 2.7 -5.9,5.9c0.0,2.0 0.4,3.8 1.2,5.2c0.8,1.6 1.4,2.2 2.4,3.3c0.3,0.3 0.3,0.8 0.0,1.0
+ C13.7,29.5 13.5,29.6 13.3,29.6z" />
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M22.6,27.1c-1.6,0.0 -2.9,-0.4 -4.1,-1.2c-1.9,-1.4 -3.1,-3.6 -3.1,
+ -6.0c0.0,-0.4 0.3,-0.7 0.7,-0.7s0.7,0.3 0.7,0.7c0.0,1.9 0.9,3.7 2.5,4.8c0.9,0.6 1.9,
+ 1.0 3.2,1.0c0.3,0.0 0.8,0.0 1.3,-0.1c0.4,-0.1 0.8,0.2 0.8,0.6c0.1,0.4 -0.2,0.8 -0.6,
+ 0.8C23.4,27.1 22.8,27.1 22.6,27.1z" />
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M20.0,29.9c-0.1,0.0 -0.1,0.0 -0.2,0.0c-2.1,-0.6 -3.4,-1.4 -4.8,-2.9c-1.8,
+ -1.9 -2.8,-4.4 -2.8,-7.1c0.0,-2.2 1.8,-4.1 4.1,-4.1c2.2,0.0 4.1,1.8 4.1,4.1c0.0,1.4 1.2,
+ 2.6 2.6,2.6c1.4,0.0 2.6,-1.2 2.6,-2.6c0.0,-5.1 -4.2,-9.3 -9.3,-9.3c-3.6,0.0 -6.9,2.1 -8.4,
+ 5.4C7.3,17.1 7.0,18.4 7.0,19.8c0.0,1.1 0.1,2.7 0.9,4.9c0.1,0.4 -0.1,0.8 -0.4,0.9c-0.4,
+ 0.1 -0.8,-0.1 -0.9,-0.4c-0.6,-1.8 -0.9,-3.6 -0.9,-5.4c0.0,-1.6 0.3,-3.1 0.9,-4.4c1.7,
+ -3.8 5.6,-6.3 9.8,-6.3c5.9,0.0 10.7,4.8 10.7,10.7c0.0,2.2 -1.8,4.1 -4.1,4.1s-4.0,
+ -1.8 -4.0,-4.1c0.0,-1.4 -1.2,-2.6 -2.6,-2.6c-1.4,0.0 -2.6,1.2 -2.6,2.6c0.0,2.3 0.9,
+ 4.5 2.4,6.1c1.2,1.3 2.4,2.0 4.2,2.5c0.4,0.1 0.6,0.5 0.5,0.9C20.6,29.7 20.3,29.9 20.0,
+ 29.9z" />
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 84b9e3de28ef..2c08f5db0323 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -18,6 +18,7 @@
<!-- extends FrameLayout -->
<com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/expandableNotificationRow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 2071cfaf2bd9..b43496cb55dc 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1901,6 +1901,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
/**
+ * Whether to show the lock icon on lock screen and bouncer. This depends on the enrolled
+ * biometrics to the device.
+ */
+ public boolean shouldShowLockIcon() {
+ return isFaceAuthEnabledForUser(KeyguardUpdateMonitor.getCurrentUser())
+ && !isUdfpsEnrolled();
+ }
+
+ /**
* @return true if there's at least one udfps enrolled
*/
public boolean isUdfpsEnrolled() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index a15a5aa86aa2..65a6f29892f0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -135,7 +135,7 @@ class UdfpsController implements DozeReceiver {
@SuppressLint("ClickableViewAccessibility")
private final UdfpsView.OnTouchListener mOnTouchListener = (v, event) -> {
UdfpsView view = (UdfpsView) v;
- final boolean isFingerDown = view.isScrimShowing();
+ final boolean isFingerDown = view.isShowScrimAndDot();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index 5a61379f3ea3..663a0da81ad5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -27,6 +27,7 @@ import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -70,6 +71,7 @@ public class UdfpsView extends View implements DozeReceiver,
// mInsetsListener to restrict the touchable region and allow the touches outside of the sensor
// to propagate to the rest of the UI.
@NonNull private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener;
+ @NonNull private final Drawable mFingerprintDrawable;
// Used to obtain the sensor location.
@NonNull private FingerprintSensorPropertiesInternal mSensorProps;
@@ -79,7 +81,7 @@ public class UdfpsView extends View implements DozeReceiver,
private float mBurnInOffsetX;
private float mBurnInOffsetY;
- private boolean mIsScrimShowing;
+ private boolean mShowScrimAndDot;
private boolean mIsHbmSupported;
@Nullable private String mDebugMessage;
@@ -112,15 +114,16 @@ public class UdfpsView extends View implements DozeReceiver,
mSensorPaint = new Paint(0 /* flags */);
mSensorPaint.setAntiAlias(true);
mSensorPaint.setColor(Color.WHITE);
- mSensorPaint.setStyle(Paint.Style.STROKE);
- mSensorPaint.setStrokeWidth(SENSOR_OUTLINE_WIDTH);
mSensorPaint.setShadowLayer(SENSOR_SHADOW_RADIUS, 0, 0, Color.BLACK);
+ mSensorPaint.setStyle(Paint.Style.FILL);
mDebugTextPaint = new Paint();
mDebugTextPaint.setAntiAlias(true);
mDebugTextPaint.setColor(Color.BLUE);
mDebugTextPaint.setTextSize(DEBUG_TEXT_SIZE_PX);
+ mFingerprintDrawable = getResources().getDrawable(R.drawable.ic_fingerprint, null);
+
mTouchableRegion = new Rect();
// When the device is rotated, it's important that mTouchableRegion is updated before
// this listener is called. This listener is usually called shortly after onLayout.
@@ -130,7 +133,7 @@ public class UdfpsView extends View implements DozeReceiver,
internalInsetsInfo.touchableRegion.set(mTouchableRegion);
};
- mIsScrimShowing = false;
+ mShowScrimAndDot = false;
}
void setSensorProperties(@NonNull FingerprintSensorPropertiesInternal properties) {
@@ -181,6 +184,13 @@ public class UdfpsView extends View implements DozeReceiver,
default:
// Do nothing to stay in portrait mode.
}
+
+ int margin = (int) (mSensorRect.bottom - mSensorRect.top) / 5;
+ mFingerprintDrawable.setBounds(
+ (int) mSensorRect.left + margin,
+ (int) mSensorRect.top + margin,
+ (int) mSensorRect.right - margin,
+ (int) mSensorRect.bottom - margin);
}
@Override
@@ -198,6 +208,8 @@ public class UdfpsView extends View implements DozeReceiver,
// is finished, mTouchableRegion will be used by mInsetsListener to compute the touch
// insets.
mSensorRect.roundOut(mTouchableRegion);
+
+
}
@Override
@@ -218,7 +230,7 @@ public class UdfpsView extends View implements DozeReceiver,
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
- if (mIsScrimShowing && mIsHbmSupported) {
+ if (mShowScrimAndDot && mIsHbmSupported) {
// Only draw the scrim if HBM is supported.
canvas.drawRect(mScrimRect, mScrimPaint);
}
@@ -229,7 +241,15 @@ public class UdfpsView extends View implements DozeReceiver,
if (!TextUtils.isEmpty(mDebugMessage)) {
canvas.drawText(mDebugMessage, 0, 160, mDebugTextPaint);
}
- canvas.drawOval(mSensorRect, mSensorPaint);
+
+ if (mShowScrimAndDot) {
+ // draw dot (white circle)
+ canvas.drawOval(mSensorRect, mSensorPaint);
+ } else {
+ // draw fingerprint icon
+ mFingerprintDrawable.draw(canvas);
+ }
+
canvas.restore();
}
@@ -264,19 +284,17 @@ public class UdfpsView extends View implements DozeReceiver,
mScrimPaint.setAlpha(alpha);
}
- boolean isScrimShowing() {
- return mIsScrimShowing;
+ boolean isShowScrimAndDot() {
+ return mShowScrimAndDot;
}
void showScrimAndDot() {
- mIsScrimShowing = true;
- mSensorPaint.setStyle(Paint.Style.FILL);
+ mShowScrimAndDot = true;
invalidate();
}
void hideScrimAndDot() {
- mIsScrimShowing = false;
- mSensorPaint.setStyle(Paint.Style.STROKE);
+ mShowScrimAndDot = false;
invalidate();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt
index a7d17e128ff3..b4137fa80f19 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt
@@ -64,7 +64,8 @@ open class FaceAuthScreenBrightnessController(
private val globalSettings: GlobalSettings,
private val systemSettings: SystemSettings,
private val mainHandler: Handler,
- private val dumpManager: DumpManager
+ private val dumpManager: DumpManager,
+ private val enabled: Boolean
) : Dumpable {
private var userDefinedBrightness: Float = 1f
@@ -86,7 +87,7 @@ open class FaceAuthScreenBrightnessController(
return
}
// TODO enable only when receiving a low-light error
- overridingBrightness = running
+ overridingBrightness = if (enabled) running else false
}
}
private lateinit var whiteOverlay: View
@@ -188,6 +189,7 @@ open class FaceAuthScreenBrightnessController(
println("brightnessAnimationDuration: $brightnessAnimationDuration")
println("maxScreenBrightness: $maxScreenBrightness")
println("userDefinedBrightness: $userDefinedBrightness")
+ println("enabled: $enabled")
}
}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index d7b5eea84c36..626abfcc85fd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -142,8 +142,9 @@ public class KeyguardModule {
return Optional.empty();
}
+ // currently disabled (doesn't ramp up brightness or use scrim) see b/175918367
return Optional.of(new FaceAuthScreenBrightnessController(
notificationShadeWindowController, keyguardUpdateMonitor, resources,
- globalSetting, systemSettings, handler, dumpManager));
+ globalSetting, systemSettings, handler, dumpManager, false));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 7bac007ae478..20efa32d63c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -197,7 +197,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
viewState.hasItemsInStableShelf = lastViewState.inShelf;
viewState.hidden = !mAmbientState.isShadeExpanded()
|| mAmbientState.isQsCustomizerShowing();
- viewState.maxShelfEnd = maxShelfEnd;
} else {
viewState.hidden = true;
viewState.location = ExpandableViewState.LOCATION_GONE;
@@ -823,10 +822,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
return - (getIntrinsicHeight() - mStatusBarHeight) / 2;
}
- public int getNotificationMergeSize() {
- return getIntrinsicHeight();
- }
-
@Override
public boolean hasNoContentHeight() {
return true;
@@ -1018,7 +1013,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
private class ShelfState extends ExpandableViewState {
private float openedAmount;
private boolean hasItemsInStableShelf;
- private float maxShelfEnd;
@Override
public void applyToView(View view) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 4ca9c5db013c..db0713cf0b7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -289,8 +289,6 @@ public class KeyguardBouncer {
SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__HIDDEN);
mDismissCallbackRegistry.notifyDismissCancelled();
}
- mExpansion = EXPANSION_HIDDEN;
- dispatchExpansionChanged();
mIsScrimmed = false;
mFalsingCollector.onBouncerHidden();
mCallback.onBouncerVisiblityChanged(false /* shown */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index b9e8d74d9b85..6da5d1b90cd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -164,10 +164,10 @@ public class KeyguardClockPositionAlgorithm {
public void setup(int statusBarMinHeight, int maxShadeBottom, int notificationStackHeight,
float panelExpansion, int parentHeight, int keyguardStatusHeight, int clockPreferredY,
boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float emptyDragAmount,
- boolean bypassEnabled, int unlockedStackScrollerPadding, boolean udfpsEnrolled,
+ boolean bypassEnabled, int unlockedStackScrollerPadding, boolean showLockIcon,
float qsExpansion) {
- mMinTopMargin = statusBarMinHeight + (udfpsEnrolled ? mContainerTopPaddingWithoutLockIcon :
- mContainerTopPaddingWithLockIcon);
+ mMinTopMargin = statusBarMinHeight + (showLockIcon
+ ? mContainerTopPaddingWithLockIcon : mContainerTopPaddingWithoutLockIcon);
mMaxShadeBottom = maxShadeBottom;
mNotificationStackHeight = notificationStackHeight;
mPanelExpansion = panelExpansion;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
index 6bdc303b4786..ab0366e07b9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
@@ -501,7 +501,7 @@ public class LockscreenLockIconController {
if (mBlockUpdates && canBlockUpdates()) {
shouldUpdate = false;
}
- if (shouldUpdate && mLockIcon != null) {
+ if (shouldUpdate && mLockIcon != null && mLockIcon.getVisibility() != GONE) {
mLockIcon.update(state,
mStatusBarStateController.isDozing(), mKeyguardJustShown);
}
@@ -549,16 +549,14 @@ public class LockscreenLockIconController {
return false;
}
- if (mKeyguardUpdateMonitor.isUdfpsEnrolled()) {
- boolean changed = mLockIcon.getVisibility() == GONE;
+ if (!mKeyguardUpdateMonitor.shouldShowLockIcon()) {
+ boolean changed = mLockIcon.getVisibility() != GONE;
mLockIcon.setVisibility(GONE);
return changed;
}
boolean onAodOrDocked = mStatusBarStateController.isDozing() || mDocked;
- boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance
- || (mKeyguardSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser())
- == KeyguardSecurityModel.SecurityMode.None);
+ boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance;
boolean fingerprintOrBypass = mFingerprintUnlock
|| mKeyguardBypassController.getBypassEnabled();
if (fingerprintOrBypass && !mBouncerShowingScrimmed) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 8d3b12868dd7..ba08e76e6f66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -912,7 +912,8 @@ public class NotificationPanelViewController extends PanelViewController {
clockPreferredY, hasCustomClock(),
hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount,
bypassEnabled, getUnlockedStackScrollerPadding(),
- mUpdateMonitor.isUdfpsEnrolled(), getQsExpansionFraction());
+ mUpdateMonitor.shouldShowLockIcon(),
+ getQsExpansionFraction());
mClockPositionAlgorithm.run(mClockPositionResult);
mKeyguardStatusViewController.updatePosition(
mClockPositionResult.clockX, mClockPositionResult.clockY,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
index 272c494b1af4..22fd93ed10ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
@@ -25,11 +25,15 @@ import android.os.UserHandle;
import androidx.annotation.NonNull;
+import com.android.systemui.Dumpable;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Date;
import javax.inject.Inject;
@@ -38,7 +42,7 @@ import javax.inject.Inject;
*/
@SysUISingleton
public class NextAlarmControllerImpl extends BroadcastReceiver
- implements NextAlarmController {
+ implements NextAlarmController, Dumpable {
private final ArrayList<NextAlarmChangeCallback> mChangeCallbacks = new ArrayList<>();
@@ -48,18 +52,34 @@ public class NextAlarmControllerImpl extends BroadcastReceiver
/**
*/
@Inject
- public NextAlarmControllerImpl(Context context) {
- mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ public NextAlarmControllerImpl(
+ AlarmManager alarmManager,
+ BroadcastDispatcher broadcastDispatcher,
+ DumpManager dumpManager) {
+ dumpManager.registerDumpable("NextAlarmController", this);
+ mAlarmManager = alarmManager;
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
- context.registerReceiverAsUser(this, UserHandle.ALL, filter, null, null);
+ broadcastDispatcher.registerReceiver(this, filter, null, UserHandle.ALL);
updateNextAlarm();
}
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("NextAlarmController state:");
- pw.print(" mNextAlarm="); pw.println(mNextAlarm);
+ pw.print("mNextAlarm=");
+ if (mNextAlarm != null) {
+ pw.println(new Date(mNextAlarm.getTriggerTime()));
+ pw.print(" PendingIntentPkg=");
+ pw.println(mNextAlarm.getShowIntent().getCreatorPackage());
+ } else {
+ pw.println("null");
+ }
+
+ pw.println("Registered Callbacks:");
+ for (NextAlarmChangeCallback callback : mChangeCallbacks) {
+ pw.print(" "); pw.println(callback.toString());
+ }
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 648c31924a20..7f8372e4bc44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -175,7 +175,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void fingerDown() throws RemoteException {
// Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isScrimShowing()).thenReturn(false);
+ when(mUdfpsView.isShowScrimAndDot()).thenReturn(false);
when(mUdfpsView.isValidTouch(anyFloat(), anyFloat(), anyFloat())).thenReturn(true);
// GIVEN that the overlay is showing
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt
index 73a7ca93507d..cb05a6b21b3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt
@@ -83,7 +83,7 @@ class FaceAuthScreenBrightnessControllerTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
faceAuthScreenBrightnessController = object : FaceAuthScreenBrightnessController(
notificationShadeWindowController, keyguardUpdateMonitor, resources, globalSettings,
- systemSettings, mainHandler, dumpManager) {
+ systemSettings, mainHandler, dumpManager, true) {
override fun createAnimator(start: Float, end: Float) = animator
}
`when`(systemSettings.getFloat(eq(SCREEN_BRIGHTNESS_FLOAT))).thenReturn(INITIAL_BRIGHTNESS)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
index 1ac793730f02..95a35050c09e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
@@ -91,6 +91,7 @@ public class LockscreenIconControllerTest extends SysuiTestCase {
public void setUp() {
MockitoAnnotations.initMocks(this);
+ when(mKeyguardUpdateMonitor.shouldShowLockIcon()).thenReturn(true);
when(mLockIcon.getContext()).thenReturn(mContext);
mLockIconController = new LockscreenLockIconController(
mLockscreenGestureLogger, mKeyguardUpdateMonitor, mLockPatternUtils,
@@ -145,12 +146,10 @@ public class LockscreenIconControllerTest extends SysuiTestCase {
}
@Test
- public void testVisibility_noBouncer() {
- // no security (ie: no lock screen OR swipe to unlock)
- when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
- KeyguardSecurityModel.SecurityMode.None);
+ public void testVisibility_doNotShowLockIcon() {
+ when(mKeyguardUpdateMonitor.shouldShowLockIcon()).thenReturn(false);
mOnAttachStateChangeListener.onViewAttachedToWindow(mLockIcon);
- verify(mLockIcon).updateIconVisibility(false);
+ verify(mLockIcon).setVisibility(View.GONE);
}
}
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index 52a82dd2a156..5ee30fb728ad 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -51,7 +51,6 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.nio.charset.StandardCharsets;
public class WallpaperBackupAgent extends BackupAgent {
private static final String TAG = "WallpaperBackup";
@@ -323,8 +322,7 @@ public class WallpaperBackupAgent extends BackupAgent {
private Rect parseCropHint(File wallpaperInfo, String sectionTag) {
Rect cropHint = new Rect();
try (FileInputStream stream = new FileInputStream(wallpaperInfo)) {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, StandardCharsets.UTF_8.name());
+ XmlPullParser parser = Xml.resolvePullParser(stream);
int type;
do {
@@ -351,8 +349,7 @@ public class WallpaperBackupAgent extends BackupAgent {
private ComponentName parseWallpaperComponent(File wallpaperInfo, String sectionTag) {
ComponentName name = null;
try (FileInputStream stream = new FileInputStream(wallpaperInfo)) {
- final XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, StandardCharsets.UTF_8.name());
+ final XmlPullParser parser = Xml.resolvePullParser(stream);
int type;
do {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 9b9919981935..2e1fbb77cd01 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -144,6 +144,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.INetworkActivityListener;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
@@ -1380,8 +1381,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
return;
}
final String action = blocked ? "BLOCKED" : "UNBLOCKED";
+ final NetworkRequest satisfiedRequest = nri.getSatisfiedRequest();
+ final int requestId = satisfiedRequest != null
+ ? satisfiedRequest.requestId : nri.mRequests.get(0).requestId;
mNetworkInfoBlockingLogs.log(String.format(
- "%s %d(%d) on netId %d", action, nri.mUid, nri.request.requestId, net.getNetId()));
+ "%s %d(%d) on netId %d", action, nri.mUid, requestId, net.getNetId()));
}
/**
@@ -1709,16 +1713,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
return newNc;
}
- Binder.withCleanCallingIdentity(
- () -> {
- if (!mLocationPermissionChecker.checkLocationPermission(
- callerPkgName, null /* featureId */, callerUid, null /* message */)) {
- // Caller does not have the requisite location permissions. Reset the
- // owner's UID in the NetworkCapabilities.
- newNc.setOwnerUid(INVALID_UID);
- }
- }
- );
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!mLocationPermissionChecker.checkLocationPermission(
+ callerPkgName, null /* featureId */, callerUid, null /* message */)) {
+ // Caller does not have the requisite location permissions. Reset the
+ // owner's UID in the NetworkCapabilities.
+ newNc.setOwnerUid(INVALID_UID);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
return newNc;
}
@@ -2336,6 +2341,31 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
/**
+ * Start listening for default data network activity state changes.
+ */
+ @Override
+ public void registerNetworkActivityListener(@NonNull INetworkActivityListener l) {
+ // TODO: Replace network activity listener registry in ConnectivityManager from NMS to here
+ }
+
+ /**
+ * Stop listening for default data network activity state changes.
+ */
+ @Override
+ public void unregisterNetworkActivityListener(@NonNull INetworkActivityListener l) {
+ // TODO: Replace network activity listener registry in ConnectivityManager from NMS to here
+ }
+
+ /**
+ * Check whether the default network radio is currently active.
+ */
+ @Override
+ public boolean isDefaultNetworkActive() {
+ // TODO: Replace isNetworkActive() in NMS.
+ return false;
+ }
+
+ /**
* Setup data activity tracking for the given network.
*
* Every {@code setupDataActivityTracking} should be paired with a
@@ -2705,7 +2735,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
* Return an array of all current NetworkRequest sorted by request id.
*/
@VisibleForTesting
- protected NetworkRequestInfo[] requestsSortedById() {
+ NetworkRequestInfo[] requestsSortedById() {
NetworkRequestInfo[] requests = new NetworkRequestInfo[0];
requests = mNetworkRequests.values().toArray(requests);
// Sort the array based off the NRI containing the min requestId in its requests.
@@ -3555,30 +3585,58 @@ public class ConnectivityService extends IConnectivityManager.Stub
return false;
}
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- if (reason == UnneededFor.LINGER && nri.request.isBackgroundRequest()) {
+ if (reason == UnneededFor.LINGER
+ && !nri.isMultilayerRequest()
+ && nri.mRequests.get(0).isBackgroundRequest()) {
// Background requests don't affect lingering.
continue;
}
- // If this Network is already the highest scoring Network for a request, or if
- // there is hope for it to become one if it validated, then it is needed.
- if (nri.request.isRequest() && nai.satisfies(nri.request) &&
- (nai.isSatisfyingRequest(nri.request.requestId) ||
- // Note that this catches two important cases:
- // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
- // is currently satisfying the request. This is desirable when
- // cellular ends up validating but WiFi does not.
- // 2. Unvalidated WiFi will not be reaped when validated cellular
- // is currently satisfying the request. This is desirable when
- // WiFi ends up validating and out scoring cellular.
- nri.mSatisfier.getCurrentScore()
- < nai.getCurrentScoreAsValidated())) {
+ if (isNetworkPotentialSatisfier(nai, nri)) {
return false;
}
}
return true;
}
+ private boolean isNetworkPotentialSatisfier(
+ @NonNull final NetworkAgentInfo candidate, @NonNull final NetworkRequestInfo nri) {
+ // listen requests won't keep up a network satisfying it. If this is not a multilayer
+ // request, we can return immediately. For multilayer requests, we have to check to see if
+ // any of the multilayer requests may have a potential satisfier.
+ if (!nri.isMultilayerRequest() && nri.mRequests.get(0).isListen()) {
+ return false;
+ }
+ for (final NetworkRequest req : nri.mRequests) {
+ // As non-multilayer listen requests have already returned, the below would only happen
+ // for a multilayer request therefore continue to the next request if available.
+ if (req.isListen()) {
+ continue;
+ }
+ // If this Network is already the highest scoring Network for a request, or if
+ // there is hope for it to become one if it validated, then it is needed.
+ if (candidate.satisfies(req)) {
+ // As soon as a network is found that satisfies a request, return. Specifically for
+ // multilayer requests, returning as soon as a NetworkAgentInfo satisfies a request
+ // is important so as to not evaluate lower priority requests further in
+ // nri.mRequests.
+ final boolean isNetworkNeeded = candidate.isSatisfyingRequest(req.requestId)
+ // Note that this catches two important cases:
+ // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
+ // is currently satisfying the request. This is desirable when
+ // cellular ends up validating but WiFi does not.
+ // 2. Unvalidated WiFi will not be reaped when validated cellular
+ // is currently satisfying the request. This is desirable when
+ // WiFi ends up validating and out scoring cellular.
+ || nri.mSatisfier.getCurrentScore()
+ < candidate.getCurrentScoreAsValidated();
+ return isNetworkNeeded;
+ }
+ }
+
+ return false;
+ }
+
private NetworkRequestInfo getNriForAppRequest(
NetworkRequest request, int callingUid, String requestedOperation) {
final NetworkRequestInfo nri = mNetworkRequests.get(request);
@@ -3875,8 +3933,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
new CaptivePortal(new CaptivePortalImpl(network).asBinder()));
appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
- Binder.withCleanCallingIdentity(() ->
- mContext.startActivityAsUser(appIntent, UserHandle.CURRENT));
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mContext.startActivityAsUser(appIntent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
private class CaptivePortalImpl extends ICaptivePortal.Stub {
@@ -5464,6 +5526,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
this(r, null);
}
+ boolean isMultilayerRequest() {
+ return mRequests.size() > 1;
+ }
+
private List<NetworkRequest> initializeRequests(NetworkRequest r) {
final ArrayList<NetworkRequest> tempRequests = new ArrayList<>();
tempRequests.add(new NetworkRequest(r));
@@ -5505,7 +5571,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
public void binderDied() {
log("ConnectivityService NetworkRequestInfo binderDied(" +
mRequests + ", " + mBinder + ")");
- releaseNetworkRequest(mRequests);
+ releaseNetworkRequests(mRequests);
}
@Override
@@ -5538,13 +5604,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
mAppOpsManager.checkPackage(callerUid, callerPackageName);
}
- private ArrayList<Integer> getSignalStrengthThresholds(NetworkAgentInfo nai) {
+ private ArrayList<Integer> getSignalStrengthThresholds(@NonNull final NetworkAgentInfo nai) {
final SortedSet<Integer> thresholds = new TreeSet<>();
synchronized (nai) {
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- if (nri.request.networkCapabilities.hasSignalStrength() &&
- nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
- thresholds.add(nri.request.networkCapabilities.getSignalStrength());
+ for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+ for (final NetworkRequest req : nri.mRequests) {
+ if (req.networkCapabilities.hasSignalStrength()
+ && nai.satisfiesImmutableCapabilitiesOf(req)) {
+ thresholds.add(req.networkCapabilities.getSignalStrength());
+ }
}
}
}
@@ -5831,7 +5899,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
return mNextNetworkProviderId.getAndIncrement();
}
- private void releaseNetworkRequest(List<NetworkRequest> networkRequests) {
+ private void releaseNetworkRequests(List<NetworkRequest> networkRequests) {
for (int i = 0; i < networkRequests.size(); i++) {
releaseNetworkRequest(networkRequests.get(i));
}
@@ -7763,10 +7831,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
final int userId = UserHandle.getCallingUserId();
- Binder.withCleanCallingIdentity(() -> {
+ final long token = Binder.clearCallingIdentity();
+ try {
final IpMemoryStore ipMemoryStore = IpMemoryStore.getMemoryStore(mContext);
ipMemoryStore.factoryReset();
- });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
// Turn airplane mode off
setAirplaneMode(false);
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index 655d8abf3e84..e8687e57a07b 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -107,23 +107,23 @@ class TestNetworkService extends ITestNetworkManager.Stub {
String ifacePrefix = isTun ? TEST_TUN_PREFIX : TEST_TAP_PREFIX;
String iface = ifacePrefix + sTestTunIndex.getAndIncrement();
- return Binder.withCleanCallingIdentity(
- () -> {
- try {
- ParcelFileDescriptor tunIntf =
- ParcelFileDescriptor.adoptFd(jniCreateTunTap(isTun, iface));
- for (LinkAddress addr : linkAddrs) {
- mNetd.interfaceAddAddress(
- iface,
- addr.getAddress().getHostAddress(),
- addr.getPrefixLength());
- }
-
- return new TestNetworkInterface(tunIntf, iface);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- });
+ final long token = Binder.clearCallingIdentity();
+ try {
+ ParcelFileDescriptor tunIntf =
+ ParcelFileDescriptor.adoptFd(jniCreateTunTap(isTun, iface));
+ for (LinkAddress addr : linkAddrs) {
+ mNetd.interfaceAddAddress(
+ iface,
+ addr.getAddress().getHostAddress(),
+ addr.getPrefixLength());
+ }
+
+ return new TestNetworkInterface(tunIntf, iface);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/**
@@ -317,7 +317,12 @@ class TestNetworkService extends ITestNetworkManager.Stub {
try {
// This requires NETWORK_STACK privileges.
- Binder.withCleanCallingIdentity(() -> mNMS.setInterfaceUp(iface));
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mNMS.setInterfaceUp(iface);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
// Synchronize all accesses to mTestNetworkTracker to prevent the case where:
// 1. TestNetworkAgent successfully binds to death of binder
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 165b6a1b08f1..74e38510770b 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -25,24 +25,44 @@ import android.net.NetworkProvider;
import android.net.NetworkRequest;
import android.net.vcn.IVcnManagementService;
import android.net.vcn.VcnConfig;
+import android.os.Binder;
+import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.ParcelUuid;
+import android.os.PersistableBundle;
+import android.os.Process;
+import android.os.ServiceSpecificException;
+import android.os.UserHandle;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
+import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
/**
* VcnManagementService manages Virtual Carrier Network profiles and lifecycles.
*
* <pre>The internal structure of the VCN Management subsystem is as follows:
*
- * +------------------------+ 1:1 +--------------------------------+
- * | VcnManagementService | ------------ Creates -------------> | TelephonySubscriptionManager |
- * | | | |
- * | Manages configs and | | Tracks subscriptions, carrier |
- * | VcnInstance lifecycles | <--- Notifies of subscription & --- | privilege changes, caches maps |
- * +------------------------+ carrier privilege changes +--------------------------------+
+ * +-------------------------+ 1:1 +--------------------------------+
+ * | VcnManagementService | ------------ Creates ------------> | TelephonySubscriptionManager |
+ * | | | |
+ * | Manages configs and | | Tracks subscriptions, carrier |
+ * | Vcn instance lifecycles | <--- Notifies of subscription & -- | privilege changes, caches maps |
+ * +-------------------------+ carrier privilege changes +--------------------------------+
* | 1:N ^
* | |
* | +-------------------------------+
@@ -54,19 +74,19 @@ import com.android.internal.annotations.VisibleForTesting.Visibility;
* | mode state changes
* v |
* +-----------------------------------------------------------------------+
- * | VcnInstance |
+ * | Vcn |
* | |
- * | Manages tunnel lifecycles based on fulfillable NetworkRequest(s) |
- * | and overall safe-mode |
+ * | Manages GatewayConnection lifecycles based on fulfillable |
+ * | NetworkRequest(s) and overall safe-mode |
* +-----------------------------------------------------------------------+
* | 1:N ^
* Creates to fulfill |
- * NetworkRequest(s), tears Notifies of VcnTunnel
+ * NetworkRequest(s), tears Notifies of VcnGatewayConnection
* down when no longer needed teardown (e.g. Network reaped)
* | and safe-mode timer changes
* v |
* +-----------------------------------------------------------------------+
- * | VcnTunnel |
+ * | VcnGatewayConnection |
* | |
* | Manages a single (IKEv2) tunnel session and NetworkAgent, |
* | handles mobility events, (IPsec) Tunnel setup and safe-mode timers |
@@ -92,20 +112,72 @@ public class VcnManagementService extends IVcnManagementService.Stub {
public static final boolean VDBG = false; // STOPSHIP: if true
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final String VCN_CONFIG_FILE = "/data/system/vcn/configs.xml";
+
/* Binder context for this service */
@NonNull private final Context mContext;
@NonNull private final Dependencies mDeps;
@NonNull private final Looper mLooper;
+ @NonNull private final Handler mHandler;
@NonNull private final VcnNetworkProvider mNetworkProvider;
+ @GuardedBy("mLock")
+ @NonNull
+ private final Map<ParcelUuid, VcnConfig> mConfigs = new ArrayMap<>();
+
+ @NonNull private final Object mLock = new Object();
+
+ @NonNull private final PersistableBundleUtils.LockingReadWriteHelper mConfigDiskRwHelper;
+
@VisibleForTesting(visibility = Visibility.PRIVATE)
VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) {
mContext = requireNonNull(context, "Missing context");
mDeps = requireNonNull(deps, "Missing dependencies");
mLooper = mDeps.getLooper();
+ mHandler = new Handler(mLooper);
mNetworkProvider = new VcnNetworkProvider(mContext, mLooper);
+
+ mConfigDiskRwHelper = mDeps.newPersistableBundleLockingReadWriteHelper(VCN_CONFIG_FILE);
+
+ // Run on handler to ensure I/O does not block system server startup
+ mHandler.post(() -> {
+ PersistableBundle configBundle = null;
+ try {
+ configBundle = mConfigDiskRwHelper.readFromDisk();
+ } catch (IOException e1) {
+ Slog.e(TAG, "Failed to read configs from disk; retrying", e1);
+
+ // Retry immediately. The IOException may have been transient.
+ try {
+ configBundle = mConfigDiskRwHelper.readFromDisk();
+ } catch (IOException e2) {
+ Slog.wtf(TAG, "Failed to read configs from disk", e2);
+ return;
+ }
+ }
+
+ if (configBundle != null) {
+ final Map<ParcelUuid, VcnConfig> configs =
+ PersistableBundleUtils.toMap(
+ configBundle,
+ PersistableBundleUtils::toParcelUuid,
+ VcnConfig::new);
+
+ synchronized (mLock) {
+ for (Entry<ParcelUuid, VcnConfig> entry : configs.entrySet()) {
+ // Ensure no new configs are overwritten; a carrier app may have added a new
+ // config.
+ if (!mConfigs.containsKey(entry.getKey())) {
+ mConfigs.put(entry.getKey(), entry.getValue());
+ }
+ }
+ // TODO: Trigger re-evaluation of active VCNs; start/stop VCNs as needed.
+ }
+ }
+ });
}
// Package-visibility for SystemServer to create instances.
@@ -130,16 +202,81 @@ public class VcnManagementService extends IVcnManagementService.Stub {
}
return mHandlerThread.getLooper();
}
+
+ /**
+ * Retrieves the caller's UID
+ *
+ * <p>This call MUST be made before calling {@link Binder#clearCallingIdentity}, otherwise
+ * this will not work properly.
+ *
+ * @return
+ */
+ public int getBinderCallingUid() {
+ return Binder.getCallingUid();
+ }
+
+ /**
+ * Creates and returns a new {@link PersistableBundle.LockingReadWriteHelper}
+ *
+ * @param path the file path to read/write from/to.
+ * @return the {@link PersistableBundleUtils.LockingReadWriteHelper} instance
+ */
+ public PersistableBundleUtils.LockingReadWriteHelper
+ newPersistableBundleLockingReadWriteHelper(@NonNull String path) {
+ return new PersistableBundleUtils.LockingReadWriteHelper(path);
+ }
}
/** Notifies the VcnManagementService that external dependencies can be set up. */
public void systemReady() {
- // TODO: Retrieve existing profiles from KeyStore
-
mContext.getSystemService(ConnectivityManager.class)
.registerNetworkProvider(mNetworkProvider);
}
+ private void enforcePrimaryUser() {
+ final int uid = mDeps.getBinderCallingUid();
+ if (uid == Process.SYSTEM_UID) {
+ throw new IllegalStateException(
+ "Calling identity was System Server. Was Binder calling identity cleared?");
+ }
+
+ if (!UserHandle.getUserHandleForUid(uid).isSystem()) {
+ throw new SecurityException(
+ "VcnManagementService can only be used by callers running as the primary user");
+ }
+ }
+
+ private void enforceCallingUserAndCarrierPrivilege(ParcelUuid subscriptionGroup) {
+ // Only apps running in the primary (system) user are allowed to configure the VCN. This is
+ // in line with Telephony's behavior with regards to binding to a Carrier App provided
+ // CarrierConfigService.
+ enforcePrimaryUser();
+
+ // TODO (b/172619301): Check based on events propagated from CarrierPrivilegesTracker
+ final SubscriptionManager subMgr = mContext.getSystemService(SubscriptionManager.class);
+ final List<SubscriptionInfo> subscriptionInfos = new ArrayList<>();
+ Binder.withCleanCallingIdentity(
+ () -> {
+ subscriptionInfos.addAll(subMgr.getSubscriptionsInGroup(subscriptionGroup));
+ });
+
+ final TelephonyManager telMgr = mContext.getSystemService(TelephonyManager.class);
+ for (SubscriptionInfo info : subscriptionInfos) {
+ // Check subscription is active first; much cheaper/faster check, and an app (currently)
+ // cannot be carrier privileged for inactive subscriptions.
+ if (subMgr.isValidSlotIndex(info.getSimSlotIndex())
+ && telMgr.hasCarrierPrivileges(info.getSubscriptionId())) {
+ // TODO (b/173717728): Allow configuration for inactive, but manageable
+ // subscriptions.
+ // TODO (b/173718661): Check for whole subscription groups at a time.
+ return;
+ }
+ }
+
+ throw new SecurityException(
+ "Carrier privilege required for subscription group to set VCN Config");
+ }
+
/**
* Sets a VCN config for a given subscription group.
*
@@ -150,7 +287,17 @@ public class VcnManagementService extends IVcnManagementService.Stub {
requireNonNull(subscriptionGroup, "subscriptionGroup was null");
requireNonNull(config, "config was null");
- // TODO: Store VCN configuration, trigger startup as necessary
+ enforceCallingUserAndCarrierPrivilege(subscriptionGroup);
+
+ synchronized (mLock) {
+ mConfigs.put(subscriptionGroup, config);
+
+ // Must be done synchronously to ensure that writes do not happen out-of-order.
+ writeConfigsToDiskLocked();
+ }
+
+ // TODO: Clear Binder calling identity
+ // TODO: Trigger startup as necessary
}
/**
@@ -162,7 +309,40 @@ public class VcnManagementService extends IVcnManagementService.Stub {
public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) {
requireNonNull(subscriptionGroup, "subscriptionGroup was null");
- // TODO: Clear VCN configuration, trigger teardown as necessary
+ enforceCallingUserAndCarrierPrivilege(subscriptionGroup);
+
+ synchronized (mLock) {
+ mConfigs.remove(subscriptionGroup);
+
+ // Must be done synchronously to ensure that writes do not happen out-of-order.
+ writeConfigsToDiskLocked();
+ }
+
+ // TODO: Clear Binder calling identity
+ // TODO: Trigger teardown as necessary
+ }
+
+ @GuardedBy("mLock")
+ private void writeConfigsToDiskLocked() {
+ try {
+ PersistableBundle bundle =
+ PersistableBundleUtils.fromMap(
+ mConfigs,
+ PersistableBundleUtils::fromParcelUuid,
+ VcnConfig::toPersistableBundle);
+ mConfigDiskRwHelper.writeToDisk(bundle);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to save configs to disk", e);
+ throw new ServiceSpecificException(0, "Failed to save configs");
+ }
+ }
+
+ /** Get current configuration list for testing purposes */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ Map<ParcelUuid, VcnConfig> getConfigs() {
+ synchronized (mLock) {
+ return Collections.unmodifiableMap(mConfigs);
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 12fe3eda78a4..fffa814566fb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2928,6 +2928,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
final PlatformCompat platformCompat = (PlatformCompat)
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
String toggleValue = getNextArgRequired();
+ boolean killPackage = !"--no-kill".equals(getNextOption());
boolean toggleAll = false;
int targetSdkVersion = -1;
long changeId = -1;
@@ -2979,7 +2980,11 @@ final class ActivityManagerShellCommand extends ShellCommand {
CompatibilityChangeConfig overrides =
new CompatibilityChangeConfig(
new Compatibility.ChangeConfig(enabled, disabled));
- platformCompat.setOverrides(overrides, packageName);
+ if (killPackage) {
+ platformCompat.setOverrides(overrides, packageName);
+ } else {
+ platformCompat.setOverridesForTest(overrides, packageName);
+ }
pw.println("Enabled change " + changeId + " for " + packageName + ".");
}
return 0;
@@ -2998,13 +3003,21 @@ final class ActivityManagerShellCommand extends ShellCommand {
CompatibilityChangeConfig overrides =
new CompatibilityChangeConfig(
new Compatibility.ChangeConfig(enabled, disabled));
- platformCompat.setOverrides(overrides, packageName);
+ if (killPackage) {
+ platformCompat.setOverrides(overrides, packageName);
+ } else {
+ platformCompat.setOverridesForTest(overrides, packageName);
+ }
pw.println("Disabled change " + changeId + " for " + packageName + ".");
}
return 0;
case "reset":
if (toggleAll) {
- platformCompat.clearOverrides(packageName);
+ if (killPackage) {
+ platformCompat.clearOverrides(packageName);
+ } else {
+ platformCompat.clearOverridesForTest(packageName);
+ }
pw.println("Reset all changes for " + packageName + " to default value.");
return 0;
}
@@ -3410,15 +3423,18 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" write");
pw.println(" Write all pending state to storage.");
pw.println(" compat [COMMAND] [...]: sub-commands for toggling app-compat changes.");
- pw.println(" enable|disable|reset <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>");
+ pw.println(" enable|disable [--no-kill] <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>");
+ pw.println(" Toggles a change either by id or by name for <PACKAGE_NAME>.");
+ pw.println(" It kills <PACKAGE_NAME> (to allow the toggle to take effect) unless --no-kill is provided.");
+ pw.println(" reset <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>");
pw.println(" Toggles a change either by id or by name for <PACKAGE_NAME>.");
pw.println(" It kills <PACKAGE_NAME> (to allow the toggle to take effect).");
- pw.println(" enable-all|disable-all <targetSdkVersion> <PACKAGE_NAME");
+ pw.println(" enable-all|disable-all <targetSdkVersion> <PACKAGE_NAME>");
pw.println(" Toggles all changes that are gated by <targetSdkVersion>.");
- pw.println(" reset-all <PACKAGE_NAME>");
+ pw.println(" reset-all [--no-kill] <PACKAGE_NAME>");
pw.println(" Removes all existing overrides for all changes for ");
pw.println(" <PACKAGE_NAME> (back to default behaviour).");
- pw.println(" It kills <PACKAGE_NAME> (to allow the toggle to take effect).");
+ pw.println(" It kills <PACKAGE_NAME> (to allow the toggle to take effect) unless --no-kill is provided.");
pw.println(" memory-factor [command] [...]: sub-commands for overriding memory pressure factor");
pw.println(" set <NORMAL|MODERATE|LOW|CRITICAL>");
pw.println(" Overrides memory pressure factor. May also supply a raw int level");
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 083e970cc434..2f7c5234d982 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -254,9 +254,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mHandler = new Handler(mHandlerThread.getLooper());
// TODO(b/173077356): Replace directly calling the HAL with PowerStatsService queries
- // Make sure to init Hal Wrapper before creating BatteryStatsImpl.
- mPowerStatsHALWrapper = new PowerStatsHALWrapper.PowerStatsHALWrapperImpl();
- mPowerStatsHALWrapper.initialize();
+ mPowerStatsHALWrapper = PowerStatsHALWrapper.getPowerStatsHalImpl();
final MeasuredEnergyArray initialEnergies = getEnergyConsumptionData();
final boolean[] supportedBuckets = getSupportedEnergyBuckets(initialEnergies);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 4444de0c00c4..ada67b1d59af 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -8906,14 +8906,8 @@ public class AudioService extends IAudioService.Stub
mPlaybackMonitor.playerAttributes(piid, attr, Binder.getCallingUid());
}
- /**
- * Update player event
- * @param piid Player id to update
- * @param event The new player event
- * @param deviceId The new player device id
- */
- public void playerEvent(int piid, int event, int deviceId) {
- mPlaybackMonitor.playerEvent(piid, event, deviceId, Binder.getCallingUid());
+ public void playerEvent(int piid, int event) {
+ mPlaybackMonitor.playerEvent(piid, event, Binder.getCallingUid());
}
public void playerHasOpPlayAudio(int piid, boolean hasOpPlayAudio) {
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 36c67cdbac4b..a5778836aa6e 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -233,25 +233,15 @@ public final class PlaybackActivityMonitor
}
}
- /**
- * Update player event
- * @param piid Player id to update
- * @param event The new player event
- * @param deviceId The new player device id
- * @param binderUid Calling binder uid
- */
- public void playerEvent(int piid, int event, int deviceId, int binderUid) {
- if (DEBUG) {
- Log.v(TAG, String.format("playerEvent(piid=%d, deviceId=%d, event=%d)",
- piid, deviceId, event));
- }
+ public void playerEvent(int piid, int event, int binderUid) {
+ if (DEBUG) { Log.v(TAG, String.format("playerEvent(piid=%d, event=%d)", piid, event)); }
final boolean change;
synchronized(mPlayerLock) {
final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
if (apc == null) {
return;
}
- sEventLogger.log(new PlayerEvent(piid, event, deviceId));
+ sEventLogger.log(new PlayerEvent(piid, event));
if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
for (Integer uidInteger: mBannedUids) {
if (checkBanPlayer(apc, uidInteger.intValue())) {
@@ -269,7 +259,7 @@ public final class PlaybackActivityMonitor
if (checkConfigurationCaller(piid, apc, binderUid)) {
//TODO add generation counter to only update to the latest state
checkVolumeForPrivilegedAlarm(apc, event);
- change = apc.handleStateEvent(event, deviceId);
+ change = apc.handleStateEvent(event);
} else {
Log.e(TAG, "Error handling event " + event);
change = false;
@@ -299,8 +289,7 @@ public final class PlaybackActivityMonitor
mPlayers.remove(new Integer(piid));
mDuckingManager.removeReleased(apc);
checkVolumeForPrivilegedAlarm(apc, AudioPlaybackConfiguration.PLAYER_STATE_RELEASED);
- change = apc.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_RELEASED,
- AudioPlaybackConfiguration.PLAYER_DEVICEID_INVALID);
+ change = apc.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_RELEASED);
}
}
if (change) {
@@ -879,19 +868,16 @@ public final class PlaybackActivityMonitor
// only keeping the player interface ID as it uniquely identifies the player in the event
final int mPlayerIId;
final int mState;
- final int mDeviceId;
- PlayerEvent(int piid, int state, int deviceId) {
+ PlayerEvent(int piid, int state) {
mPlayerIId = piid;
mState = state;
- mDeviceId = deviceId;
}
@Override
public String eventToString() {
return new StringBuilder("player piid:").append(mPlayerIId).append(" state:")
- .append(AudioPlaybackConfiguration.toLogFriendlyPlayerState(mState))
- .append(" DeviceId:").append(mDeviceId).toString();
+ .append(AudioPlaybackConfiguration.toLogFriendlyPlayerState(mState)).toString();
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java
index 78e875b864f4..a26662dfd970 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java
@@ -133,7 +133,7 @@ public class FaceUserState extends BiometricUserState<Face> {
if (tagName.equals(TAG_FACE)) {
String name = parser.getAttributeValue(null, ATTR_NAME);
int faceId = parser.getAttributeInt(null, ATTR_FACE_ID);
- int deviceId = parser.getAttributeInt(null, ATTR_DEVICE_ID);
+ long deviceId = parser.getAttributeLong(null, ATTR_DEVICE_ID);
mBiometrics.add(new Face(name, faceId, deviceId));
}
}
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index ff3193182501..a8aa9aada607 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -143,6 +143,9 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @return {@code true} if the change should be enabled for the package.
*/
boolean isEnabled(ApplicationInfo app) {
+ if (app == null) {
+ return defaultValue();
+ }
if (mPackageOverrides != null && mPackageOverrides.containsKey(app.packageName)) {
return mPackageOverrides.get(app.packageName);
}
@@ -156,6 +159,15 @@ public final class CompatChange extends CompatibilityChangeInfo {
}
/**
+ * Returns the default value for the change id, assuming there are no overrides.
+ *
+ * @return {@code false} if it's a default disabled change, {@code true} otherwise.
+ */
+ boolean defaultValue() {
+ return !getDisabled();
+ }
+
+ /**
* Checks whether a change has an override for a package.
* @param packageName name of the package
* @return true if there is such override
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index d80c58b39768..8511118cc840 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -392,6 +392,14 @@ final class CompatConfig {
return alreadyKnown;
}
+ boolean defaultChangeIdValue(long changeId) {
+ CompatChange c = mChanges.get(changeId);
+ if (c == null) {
+ return true;
+ }
+ return c.defaultValue();
+ }
+
@VisibleForTesting
void clearChanges() {
synchronized (mChanges) {
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 77d5411f5f7f..4bd01d465e62 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -120,10 +120,12 @@ public class PlatformCompat extends IPlatformCompat.Stub {
* permission check.
*/
public boolean isChangeEnabledInternal(long changeId, ApplicationInfo appInfo) {
- boolean value = isChangeEnabledInternalNoLogging(changeId, appInfo);
- reportChange(changeId, appInfo.uid,
- value ? ChangeReporter.STATE_ENABLED : ChangeReporter.STATE_DISABLED);
- return value;
+ boolean enabled = isChangeEnabledInternalNoLogging(changeId, appInfo);
+ if (appInfo != null) {
+ reportChange(changeId, appInfo.uid,
+ enabled ? ChangeReporter.STATE_ENABLED : ChangeReporter.STATE_DISABLED);
+ }
+ return enabled;
}
@Override
@@ -131,9 +133,6 @@ public class PlatformCompat extends IPlatformCompat.Stub {
@UserIdInt int userId) {
checkCompatChangeReadAndLogPermission();
ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
- if (appInfo == null) {
- return true;
- }
return isChangeEnabled(changeId, appInfo);
}
@@ -142,7 +141,7 @@ public class PlatformCompat extends IPlatformCompat.Stub {
checkCompatChangeReadAndLogPermission();
String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
if (packages == null || packages.length == 0) {
- return true;
+ return mCompatConfig.defaultChangeIdValue(changeId);
}
boolean enabled = true;
for (String packageName : packages) {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index 20458b4db558..cc510fbc38bd 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -39,6 +39,10 @@ import android.util.proto.ProtoOutputStream;
import com.android.server.location.ClientBrokerProto;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
@@ -119,6 +123,13 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
private final boolean mHasAccessContextHubPermission;
/*
+ * The set of nanoapp IDs that represent the group of nanoapps this client has a messaging
+ * channel with, i.e. has sent or received messages from this particular nanoapp.
+ */
+ private final Set<Long> mMessageChannelNanoappIdSet =
+ Collections.newSetFromMap(new ConcurrentHashMap<Long, Boolean>());
+
+ /*
* Helper class to manage registered PendingIntent requests from the client.
*/
private class PendingIntentRequest {
@@ -134,7 +145,8 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
private boolean mValid = false;
- PendingIntentRequest() {}
+ PendingIntentRequest() {
+ }
PendingIntentRequest(PendingIntent pendingIntent, long nanoAppId) {
mPendingIntent = pendingIntent;
@@ -177,7 +189,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
mPackage = mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
mHasAccessContextHubPermission = context.checkCallingPermission(
- Manifest.permission.ACCESS_CONTEXT_HUB) == PERMISSION_GRANTED;
+ Manifest.permission.ACCESS_CONTEXT_HUB) == PERMISSION_GRANTED;
}
/* package */ ContextHubClientBroker(
@@ -193,7 +205,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
mPackage = pendingIntent.getCreatorPackage();
mHasAccessContextHubPermission = context.checkCallingPermission(
- Manifest.permission.ACCESS_CONTEXT_HUB) == PERMISSION_GRANTED;
+ Manifest.permission.ACCESS_CONTEXT_HUB) == PERMISSION_GRANTED;
}
/**
@@ -209,6 +221,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
int result;
if (isRegistered()) {
+ mMessageChannelNanoappIdSet.add(message.getNanoAppId());
ContextHubMsg messageToNanoApp =
ContextHubServiceUtil.createHidlContextHubMessage(mHostEndPointId, message);
@@ -269,6 +282,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
* @param message the message that came from a nanoapp
*/
/* package */ void sendMessageToClient(NanoAppMessage message) {
+ mMessageChannelNanoappIdSet.add(message.getNanoAppId());
invokeCallback(callback -> callback.onMessageFromNanoApp(message));
Supplier<Intent> supplier =
@@ -413,7 +427,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
/**
* Sends an intent to any existing PendingIntent
*
- * @param supplier method to create the extra Intent
+ * @param supplier method to create the extra Intent
* @param nanoAppId the ID of the nanoapp which this event is for
*/
private synchronized void sendPendingIntent(Supplier<Intent> supplier, long nanoAppId) {
@@ -427,7 +441,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
* Sends a PendingIntent with extra Intent data
*
* @param pendingIntent the PendingIntent
- * @param intent the extra Intent data
+ * @param intent the extra Intent data
*/
private void doSendPendingIntent(PendingIntent pendingIntent, Intent intent) {
try {
@@ -500,6 +514,17 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
} else {
out += "package: " + mPackage;
}
+ if (mMessageChannelNanoappIdSet.size() > 0) {
+ out += " messageChannelNanoappSet: (";
+ Iterator<Long> it = mMessageChannelNanoappIdSet.iterator();
+ while (it.hasNext()) {
+ out += "0x" + Long.toHexString(it.next());
+ if (it.hasNext()) {
+ out += ",";
+ }
+ }
+ out += ")";
+ }
out += "]";
return out;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 225c998d4c50..ccbf73ca9ab0 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -4760,31 +4760,13 @@ public class UserManagerService extends IUserManager.Stub {
final boolean hasParent = user.profileGroupId != user.id
&& user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID;
if (verbose) {
- final DevicePolicyManagerInternal dpm = getDevicePolicyManagerInternal();
- String deviceOwner = "";
- String profileOwner = "";
- if (dpm != null) {
- final long ident = Binder.clearCallingIdentity();
- try {
- if (dpm.getDeviceOwnerUserId() == user.id) {
- deviceOwner = " (device-owner)";
- }
- if (dpm.getProfileOwnerAsUser(user.id) != null) {
- profileOwner = " (profile-owner)";
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s%s%s%s%s\n", i, user.id,
- user.name,
+ pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s%s%s\n", i, user.id, user.name,
UserInfo.flagsToString(user.flags),
hasParent ? " (parentId=" + user.profileGroupId + ")" : "",
running ? " (running)" : "",
user.partial ? " (partial)" : "",
user.preCreated ? " (pre-created)" : "",
user.convertedFromPreCreated ? " (converted)" : "",
- deviceOwner, profileOwner,
current ? " (current)" : "");
} else {
// NOTE: the standard "list users" command is used by integration tests and
@@ -4878,21 +4860,6 @@ public class UserManagerService extends IUserManager.Stub {
if (userInfo.convertedFromPreCreated) {
pw.print(" <converted>");
}
- final DevicePolicyManagerInternal dpm = getDevicePolicyManagerInternal();
- if (dpm != null) {
- final long ident = Binder.clearCallingIdentity();
- try {
- if (dpm.getDeviceOwnerUserId() == userId) {
- pw.print(" <device-owner>");
- }
- if (dpm.getProfileOwnerAsUser(userId) != null) {
- pw.print(" <profile-owner>");
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
pw.println();
pw.print(" Type: "); pw.println(userInfo.userType);
pw.print(" Flags: "); pw.print(userInfo.flags); pw.print(" (");
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
index 88e5f69e02c4..79c039291f57 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
@@ -16,7 +16,11 @@
package com.android.server.powerstats;
+import android.hardware.power.stats.ChannelInfo;
+import android.hardware.power.stats.EnergyMeasurement;
import android.hardware.power.stats.IPowerStats;
+import android.hardware.power.stats.PowerEntityInfo;
+import android.hardware.power.stats.StateResidencyResult;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -32,6 +36,7 @@ import java.util.function.Supplier;
*/
public final class PowerStatsHALWrapper {
private static final String TAG = PowerStatsHALWrapper.class.getSimpleName();
+ private static final boolean DEBUG = false;
/**
* IPowerStatsHALWrapper defines the interface to the PowerStatsHAL.
@@ -122,17 +127,30 @@ public final class PowerStatsHALWrapper {
*
* @return true if connection to power stats HAL was correctly established.
*/
- boolean initialize();
+ boolean isInitialized();
}
/**
- * PowerStatsHALWrapperImpl is the implementation of the IPowerStatsHALWrapper
- * used by the PowerStatsService. Other implementations will be used by the testing
- * framework and will be passed into the PowerStatsService through an injector.
+ * PowerStatsHALWrapper20Impl is the implementation of the IPowerStatsHALWrapper
+ * used by the PowerStatsService on devices that support only PowerStats HAL 2.0.
+ * Other implementations will be used by the testing framework and will be passed
+ * into the PowerStatsService through an injector.
*/
- public static final class PowerStatsHALWrapperImpl implements IPowerStatsHALWrapper {
+ public static final class PowerStatsHAL20WrapperImpl implements IPowerStatsHALWrapper {
private static Supplier<IPowerStats> sVintfPowerStats;
+ public PowerStatsHAL20WrapperImpl() {
+ Supplier<IPowerStats> service = new VintfHalCache();
+ sVintfPowerStats = null;
+
+ if (service.get() == null) {
+ if (DEBUG) Slog.d(TAG, "PowerStats HAL 2.0 not available on this device.");
+ sVintfPowerStats = null;
+ } else {
+ sVintfPowerStats = service;
+ }
+ }
+
@Override
public android.hardware.power.stats.PowerEntityInfo[] getPowerEntityInfo() {
android.hardware.power.stats.PowerEntityInfo[] powerEntityInfoHAL = null;
@@ -141,7 +159,7 @@ public final class PowerStatsHALWrapper {
try {
powerEntityInfoHAL = sVintfPowerStats.get().getPowerEntityInfo();
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to get power entity info from PowerStats HAL");
+ if (DEBUG) Slog.d(TAG, "Failed to get power entity info from PowerStats HAL");
}
}
@@ -158,7 +176,7 @@ public final class PowerStatsHALWrapper {
stateResidencyResultHAL =
sVintfPowerStats.get().getStateResidency(powerEntityIds);
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to get state residency from PowerStats HAL");
+ if (DEBUG) Slog.d(TAG, "Failed to get state residency from PowerStats HAL");
}
}
@@ -173,7 +191,9 @@ public final class PowerStatsHALWrapper {
try {
energyConsumerInfoHAL = sVintfPowerStats.get().getEnergyConsumerInfo();
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to get energy consumer info from PowerStats HAL");
+ if (DEBUG) {
+ Slog.d(TAG, "Failed to get energy consumer info from PowerStats HAL");
+ }
}
}
@@ -190,7 +210,9 @@ public final class PowerStatsHALWrapper {
energyConsumedHAL =
sVintfPowerStats.get().getEnergyConsumed(energyConsumerIds);
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to get energy consumer results from PowerStats HAL");
+ if (DEBUG) {
+ Slog.d(TAG, "Failed to get energy consumer results from PowerStats HAL");
+ }
}
}
@@ -205,7 +227,7 @@ public final class PowerStatsHALWrapper {
try {
energyMeterInfoHAL = sVintfPowerStats.get().getEnergyMeterInfo();
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to get energy meter info from PowerStats HAL");
+ if (DEBUG) Slog.d(TAG, "Failed to get energy meter info from PowerStats HAL");
}
}
@@ -221,7 +243,7 @@ public final class PowerStatsHALWrapper {
energyMeasurementHAL =
sVintfPowerStats.get().readEnergyMeters(channelIds);
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to get energy measurements from PowerStats HAL");
+ if (DEBUG) Slog.d(TAG, "Failed to get energy measurements from PowerStats HAL");
}
}
@@ -229,17 +251,90 @@ public final class PowerStatsHALWrapper {
}
@Override
- public boolean initialize() {
- Supplier<IPowerStats> service = new VintfHalCache();
+ public boolean isInitialized() {
+ return (sVintfPowerStats != null);
+ }
+ }
- if (service.get() == null) {
- sVintfPowerStats = null;
- return false;
+ /**
+ * PowerStatsHALWrapper10Impl is the implementation of the IPowerStatsHALWrapper
+ * used by the PowerStatsService on devices that support only PowerStats HAL 1.0.
+ * Other implementations will be used by the testing framework and will be passed
+ * into the PowerStatsService through an injector.
+ */
+ public static final class PowerStatsHAL10WrapperImpl implements IPowerStatsHALWrapper {
+ private boolean mIsInitialized;
+
+ // PowerStatsHAL 1.0 native functions exposed by JNI layer.
+ private static native boolean nativeInit();
+ private static native PowerEntityInfo[] nativeGetPowerEntityInfo();
+ private static native StateResidencyResult[] nativeGetStateResidency(int[] powerEntityIds);
+ private static native ChannelInfo[] nativeGetEnergyMeterInfo();
+ private static native EnergyMeasurement[] nativeReadEnergyMeters(int[] channelIds);
+
+ public PowerStatsHAL10WrapperImpl() {
+ if (nativeInit()) {
+ mIsInitialized = true;
} else {
- sVintfPowerStats = service;
- return true;
+ if (DEBUG) Slog.d(TAG, "PowerStats HAL 1.0 not available on this device.");
+ mIsInitialized = false;
}
}
+
+ @Override
+ public android.hardware.power.stats.PowerEntityInfo[] getPowerEntityInfo() {
+ return nativeGetPowerEntityInfo();
+ }
+
+ @Override
+ public android.hardware.power.stats.StateResidencyResult[] getStateResidency(
+ int[] powerEntityIds) {
+ return nativeGetStateResidency(powerEntityIds);
+ }
+
+ @Override
+ public int[] getEnergyConsumerInfo() {
+ if (DEBUG) Slog.d(TAG, "Energy consumer info is not supported");
+ return null;
+ }
+
+ @Override
+ public android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed(
+ int[] energyConsumerIds) {
+ if (DEBUG) Slog.d(TAG, "Energy consumer results are not supported");
+ return null;
+ }
+
+ @Override
+ public android.hardware.power.stats.ChannelInfo[] getEnergyMeterInfo() {
+ return nativeGetEnergyMeterInfo();
+ }
+
+ @Override
+ public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters(int[] channelIds) {
+ return nativeReadEnergyMeters(channelIds);
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return mIsInitialized;
+ }
+ }
+
+ /**
+ * Returns an instance of an IPowerStatsHALWrapper. If PowerStats HAL 2.0 is supported on the
+ * device, return a PowerStatsHAL20WrapperImpl, else return a PowerStatsHAL10WrapperImpl.
+ *
+ * @return an instance of an IPowerStatsHALWrapper where preference is given to PowerStats HAL
+ * 2.0.
+ */
+ public static IPowerStatsHALWrapper getPowerStatsHalImpl() {
+ PowerStatsHAL20WrapperImpl powerStatsHAL20WrapperImpl = new PowerStatsHAL20WrapperImpl();
+ if (powerStatsHAL20WrapperImpl.isInitialized()) {
+ return powerStatsHAL20WrapperImpl;
+ } else {
+ return new PowerStatsHAL10WrapperImpl();
+ }
}
private static class VintfHalCache implements Supplier<IPowerStats>, IBinder.DeathRecipient {
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 1150d4bbe770..ce50e5833c45 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -29,7 +29,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
-import com.android.server.powerstats.PowerStatsHALWrapper.PowerStatsHALWrapperImpl;
import com.android.server.powerstats.ProtoStreamUtils.ChannelInfoUtils;
import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerIdUtils;
import com.android.server.powerstats.ProtoStreamUtils.PowerEntityInfoUtils;
@@ -78,7 +77,7 @@ public class PowerStatsService extends SystemService {
}
IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() {
- return new PowerStatsHALWrapperImpl();
+ return PowerStatsHALWrapper.getPowerStatsHalImpl();
}
PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
@@ -143,7 +142,7 @@ public class PowerStatsService extends SystemService {
private void onSystemServiceReady() {
mPowerStatsHALWrapper = mInjector.createPowerStatsHALWrapperImpl();
- if (mPowerStatsHALWrapper.initialize()) {
+ if (mPowerStatsHALWrapper.isInitialized()) {
if (DEBUG) Slog.d(TAG, "Starting PowerStatsService");
// Only start logger and triggers if initialization is successful.
diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
index 5a4256ac0264..5e23b86e0adb 100644
--- a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
+++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
@@ -46,23 +46,31 @@ public class ProtoStreamUtils {
static class PowerEntityInfoUtils {
public static void print(PowerEntityInfo[] powerEntityInfo) {
+ if (powerEntityInfo == null) return;
+
for (int i = 0; i < powerEntityInfo.length; i++) {
Slog.d(TAG, "PowerEntityId: " + powerEntityInfo[i].powerEntityId
+ ", PowerEntityName: " + powerEntityInfo[i].powerEntityName);
- for (int j = 0; j < powerEntityInfo[i].states.length; j++) {
- Slog.d(TAG, " StateId: " + powerEntityInfo[i].states[j].stateId
- + ", StateName: " + powerEntityInfo[i].states[j].stateName);
+ if (powerEntityInfo[i].states != null) {
+ for (int j = 0; j < powerEntityInfo[i].states.length; j++) {
+ Slog.d(TAG, " StateId: " + powerEntityInfo[i].states[j].stateId
+ + ", StateName: " + powerEntityInfo[i].states[j].stateName);
+ }
}
}
}
public static void dumpsys(PowerEntityInfo[] powerEntityInfo, PrintWriter pw) {
+ if (powerEntityInfo == null) return;
+
for (int i = 0; i < powerEntityInfo.length; i++) {
pw.println("PowerEntityId: " + powerEntityInfo[i].powerEntityId
+ ", PowerEntityName: " + powerEntityInfo[i].powerEntityName);
- for (int j = 0; j < powerEntityInfo[i].states.length; j++) {
- pw.println(" StateId: " + powerEntityInfo[i].states[j].stateId
- + ", StateName: " + powerEntityInfo[i].states[j].stateName);
+ if (powerEntityInfo[i].states != null) {
+ for (int j = 0; j < powerEntityInfo[i].states.length; j++) {
+ pw.println(" StateId: " + powerEntityInfo[i].states[j].stateId
+ + ", StateName: " + powerEntityInfo[i].states[j].stateName);
+ }
}
}
}
@@ -70,6 +78,8 @@ public class ProtoStreamUtils {
static class StateResidencyResultUtils {
public static void print(StateResidencyResult[] stateResidencyResult) {
+ if (stateResidencyResult == null) return;
+
for (int i = 0; i < stateResidencyResult.length; i++) {
Slog.d(TAG, "PowerEntityId: " + stateResidencyResult[i].powerEntityId);
for (int j = 0; j < stateResidencyResult[i].stateResidencyData.length; j++) {
@@ -90,6 +100,8 @@ public class ProtoStreamUtils {
public static void packProtoMessage(ChannelInfo[] channelInfo, ProtoOutputStream pos) {
long token;
+ if (channelInfo == null) return;
+
for (int i = 0; i < channelInfo.length; i++) {
token = pos.start(PowerStatsServiceMeterProto.CHANNEL_INFO);
pos.write(ChannelInfoProto.CHANNEL_ID, channelInfo[i].channelId);
@@ -100,6 +112,8 @@ public class ProtoStreamUtils {
}
public static void print(ChannelInfo[] channelInfo) {
+ if (channelInfo == null) return;
+
for (int i = 0; i < channelInfo.length; i++) {
Slog.d(TAG, "ChannelId: " + channelInfo[i].channelId
+ ", ChannelName: " + channelInfo[i].channelName);
@@ -107,6 +121,8 @@ public class ProtoStreamUtils {
}
public static void dumpsys(ChannelInfo[] channelInfo, PrintWriter pw) {
+ if (channelInfo == null) return;
+
for (int i = 0; i < channelInfo.length; i++) {
pw.println("ChannelId: " + channelInfo[i].channelId
+ ", ChannelName: " + channelInfo[i].channelName);
@@ -125,6 +141,8 @@ public class ProtoStreamUtils {
ProtoOutputStream pos) {
long token;
+ if (energyMeasurement == null) return;
+
for (int i = 0; i < energyMeasurement.length; i++) {
token = pos.start(PowerStatsServiceMeterProto.ENERGY_MEASUREMENT);
pos.write(EnergyMeasurementProto.CHANNEL_ID, energyMeasurement[i].channelId);
@@ -200,6 +218,8 @@ public class ProtoStreamUtils {
}
public static void print(EnergyMeasurement[] energyMeasurement) {
+ if (energyMeasurement == null) return;
+
for (int i = 0; i < energyMeasurement.length; i++) {
Slog.d(TAG, "ChannelId: " + energyMeasurement[i].channelId
+ ", Timestamp (ms): " + energyMeasurement[i].timestampMs
@@ -212,6 +232,8 @@ public class ProtoStreamUtils {
public static void packProtoMessage(int[] energyConsumerId, ProtoOutputStream pos) {
long token;
+ if (energyConsumerId == null) return;
+
for (int i = 0; i < energyConsumerId.length; i++) {
token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_ID);
pos.write(EnergyConsumerIdProto.ENERGY_CONSUMER_ID, energyConsumerId[i]);
@@ -220,12 +242,16 @@ public class ProtoStreamUtils {
}
public static void print(int[] energyConsumerId) {
+ if (energyConsumerId == null) return;
+
for (int i = 0; i < energyConsumerId.length; i++) {
Slog.d(TAG, "EnergyConsumerId: " + energyConsumerId[i]);
}
}
public static void dumpsys(int[] energyConsumerId, PrintWriter pw) {
+ if (energyConsumerId == null) return;
+
for (int i = 0; i < energyConsumerId.length; i++) {
pw.println("EnergyConsumerId: " + energyConsumerId[i]);
}
@@ -243,6 +269,8 @@ public class ProtoStreamUtils {
ProtoOutputStream pos) {
long token;
+ if (energyConsumerResult == null) return;
+
for (int i = 0; i < energyConsumerResult.length; i++) {
token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT);
pos.write(EnergyConsumerResultProto.ENERGY_CONSUMER_ID,
@@ -321,6 +349,8 @@ public class ProtoStreamUtils {
}
public static void print(EnergyConsumerResult[] energyConsumerResult) {
+ if (energyConsumerResult == null) return;
+
for (int i = 0; i < energyConsumerResult.length; i++) {
Slog.d(TAG, "EnergyConsumerId: " + energyConsumerResult[i].energyConsumerId
+ ", Timestamp (ms): " + energyConsumerResult[i].timestampMs
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
index 990055ebda9a..5c01e43af5a9 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@@ -32,6 +32,8 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.SystemProperties;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -49,10 +51,6 @@ import java.io.FileDescriptor;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
/**
* The recovery system service is responsible for coordinating recovery related
@@ -84,9 +82,9 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
private final Context mContext;
@GuardedBy("this")
- private final Map<String, IntentSender> mCallerPendingRequest = new HashMap<>();
+ private final ArrayMap<String, IntentSender> mCallerPendingRequest = new ArrayMap<>();
@GuardedBy("this")
- private final Set<String> mCallerPreparedForReboot = new HashSet<>();
+ private final ArraySet<String> mCallerPreparedForReboot = new ArraySet<>();
/**
* Need to prepare for resume on reboot.
@@ -121,7 +119,7 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
@IntDef({ ROR_NEED_PREPARATION,
ROR_SKIP_PREPARATION_AND_NOTIFY,
ROR_SKIP_PREPARATION_NOT_NOTIFY })
- @interface ResumeOnRebootActionsOnRequest {}
+ private @interface ResumeOnRebootActionsOnRequest {}
/**
* The action to perform upon resume on reboot clear request for a given client.
@@ -129,7 +127,7 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
@IntDef({ROR_NOT_REQUESTED,
ROR_REQUESTED_NEED_CLEAR,
ROR_REQUESTED_SKIP_CLEAR})
- @interface ResumeOnRebootActionsOnClear{}
+ private @interface ResumeOnRebootActionsOnClear{}
static class Injector {
protected final Context mContext;
@@ -342,9 +340,8 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
!= PackageManager.PERMISSION_GRANTED
&& mContext.checkCallingOrSelfPermission(android.Manifest.permission.REBOOT)
!= PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Caller or self must have "
- + android.Manifest.permission.RECOVERY + " or "
- + android.Manifest.permission.REBOOT + " for resume on reboot.");
+ throw new SecurityException("Caller must have " + android.Manifest.permission.RECOVERY
+ + " or " + android.Manifest.permission.REBOOT + " for resume on reboot.");
}
}
@@ -414,10 +411,14 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
Slog.w(TAG, "onPreparedForReboot called when some clients have prepared.");
}
+ if (mCallerPendingRequest.isEmpty()) {
+ Slog.w(TAG, "onPreparedForReboot called but no client has requested.");
+ }
+
// Send intents to notify callers
- for (Map.Entry<String, IntentSender> entry : mCallerPendingRequest.entrySet()) {
- sendPreparedForRebootIntentIfNeeded(entry.getValue());
- mCallerPreparedForReboot.add(entry.getKey());
+ for (int i = 0; i < mCallerPendingRequest.size(); i++) {
+ sendPreparedForRebootIntentIfNeeded(mCallerPendingRequest.valueAt(i));
+ mCallerPreparedForReboot.add(mCallerPendingRequest.keyAt(i));
}
mCallerPendingRequest.clear();
}
@@ -475,9 +476,7 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
return needClear ? ROR_REQUESTED_NEED_CLEAR : ROR_REQUESTED_SKIP_CLEAR;
}
- @Override // Binder call
- public boolean rebootWithLskf(String packageName, String reason, boolean slotSwitch) {
- enforcePermissionForResumeOnReboot();
+ private boolean rebootWithLskfImpl(String packageName, String reason, boolean slotSwitch) {
if (packageName == null) {
Slog.w(TAG, "Missing packageName when rebooting with lskf.");
return false;
@@ -498,11 +497,29 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
return true;
}
+ @Override // Binder call for the legacy rebootWithLskf
+ public boolean rebootWithLskfAssumeSlotSwitch(String packageName, String reason) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
+ return rebootWithLskfImpl(packageName, reason, true);
+ }
+
@Override // Binder call
- public synchronized boolean isLskfCaptured(String packageName) {
+ public boolean rebootWithLskf(String packageName, String reason, boolean slotSwitch) {
enforcePermissionForResumeOnReboot();
- if (!mCallerPreparedForReboot.contains(packageName)) {
- Slog.i(TAG, "Reboot requested before prepare completed for caller " + packageName);
+ return rebootWithLskfImpl(packageName, reason, slotSwitch);
+ }
+
+ @Override // Binder call
+ public boolean isLskfCaptured(String packageName) {
+ enforcePermissionForResumeOnReboot();
+ boolean captured;
+ synchronized (this) {
+ captured = mCallerPreparedForReboot.contains(packageName);
+ }
+
+ if (!captured) {
+ Slog.i(TAG, "Reboot requested before prepare completed for caller "
+ + packageName);
return false;
}
return true;
diff --git a/services/core/java/com/android/server/vcn/Android.bp b/services/core/java/com/android/server/vcn/Android.bp
new file mode 100644
index 000000000000..5ed204fd7640
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/Android.bp
@@ -0,0 +1,4 @@
+filegroup {
+ name: "framework-vcn-util-sources",
+ srcs: ["util/**/*.java"],
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
new file mode 100644
index 000000000000..c0608072df9d
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2020 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.vcn;
+
+import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
+import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX;
+import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX;
+import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.ParcelUuid;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * TelephonySubscriptionTracker provides a caching layer for tracking active subscription groups.
+ *
+ * <p>This class performs two roles:
+ *
+ * <ol>
+ * <li>De-noises subscription changes by ensuring that only changes in active and ready
+ * subscription groups are acted upon
+ * <li>Caches mapping between subIds and subscription groups
+ * </ol>
+ *
+ * <p>An subscription group is active and ready if any of its contained subIds has had BOTH the
+ * {@link CarrierConfigManager#isConfigForIdentifiedCarrier()} return true, AND the subscription is
+ * listed as active per SubscriptionManager#getAllSubscriptionInfoList().
+ *
+ * <p>Note that due to the asynchronous nature of callbacks and broadcasts, the output of this class
+ * is (only) eventually consistent.
+ *
+ * @hide
+ */
+public class TelephonySubscriptionTracker extends BroadcastReceiver {
+ @NonNull private static final String TAG = TelephonySubscriptionTracker.class.getSimpleName();
+ private static final boolean LOG_DBG = false; // STOPSHIP if true
+
+ @NonNull private final Context mContext;
+ @NonNull private final Handler mHandler;
+ @NonNull private final TelephonySubscriptionTrackerCallback mCallback;
+ @NonNull private final Dependencies mDeps;
+
+ @NonNull private final SubscriptionManager mSubscriptionManager;
+ @NonNull private final CarrierConfigManager mCarrierConfigManager;
+
+ // TODO (Android T+): Add ability to handle multiple subIds per slot.
+ @NonNull private final Map<Integer, Integer> mReadySubIdsBySlotId = new HashMap<>();
+ @NonNull private final OnSubscriptionsChangedListener mSubscriptionChangedListener;
+
+ @NonNull private TelephonySubscriptionSnapshot mCurrentSnapshot;
+
+ public TelephonySubscriptionTracker(
+ @NonNull Context context,
+ @NonNull Handler handler,
+ @NonNull TelephonySubscriptionTrackerCallback callback) {
+ this(context, handler, callback, new Dependencies());
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ TelephonySubscriptionTracker(
+ @NonNull Context context,
+ @NonNull Handler handler,
+ @NonNull TelephonySubscriptionTrackerCallback callback,
+ @NonNull Dependencies deps) {
+ mContext = Objects.requireNonNull(context, "Missing context");
+ mHandler = Objects.requireNonNull(handler, "Missing handler");
+ mCallback = Objects.requireNonNull(callback, "Missing callback");
+ mDeps = Objects.requireNonNull(deps, "Missing deps");
+
+ mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
+ mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
+
+ mSubscriptionChangedListener =
+ new OnSubscriptionsChangedListener() {
+ @Override
+ public void onSubscriptionsChanged() {
+ handleSubscriptionsChanged();
+ }
+ };
+ }
+
+ /** Registers the receivers, and starts tracking subscriptions. */
+ public void register() {
+ mContext.registerReceiver(
+ this, new IntentFilter(ACTION_CARRIER_CONFIG_CHANGED), null, mHandler);
+ mSubscriptionManager.addOnSubscriptionsChangedListener(
+ new HandlerExecutor(mHandler), mSubscriptionChangedListener);
+ }
+
+ /** Unregisters the receivers, and stops tracking subscriptions. */
+ public void unregister() {
+ mContext.unregisterReceiver(this);
+ mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionChangedListener);
+ }
+
+ /**
+ * Handles subscription changes, correlating available subscriptions and loaded carrier configs
+ *
+ * <p>The subscription change listener is registered with a HandlerExecutor backed by mHandler,
+ * so callbacks & broadcasts are all serialized on mHandler, avoiding the need for locking.
+ */
+ public void handleSubscriptionsChanged() {
+ final Set<ParcelUuid> activeSubGroups = new ArraySet<>();
+ final Map<Integer, ParcelUuid> newSubIdToGroupMap = new HashMap<>();
+
+ final List<SubscriptionInfo> allSubs = mSubscriptionManager.getAllSubscriptionInfoList();
+ if (allSubs == null) {
+ return; // Telephony crashed; no way to verify subscriptions.
+ }
+
+ // If allSubs is empty, no subscriptions exist. Cache will be cleared by virtue of no active
+ // subscriptions
+ for (SubscriptionInfo subInfo : allSubs) {
+ if (subInfo.getGroupUuid() == null) {
+ continue;
+ }
+
+ // Build subId -> subGrp cache
+ newSubIdToGroupMap.put(subInfo.getSubscriptionId(), subInfo.getGroupUuid());
+
+ // Update subscription groups that are both ready, and active. For a group to be
+ // considered active, both of the following must be true:
+ //
+ // 1. A final CARRIER_CONFIG_CHANGED (where config is for an identified carrier)
+ // broadcast must have been received for the subId
+ // 2. A active subscription (is loaded into a SIM slot) must be part of the subscription
+ // group.
+ if (subInfo.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX
+ && mReadySubIdsBySlotId.values().contains(subInfo.getSubscriptionId())) {
+ activeSubGroups.add(subInfo.getGroupUuid());
+ }
+ }
+
+ final TelephonySubscriptionSnapshot newSnapshot =
+ new TelephonySubscriptionSnapshot(newSubIdToGroupMap, activeSubGroups);
+
+ // If snapshot was meaningfully updated, fire the callback
+ if (!newSnapshot.equals(mCurrentSnapshot)) {
+ mCurrentSnapshot = newSnapshot;
+ mHandler.post(
+ () -> {
+ mCallback.onNewSnapshot(newSnapshot);
+ });
+ }
+ }
+
+ /**
+ * Broadcast receiver for ACTION_CARRIER_CONFIG_CHANGED
+ *
+ * <p>The broadcast receiver is registered with mHandler, so callbacks & broadcasts are all
+ * serialized on mHandler, avoiding the need for locking.
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Accept sticky broadcasts; if CARRIER_CONFIG_CHANGED was previously broadcast and it
+ // already was for an identified carrier, we can stop waiting for initial load to complete
+ if (!ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
+ return;
+ }
+
+ final int subId = intent.getIntExtra(EXTRA_SUBSCRIPTION_INDEX, INVALID_SUBSCRIPTION_ID);
+ final int slotId = intent.getIntExtra(EXTRA_SLOT_INDEX, INVALID_SIM_SLOT_INDEX);
+
+ if (slotId == INVALID_SIM_SLOT_INDEX) {
+ return;
+ }
+
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ final PersistableBundle carrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
+ if (mDeps.isConfigForIdentifiedCarrier(carrierConfigs)) {
+ Slog.v(TAG, String.format("SubId %s ready for SlotId %s", subId, slotId));
+ mReadySubIdsBySlotId.put(slotId, subId);
+ handleSubscriptionsChanged();
+ }
+ } else {
+ Slog.v(TAG, "Slot unloaded: " + slotId);
+ mReadySubIdsBySlotId.remove(slotId);
+ handleSubscriptionsChanged();
+ }
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void setReadySubIdsBySlotId(Map<Integer, Integer> readySubIdsBySlotId) {
+ mReadySubIdsBySlotId.putAll(readySubIdsBySlotId);
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ Map<Integer, Integer> getReadySubIdsBySlotId() {
+ return Collections.unmodifiableMap(mReadySubIdsBySlotId);
+ }
+
+ /** TelephonySubscriptionSnapshot is a class containing info about active subscriptions */
+ public static class TelephonySubscriptionSnapshot {
+ private final Map<Integer, ParcelUuid> mSubIdToGroupMap;
+ private final Set<ParcelUuid> mActiveGroups;
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ TelephonySubscriptionSnapshot(
+ @NonNull Map<Integer, ParcelUuid> subIdToGroupMap,
+ @NonNull Set<ParcelUuid> activeGroups) {
+ mSubIdToGroupMap = Collections.unmodifiableMap(
+ Objects.requireNonNull(subIdToGroupMap, "subIdToGroupMap was null"));
+ mActiveGroups = Collections.unmodifiableSet(
+ Objects.requireNonNull(activeGroups, "activeGroups was null"));
+ }
+
+ /** Returns the active subscription groups */
+ @NonNull
+ public Set<ParcelUuid> getActiveSubscriptionGroups() {
+ return mActiveGroups;
+ }
+
+ /** Returns the Subscription Group for a given subId. */
+ @Nullable
+ public ParcelUuid getGroupForSubId(int subId) {
+ return mSubIdToGroupMap.get(subId);
+ }
+
+ /**
+ * Returns all the subIds in a given group, including available, but inactive subscriptions.
+ */
+ @NonNull
+ public Set<Integer> getAllSubIdsInGroup(ParcelUuid subGrp) {
+ final Set<Integer> subIds = new ArraySet<>();
+
+ for (Entry<Integer, ParcelUuid> entry : mSubIdToGroupMap.entrySet()) {
+ if (subGrp.equals(entry.getValue())) {
+ subIds.add(entry.getKey());
+ }
+ }
+
+ return subIds;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSubIdToGroupMap, mActiveGroups);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof TelephonySubscriptionSnapshot)) {
+ return false;
+ }
+
+ final TelephonySubscriptionSnapshot other = (TelephonySubscriptionSnapshot) obj;
+
+ return mSubIdToGroupMap.equals(other.mSubIdToGroupMap)
+ && mActiveGroups.equals(other.mActiveGroups);
+ }
+ }
+
+ /**
+ * Interface for listening to changes in subscriptions
+ *
+ * @see TelephonySubscriptionTracker
+ */
+ public interface TelephonySubscriptionTrackerCallback {
+ /**
+ * Called when subscription information changes, and a new subscription snapshot was taken
+ *
+ * @param snapshot the snapshot of subscription information.
+ */
+ void onNewSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot);
+ }
+
+ /** External static dependencies for test injection */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class Dependencies {
+ /** Checks if the given bundle is for an identified carrier */
+ public boolean isConfigForIdentifiedCarrier(PersistableBundle bundle) {
+ return CarrierConfigManager.isConfigForIdentifiedCarrier(bundle);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
index c475da354dda..53f700938bf1 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
@@ -134,7 +134,7 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
@Override
public DisplayAreaAppearedInfo createTaskDisplayArea(IDisplayAreaOrganizer organizer,
- int displayId, int rootFeatureId, String name) {
+ int displayId, int parentFeatureId, String name) {
enforceTaskPermission("createTaskDisplayArea()");
final long uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
@@ -149,13 +149,26 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
+ displayId);
}
- final DisplayArea root = display.getItemFromDisplayAreas(da ->
- da.asRootDisplayArea() != null && da.mFeatureId == rootFeatureId
- ? da
+ // The parentFeatureId can be either a RootDisplayArea or a TaskDisplayArea.
+ // Check if there is a RootDisplayArea with the given parentFeatureId.
+ final RootDisplayArea parentRoot = display.getItemFromDisplayAreas(da ->
+ da.asRootDisplayArea() != null && da.mFeatureId == parentFeatureId
+ ? da.asRootDisplayArea()
: null);
- if (root == null) {
- throw new IllegalArgumentException("Can't find RootDisplayArea with featureId="
- + rootFeatureId);
+ final TaskDisplayArea parentTda;
+ if (parentRoot == null) {
+ // There is no RootDisplayArea matching the parentFeatureId.
+ // Check if there is a TaskDisplayArea with the given parentFeatureId.
+ parentTda = display.getItemFromTaskDisplayAreas(taskDisplayArea ->
+ taskDisplayArea.mFeatureId == parentFeatureId
+ ? taskDisplayArea
+ : null);
+ } else {
+ parentTda = null;
+ }
+ if (parentRoot == null && parentTda == null) {
+ throw new IllegalArgumentException(
+ "Can't find a parent DisplayArea with featureId=" + parentFeatureId);
}
final int taskDisplayAreaFeatureId = mNextTaskDisplayAreaFeatureId++;
@@ -166,10 +179,13 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
// Oh well...
}
- final TaskDisplayArea tda = createTaskDisplayArea(root.asRootDisplayArea(), name,
- taskDisplayAreaFeatureId);
- return organizeDisplayArea(organizer, tda,
+ final TaskDisplayArea tda = parentRoot != null
+ ? createTaskDisplayArea(parentRoot, name, taskDisplayAreaFeatureId)
+ : createTaskDisplayArea(parentTda, name, taskDisplayAreaFeatureId);
+ final DisplayAreaAppearedInfo tdaInfo = organizeDisplayArea(organizer, tda,
"DisplayAreaOrganizerController.createTaskDisplayArea");
+ mOrganizersByFeatureIds.put(taskDisplayAreaFeatureId, organizer);
+ return tdaInfo;
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -196,6 +212,7 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
+ "TaskDisplayArea=" + taskDisplayArea);
}
+ mOrganizersByFeatureIds.remove(taskDisplayArea.mFeatureId);
deleteTaskDisplayArea(taskDisplayArea);
}
} finally {
@@ -253,6 +270,9 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
new SurfaceControl(displayArea.getSurfaceControl(), callsite));
}
+ /**
+ * Creates a {@link TaskDisplayArea} as the topmost TDA below the given {@link RootDisplayArea}.
+ */
private TaskDisplayArea createTaskDisplayArea(RootDisplayArea root, String name,
int taskDisplayAreaFeatureId) {
final TaskDisplayArea taskDisplayArea = new TaskDisplayArea(root.mDisplayContent,
@@ -283,6 +303,21 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
return taskDisplayArea;
}
+ /**
+ * Creates a {@link TaskDisplayArea} as the topmost child of the given {@link TaskDisplayArea}.
+ */
+ private TaskDisplayArea createTaskDisplayArea(TaskDisplayArea parentTda, String name,
+ int taskDisplayAreaFeatureId) {
+ final TaskDisplayArea taskDisplayArea = new TaskDisplayArea(parentTda.mDisplayContent,
+ parentTda.mWmService, name, taskDisplayAreaFeatureId,
+ true /* createdByOrganizer */);
+
+ // Insert the TaskDisplayArea on the top.
+ parentTda.addChild(taskDisplayArea, WindowContainer.POSITION_TOP);
+
+ return taskDisplayArea;
+ }
+
private void deleteTaskDisplayArea(TaskDisplayArea taskDisplayArea) {
taskDisplayArea.setOrganizer(null);
mService.mRootWindowContainer.mTaskSupervisor.beginDeferResume();
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 2c4eb1b92086..77ca4e9eddab 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -40,6 +40,7 @@ cc_library_static {
"com_android_server_locksettings_SyntheticPasswordManager.cpp",
"com_android_server_net_NetworkStatsService.cpp",
"com_android_server_power_PowerManagerService.cpp",
+ "com_android_server_powerstats_PowerStatsService.cpp",
"com_android_server_security_VerityUtils.cpp",
"com_android_server_SerialService.cpp",
"com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp",
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 7fc55656fc80..995cfe9fc2de 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -23,6 +23,7 @@ per-file com_android_server_locksettings_* = file:/services/core/java/com/androi
per-file com_android_server_net_* = file:/services/core/java/com/android/server/net/OWNERS
per-file com_android_server_pm_* = file:/services/core/java/com/android/server/pm/OWNERS
per-file com_android_server_power_* = file:/services/core/java/com/android/server/power/OWNERS
+per-file com_android_server_powerstats_* = file:/services/core/java/com/android/server/powerstats/OWNERS
per-file com_android_server_se_* = file:/core/java/android/se/OWNERS
per-file com_android_server_security_* = file:/core/java/android/security/OWNERS
per-file com_android_server_tv_* = file:/media/java/android/media/tv/OWNERS
diff --git a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp
new file mode 100644
index 000000000000..f5b851f7aea5
--- /dev/null
+++ b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "PowerStatsService"
+
+#include <android/hardware/power/stats/1.0/IPowerStats.h>
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+
+#include <log/log.h>
+
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::power::stats::V1_0::EnergyData;
+using android::hardware::power::stats::V1_0::RailInfo;
+using android::hardware::power::stats::V1_0::Status;
+
+// ChannelInfo
+static jclass class_CI;
+static jmethodID method_CI_init;
+static jfieldID field_CI_channelId;
+static jfieldID field_CI_channelName;
+
+// EnergyMeasurement
+static jclass class_EM;
+static jmethodID method_EM_init;
+static jfieldID field_EM_channelId;
+static jfieldID field_EM_timestampMs;
+static jfieldID field_EM_durationMs;
+static jfieldID field_EM_energyUWs;
+
+// StateInfo
+static jclass class_SI;
+static jmethodID method_SI_init;
+static jfieldID field_SI_stateId;
+static jfieldID field_SI_stateName;
+
+// PowerEntityInfo
+static jclass class_PEI;
+static jmethodID method_PEI_init;
+static jfieldID field_PEI_powerEntityId;
+static jfieldID field_PEI_powerEntityName;
+static jfieldID field_PEI_states;
+
+// StateResidency
+static jclass class_SR;
+static jmethodID method_SR_init;
+static jfieldID field_SR_stateId;
+static jfieldID field_SR_totalTimeInStateMs;
+static jfieldID field_SR_totalStateEntryCount;
+static jfieldID field_SR_lastEntryTimestampMs;
+
+// StateResidencyResult
+static jclass class_SRR;
+static jmethodID method_SRR_init;
+static jfieldID field_SRR_powerEntityId;
+static jfieldID field_SRR_stateResidencyData;
+
+namespace android {
+
+static std::mutex gPowerStatsHalMutex;
+static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0_ptr = nullptr;
+
+static void deinitPowerStats() {
+ gPowerStatsHalV1_0_ptr = nullptr;
+}
+
+struct PowerStatsHalDeathRecipient : virtual public hardware::hidl_death_recipient {
+ virtual void serviceDied(uint64_t cookie,
+ const wp<android::hidl::base::V1_0::IBase> &who) override {
+ // The HAL just died. Reset all handles to HAL services.
+ std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
+ deinitPowerStats();
+ }
+};
+
+sp<PowerStatsHalDeathRecipient> gPowerStatsHalDeathRecipient = new PowerStatsHalDeathRecipient();
+
+static bool connectToPowerStatsHal() {
+ if (gPowerStatsHalV1_0_ptr == nullptr) {
+ gPowerStatsHalV1_0_ptr = android::hardware::power::stats::V1_0::IPowerStats::getService();
+
+ if (gPowerStatsHalV1_0_ptr == nullptr) {
+ ALOGE("Unable to get power.stats HAL service.");
+ return false;
+ }
+
+ // Link death recipient to power.stats service handle
+ hardware::Return<bool> linked =
+ gPowerStatsHalV1_0_ptr->linkToDeath(gPowerStatsHalDeathRecipient, 0);
+ if (!linked.isOk()) {
+ ALOGE("Transaction error in linking to power.stats HAL death: %s",
+ linked.description().c_str());
+ deinitPowerStats();
+ return false;
+ } else if (!linked) {
+ ALOGW("Unable to link to power.stats HAL death notifications");
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool checkResult(const Return<void> &ret, const char *function) {
+ if (!ret.isOk()) {
+ ALOGE("%s failed: requested HAL service not available. Description: %s", function,
+ ret.description().c_str());
+ if (ret.isDeadObject()) {
+ deinitPowerStats();
+ }
+ return false;
+ }
+ return true;
+}
+
+static jobjectArray nativeGetPowerEntityInfo(JNIEnv *env, jclass clazz) {
+ std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
+
+ if (!connectToPowerStatsHal()) {
+ ALOGE("nativeGetPowerEntityInfo failed to connect to power.stats HAL");
+ return nullptr;
+ }
+
+ jobjectArray powerEntityInfoArray = nullptr;
+ Return<void> ret = gPowerStatsHalV1_0_ptr->getPowerEntityInfo(
+ [&env, &powerEntityInfoArray](auto infos, auto status) {
+ if (status != Status::SUCCESS) {
+ ALOGE("Error getting power entity info");
+ } else {
+ powerEntityInfoArray = env->NewObjectArray(infos.size(), class_PEI, nullptr);
+ for (int i = 0; i < infos.size(); i++) {
+ jstring powerEntityName =
+ env->NewStringUTF(infos[i].powerEntityName.c_str());
+ jobject powerEntityInfo = env->NewObject(class_PEI, method_PEI_init);
+ env->SetIntField(powerEntityInfo, field_PEI_powerEntityId,
+ infos[i].powerEntityId);
+ env->SetObjectField(powerEntityInfo, field_PEI_powerEntityName,
+ powerEntityName);
+ env->SetObjectArrayElement(powerEntityInfoArray, i, powerEntityInfo);
+ env->DeleteLocalRef(powerEntityName);
+ env->DeleteLocalRef(powerEntityInfo);
+ }
+ }
+ });
+ if (!checkResult(ret, __func__)) {
+ return nullptr;
+ }
+
+ ret = gPowerStatsHalV1_0_ptr->getPowerEntityStateInfo(
+ {}, [&env, &powerEntityInfoArray](auto infos, auto status) {
+ if (status != Status::SUCCESS) {
+ ALOGE("Error getting power entity state info");
+ } else {
+ for (int i = 0; i < infos.size(); i++) {
+ jobjectArray stateInfoArray =
+ env->NewObjectArray(infos[i].states.size(), class_SI, nullptr);
+ for (int j = 0; j < infos[i].states.size(); j++) {
+ jstring powerEntityStateName = env->NewStringUTF(
+ infos[i].states[j].powerEntityStateName.c_str());
+ jobject stateInfo = env->NewObject(class_SI, method_SI_init);
+ env->SetIntField(stateInfo, field_SI_stateId,
+ infos[i].states[j].powerEntityStateId);
+ env->SetObjectField(stateInfo, field_SI_stateName,
+ powerEntityStateName);
+ env->SetObjectArrayElement(stateInfoArray, j, stateInfo);
+ env->DeleteLocalRef(powerEntityStateName);
+ env->DeleteLocalRef(stateInfo);
+ }
+
+ for (int j = 0; j < env->GetArrayLength(powerEntityInfoArray); j++) {
+ jobject powerEntityInfo =
+ env->GetObjectArrayElement(powerEntityInfoArray, j);
+ if (env->GetIntField(powerEntityInfo, field_PEI_powerEntityId) ==
+ infos[i].powerEntityId) {
+ env->SetObjectField(powerEntityInfo, field_PEI_states,
+ stateInfoArray);
+ env->SetObjectArrayElement(powerEntityInfoArray, j,
+ powerEntityInfo);
+ break;
+ }
+ }
+ }
+ }
+ });
+ if (!checkResult(ret, __func__)) {
+ return nullptr;
+ }
+
+ return powerEntityInfoArray;
+}
+
+static jobjectArray nativeGetStateResidency(JNIEnv *env, jclass clazz, jintArray powerEntityIds) {
+ std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
+
+ if (!connectToPowerStatsHal()) {
+ ALOGE("nativeGetStateResidency failed to connect to power.stats HAL");
+ return nullptr;
+ }
+
+ size_t powerEntityIdCount = env->GetArrayLength(powerEntityIds);
+ hidl_vec<uint32_t> powerEntityIdVector(powerEntityIdCount);
+
+ jint *powerEntityIdElements = env->GetIntArrayElements(powerEntityIds, 0);
+ for (int i = 0; i < powerEntityIdCount; i++) {
+ powerEntityIdVector[i] = powerEntityIdElements[i];
+ }
+ env->ReleaseIntArrayElements(powerEntityIds, powerEntityIdElements, 0);
+
+ jobjectArray stateResidencyResultArray = nullptr;
+ Return<void> ret = gPowerStatsHalV1_0_ptr->getPowerEntityStateResidencyData(
+ powerEntityIdVector, [&env, &stateResidencyResultArray](auto results, auto status) {
+ if (status != Status::SUCCESS) {
+ ALOGE("Error getting power entity state residency data");
+ } else {
+ stateResidencyResultArray =
+ env->NewObjectArray(results.size(), class_SRR, nullptr);
+ for (int i = 0; i < results.size(); i++) {
+ jobjectArray stateResidencyArray =
+ env->NewObjectArray(results[i].stateResidencyData.size(), class_SR,
+ nullptr);
+ for (int j = 0; j < results[i].stateResidencyData.size(); j++) {
+ jobject stateResidency = env->NewObject(class_SR, method_SR_init);
+ env->SetIntField(stateResidency, field_SR_stateId,
+ results[i].stateResidencyData[j].powerEntityStateId);
+ env->SetLongField(stateResidency, field_SR_totalTimeInStateMs,
+ results[i].stateResidencyData[j].totalTimeInStateMs);
+ env->SetLongField(stateResidency, field_SR_totalStateEntryCount,
+ results[i]
+ .stateResidencyData[j]
+ .totalStateEntryCount);
+ env->SetLongField(stateResidency, field_SR_lastEntryTimestampMs,
+ results[i]
+ .stateResidencyData[j]
+ .lastEntryTimestampMs);
+ env->SetObjectArrayElement(stateResidencyArray, j, stateResidency);
+ env->DeleteLocalRef(stateResidency);
+ }
+ jobject stateResidencyResult = env->NewObject(class_SRR, method_SRR_init);
+ env->SetIntField(stateResidencyResult, field_SRR_powerEntityId,
+ results[i].powerEntityId);
+ env->SetObjectField(stateResidencyResult, field_SRR_stateResidencyData,
+ stateResidencyArray);
+ env->SetObjectArrayElement(stateResidencyResultArray, i,
+ stateResidencyResult);
+ env->DeleteLocalRef(stateResidencyResult);
+ }
+ }
+ });
+ if (!checkResult(ret, __func__)) {
+ return nullptr;
+ }
+
+ return stateResidencyResultArray;
+}
+
+static jobjectArray nativeGetEnergyMeterInfo(JNIEnv *env, jclass clazz) {
+ std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
+
+ if (!connectToPowerStatsHal()) {
+ ALOGE("nativeGetEnergyMeterInfo failed to connect to power.stats HAL");
+ return nullptr;
+ }
+
+ jobjectArray channelInfoArray = nullptr;
+ Return<void> ret = gPowerStatsHalV1_0_ptr->getRailInfo(
+ [&env, &channelInfoArray](auto railInfo, auto status) {
+ if (status != Status::SUCCESS) {
+ ALOGW("Error getting rail info");
+ } else {
+ channelInfoArray = env->NewObjectArray(railInfo.size(), class_CI, nullptr);
+ for (int i = 0; i < railInfo.size(); i++) {
+ jstring channelName = env->NewStringUTF(railInfo[i].railName.c_str());
+ jobject channelInfo = env->NewObject(class_CI, method_CI_init);
+ env->SetIntField(channelInfo, field_CI_channelId, railInfo[i].index);
+ env->SetObjectField(channelInfo, field_CI_channelName, channelName);
+ env->SetObjectArrayElement(channelInfoArray, i, channelInfo);
+ env->DeleteLocalRef(channelName);
+ env->DeleteLocalRef(channelInfo);
+ }
+ }
+ });
+
+ if (!checkResult(ret, __func__)) {
+ ALOGE("getRailInfo failed");
+ return nullptr;
+ }
+
+ return channelInfoArray;
+}
+
+static jobjectArray nativeReadEnergyMeters(JNIEnv *env, jclass clazz, jintArray channelIds) {
+ std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
+
+ if (!connectToPowerStatsHal()) {
+ ALOGE("nativeGetEnergy failed to connect to power.stats HAL");
+ }
+
+ size_t channelIdCount = env->GetArrayLength(channelIds);
+ hidl_vec<uint32_t> channelIdVector(channelIdCount);
+
+ jint *channelIdElements = env->GetIntArrayElements(channelIds, 0);
+ for (int i = 0; i < channelIdCount; i++) {
+ channelIdVector[i] = channelIdElements[i];
+ }
+ env->ReleaseIntArrayElements(channelIds, channelIdElements, 0);
+
+ jobjectArray energyMeasurementArray = nullptr;
+ Return<void> ret =
+ gPowerStatsHalV1_0_ptr
+ ->getEnergyData(channelIdVector,
+ [&env, &energyMeasurementArray](auto energyData, auto status) {
+ if (status != Status::SUCCESS) {
+ ALOGW("Error getting energy data");
+ } else {
+ energyMeasurementArray =
+ env->NewObjectArray(energyData.size(), class_EM,
+ nullptr);
+ for (int i = 0; i < energyData.size(); i++) {
+ jobject energyMeasurement =
+ env->NewObject(class_EM, method_EM_init);
+ env->SetIntField(energyMeasurement,
+ field_EM_channelId,
+ energyData[i].index);
+ env->SetLongField(energyMeasurement,
+ field_EM_timestampMs,
+ energyData[i].timestamp);
+ env->SetLongField(energyMeasurement,
+ field_EM_durationMs, -1);
+ env->SetLongField(energyMeasurement,
+ field_EM_energyUWs,
+ energyData[i].energy);
+ env->SetObjectArrayElement(energyMeasurementArray,
+ i, energyMeasurement);
+ env->DeleteLocalRef(energyMeasurement);
+ }
+ }
+ });
+
+ if (!checkResult(ret, __func__)) {
+ ALOGE("getEnergyData failed");
+ return nullptr;
+ }
+
+ return energyMeasurementArray;
+}
+
+static jboolean nativeInit(JNIEnv *env, jclass clazz) {
+ std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
+
+ // ChannelInfo
+ jclass temp = env->FindClass("android/hardware/power/stats/ChannelInfo");
+ class_CI = (jclass)env->NewGlobalRef(temp);
+ method_CI_init = env->GetMethodID(class_CI, "<init>", "()V");
+ field_CI_channelId = env->GetFieldID(class_CI, "channelId", "I");
+ field_CI_channelName = env->GetFieldID(class_CI, "channelName", "Ljava/lang/String;");
+
+ // EnergyMeasurement
+ temp = env->FindClass("android/hardware/power/stats/EnergyMeasurement");
+ class_EM = (jclass)env->NewGlobalRef(temp);
+ method_EM_init = env->GetMethodID(class_EM, "<init>", "()V");
+ field_EM_channelId = env->GetFieldID(class_EM, "channelId", "I");
+ field_EM_timestampMs = env->GetFieldID(class_EM, "timestampMs", "J");
+ field_EM_durationMs = env->GetFieldID(class_EM, "durationMs", "J");
+ field_EM_energyUWs = env->GetFieldID(class_EM, "energyUWs", "J");
+
+ // StateInfo
+ temp = env->FindClass("android/hardware/power/stats/StateInfo");
+ class_SI = (jclass)env->NewGlobalRef(temp);
+ method_SI_init = env->GetMethodID(class_SI, "<init>", "()V");
+ field_SI_stateId = env->GetFieldID(class_SI, "stateId", "I");
+ field_SI_stateName = env->GetFieldID(class_SI, "stateName", "Ljava/lang/String;");
+
+ // PowerEntityInfo
+ temp = env->FindClass("android/hardware/power/stats/PowerEntityInfo");
+ class_PEI = (jclass)env->NewGlobalRef(temp);
+ method_PEI_init = env->GetMethodID(class_PEI, "<init>", "()V");
+ field_PEI_powerEntityId = env->GetFieldID(class_PEI, "powerEntityId", "I");
+ field_PEI_powerEntityName = env->GetFieldID(class_PEI, "powerEntityName", "Ljava/lang/String;");
+ field_PEI_states =
+ env->GetFieldID(class_PEI, "states", "[Landroid/hardware/power/stats/StateInfo;");
+
+ // StateResidency
+ temp = env->FindClass("android/hardware/power/stats/StateResidency");
+ class_SR = (jclass)env->NewGlobalRef(temp);
+ method_SR_init = env->GetMethodID(class_SR, "<init>", "()V");
+ field_SR_stateId = env->GetFieldID(class_SR, "stateId", "I");
+ field_SR_totalTimeInStateMs = env->GetFieldID(class_SR, "totalTimeInStateMs", "J");
+ field_SR_totalStateEntryCount = env->GetFieldID(class_SR, "totalStateEntryCount", "J");
+ field_SR_lastEntryTimestampMs = env->GetFieldID(class_SR, "lastEntryTimestampMs", "J");
+
+ // StateResidencyResult
+ temp = env->FindClass("android/hardware/power/stats/StateResidencyResult");
+ class_SRR = (jclass)env->NewGlobalRef(temp);
+ method_SRR_init = env->GetMethodID(class_SRR, "<init>", "()V");
+ field_SRR_powerEntityId = env->GetFieldID(class_SRR, "powerEntityId", "I");
+ field_SRR_stateResidencyData =
+ env->GetFieldID(class_SRR, "stateResidencyData",
+ "[Landroid/hardware/power/stats/StateResidency;");
+
+ if (!connectToPowerStatsHal()) {
+ ALOGE("nativeInit failed to connect to power.stats HAL");
+ return false;
+ }
+
+ return true;
+}
+
+static const JNINativeMethod method_table[] = {
+ {"nativeInit", "()Z", (void *)nativeInit},
+ {"nativeGetPowerEntityInfo", "()[Landroid/hardware/power/stats/PowerEntityInfo;",
+ (void *)nativeGetPowerEntityInfo},
+ {"nativeGetStateResidency", "([I)[Landroid/hardware/power/stats/StateResidencyResult;",
+ (void *)nativeGetStateResidency},
+ {"nativeGetEnergyMeterInfo", "()[Landroid/hardware/power/stats/ChannelInfo;",
+ (void *)nativeGetEnergyMeterInfo},
+ {"nativeReadEnergyMeters", "([I)[Landroid/hardware/power/stats/EnergyMeasurement;",
+ (void *)nativeReadEnergyMeters},
+};
+
+int register_android_server_PowerStatsService(JNIEnv *env) {
+ return jniRegisterNativeMethods(env,
+ "com/android/server/powerstats/"
+ "PowerStatsHALWrapper$PowerStatsHAL10WrapperImpl",
+ method_table, NELEM(method_table));
+}
+
+}; // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 5a0d08aeb6b3..85ef39470cb3 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -29,6 +29,7 @@ int register_android_server_ConsumerIrService(JNIEnv *env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
+int register_android_server_PowerStatsService(JNIEnv* env);
int register_android_server_storage_AppFuse(JNIEnv* env);
int register_android_server_SerialService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
@@ -82,6 +83,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_broadcastradio_BroadcastRadioService(env);
register_android_server_broadcastradio_Tuner(vm, env);
register_android_server_PowerManagerService(env);
+ register_android_server_PowerStatsService(env);
register_android_server_SerialService(env);
register_android_server_InputManager(env);
register_android_server_LightsService(env);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f2c65e230ec2..0c00e3d63f88 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -11766,13 +11766,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
- public ComponentName getProfileOwnerAsUser(@UserIdInt int userId) {
- return DevicePolicyManagerService.this.getProfileOwnerAsUser(userId);
- }
-
- @Override
- public int getDeviceOwnerUserId() {
- return DevicePolicyManagerService.this.getDeviceOwnerUserId();
+ public ComponentName getProfileOwnerAsUser(int userHandle) {
+ return DevicePolicyManagerService.this.getProfileOwnerAsUser(userHandle);
}
@Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
index 9f895c678dd0..99693d28f378 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
@@ -36,7 +36,6 @@ import android.os.HandlerThread;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedXmlPullParser;
-import android.util.TypedXmlSerializer;
import android.util.Xml;
import androidx.test.InstrumentationRegistry;
@@ -53,7 +52,6 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
/**
* Tests app ops version upgrades
@@ -183,8 +181,7 @@ public class AppOpsUpgradeTest {
boolean parse() {
try (FileInputStream stream = new FileInputStream(mFile)) {
- TypedXmlPullParser parser = Xml.newFastPullParser();
- parser.setInput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(stream);
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS b/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS
new file mode 100644
index 000000000000..d825dfd7cf00
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/pm/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/audio/OWNERS b/services/tests/servicestests/src/com/android/server/audio/OWNERS
new file mode 100644
index 000000000000..894a1f5c25d0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/audio/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index e5883708e921..8c63bfcf1407 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -190,6 +190,22 @@ public class CompatConfigTest {
}
@Test
+ public void testIsChangeEnabledForInvalidApp() throws Exception {
+ final long disabledChangeId = 1234L;
+ final long enabledChangeId = 1235L;
+ final long targetSdkChangeId = 1236L;
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addEnabledChangeWithId(enabledChangeId)
+ .addDisabledChangeWithId(disabledChangeId)
+ .addEnableSinceSdkChangeWithId(42, targetSdkChangeId)
+ .build();
+
+ assertThat(compatConfig.isChangeEnabled(enabledChangeId, null)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId, null)).isFalse();
+ assertThat(compatConfig.isChangeEnabled(targetSdkChangeId, null)).isTrue();
+ }
+
+ @Test
public void testPreventAddOverride() throws Exception {
final long changeId = 1234L;
CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index b26d1efef2a8..08d4caacd777 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -209,7 +209,7 @@ public class PowerStatsServiceTest {
}
@Override
- public boolean initialize() {
+ public boolean isInitialized() {
return true;
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
index 1198ee2ba5f4..5597be93c1f1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
import static android.window.DisplayAreaOrganizer.FEATURE_RUNTIME_TASK_CONTAINER_FIRST;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
@@ -117,7 +118,7 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase {
}
@Test
- public void testCreateTaskDisplayArea() {
+ public void testCreateTaskDisplayArea_topBelowRoot() {
final String newTdaName = "testTda";
final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder());
final DisplayAreaAppearedInfo tdaInfo = mOrganizerController.createTaskDisplayArea(
@@ -142,6 +143,30 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase {
}
@Test
+ public void testCreateTaskDisplayArea_topBelowAnotherTaskDisplayArea() {
+ final String newTdaName = "testTda";
+ final TaskDisplayArea parentTda = mDisplayContent.getDefaultTaskDisplayArea();
+ final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder());
+ final DisplayAreaAppearedInfo tdaInfo = mOrganizerController.createTaskDisplayArea(
+ organizer, DEFAULT_DISPLAY, FEATURE_DEFAULT_TASK_CONTAINER, newTdaName);
+
+ final WindowContainer wc = parentTda.getChildAt(parentTda.getChildCount() - 1);
+
+ // A new TaskDisplayArea is created on the top.
+ assertThat(wc).isInstanceOf(TaskDisplayArea.class);
+ assertThat(tdaInfo.getDisplayAreaInfo().displayId).isEqualTo(DEFAULT_DISPLAY);
+ assertThat(tdaInfo.getDisplayAreaInfo().token)
+ .isEqualTo(wc.mRemoteToken.toWindowContainerToken());
+
+ final TaskDisplayArea tda = wc.asTaskDisplayArea();
+
+ assertThat(tda.getName()).isEqualTo(newTdaName);
+ assertThat(tda.mFeatureId).isEqualTo(tdaInfo.getDisplayAreaInfo().featureId);
+ assertThat(tda.mCreatedByOrganizer).isTrue();
+ assertThat(tda.mOrganizer).isEqualTo(organizer);
+ }
+
+ @Test
public void testCreateTaskDisplayArea_incrementalTdaFeatureId() {
final String newTdaName = "testTda";
final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder());
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index d645e86d3f37..56345c032670 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2050,7 +2050,13 @@ public class CarrierConfigManager {
* via {@link android.telecom.PhoneAccount#CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE}
* and can choose to hide or show the video calling icon based on whether a contact supports
* video.
+ *
+ * @deprecated No longer used in framework code, however it may still be used by applications
+ * that have not updated their code. This config should still be set to {@code true} if
+ * {@link Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is set to {@code true} and
+ * {@link Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL} is set to {@code true}.
*/
+ @Deprecated
public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
/**
@@ -3866,13 +3872,51 @@ public class CarrierConfigManager {
* <p>
* If this key's value is set to false, the procedure for RCS contact capability exchange
* via SIP SUBSCRIBE/NOTIFY will also be disabled internally, and
- * {@link #KEY_USE_RCS_PRESENCE_BOOL} must also be set to false to ensure apps do not
- * improperly think that capability exchange via SIP PUBLISH is enabled.
+ * {@link Ims#KEY_ENABLE_PRESENCE_PUBLISH_BOOL} must also be set to false to ensure
+ * apps do not improperly think that capability exchange via SIP PUBLISH is enabled.
* <p> The default value for this key is {@code false}.
*/
public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL =
KEY_PREFIX + "enable_presence_publish_bool";
+ /**
+ * Flag indicating whether or not this carrier supports the exchange of phone numbers with
+ * the carrier's RCS presence server in order to retrieve the RCS capabilities of requested
+ * contacts used in the RCS User Capability Exchange (UCE) procedure. See RCC.71, section 3
+ * for more information.
+ * <p>
+ * When presence is supported, the device uses the SIP SUBSCRIBE/NOTIFY procedure internally
+ * to retrieve the requested RCS capabilities. See
+ * {@link android.telephony.ims.RcsUceAdapter} for more information on how RCS capabilities
+ * can be retrieved from the carrier's network.
+ */
+ public static final String KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL =
+ KEY_PREFIX + "enable_presence_capability_exchange_bool";
+
+
+ /**
+ * Flag indicating whether or not the carrier expects the RCS UCE service to periodically
+ * refresh the RCS capabilities cache of the user's contacts as well as request the
+ * capabilities of call contacts when the SIM card is first inserted or when a new contact
+ * is added, removed, or modified. This corresponds to the RCC.07 A.19
+ * "DISABLE INITIAL ADDRESS BOOK SCAN" parameter.
+ * <p>
+ * If this flag is disabled, the capabilities cache will not be refreshed internally at all
+ * and will only be updated if the cached capabilities are stale when an application
+ * requests them.
+ */
+ public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL =
+ KEY_PREFIX + "rcs_bulk_capability_exchange_bool";
+
+ /**
+ * Flag indicating whether or not the carrier supports capability exchange with a list of
+ * contacts. When {@code true}, the device will batch together multiple requests and
+ * construct a RLMI document in the SIP SUBSCRIBE request (see RFC 4662). If {@code false},
+ * the request will be split up into one SIP SUBSCRIBE request per contact.
+ */
+ public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL =
+ KEY_PREFIX + "enable_presence_group_subscribe_bool";
+
private Ims() {}
private static PersistableBundle getDefaults() {
@@ -3880,6 +3924,9 @@ public class CarrierConfigManager {
defaults.putInt(KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT, 4000);
defaults.putBoolean(KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false);
defaults.putBoolean(KEY_ENABLE_PRESENCE_PUBLISH_BOOL, false);
+ defaults.putBoolean(KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL, false);
+ defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false);
+ defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true);
return defaults;
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 31a12493b33c..d4565f32641a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -8671,6 +8671,77 @@ public class TelephonyManager {
return Collections.EMPTY_LIST;
}
+ /**
+ * Call composer status OFF from user setting.
+ */
+ public static final int CALL_COMPOSER_STATUS_OFF = 0;
+
+ /**
+ * Call composer status ON from user setting.
+ */
+ public static final int CALL_COMPOSER_STATUS_ON = 1;
+
+ /** @hide */
+ @IntDef(prefix = {"CALL_COMPOSER_STATUS_"},
+ value = {
+ CALL_COMPOSER_STATUS_ON,
+ CALL_COMPOSER_STATUS_OFF,
+ })
+ public @interface CallComposerStatus {}
+
+ /**
+ * Set the user-set status for enriched calling with call composer.
+ *
+ * @param status user-set status for enriched calling with call composer;
+ * it must be a value of either {@link #CALL_COMPOSER_STATUS_ON}
+ * or {@link #CALL_COMPOSER_STATUS_OFF}.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+ *
+ * @throws IllegalArgumentException if requested state is invalid.
+ * @throws SecurityException if the caller does not have the permission.
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setCallComposerStatus(@CallComposerStatus int status) {
+ if (status != CALL_COMPOSER_STATUS_ON && status != CALL_COMPOSER_STATUS_OFF) {
+ throw new IllegalArgumentException("requested status is invalid");
+ }
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.setCallComposerStatus(getSubId(), status);
+ }
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Error calling ITelephony#setCallComposerStatus", ex);
+ ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the user-set status for enriched calling with call composer.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+ *
+ * @throws SecurityException if the caller does not have the permission.
+ *
+ * @return the user-set status for enriched calling with call composer either
+ * {@link #CALL_COMPOSER_STATUS_ON} or {@link #CALL_COMPOSER_STATUS_OFF}.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @CallComposerStatus int getCallComposerStatus() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getCallComposerStatus(getSubId());
+ }
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Error calling ITelephony#getCallComposerStatus", ex);
+ ex.rethrowFromSystemServer();
+ }
+ return CALL_COMPOSER_STATUS_OFF;
+ }
/** @hide */
@SystemApi
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 885ff9be550c..ad461c091493 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -30,7 +30,6 @@ import android.os.ServiceSpecificException;
import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
import android.telephony.BinderCacheManager;
-import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsRcsController;
@@ -62,9 +61,10 @@ public class ImsRcsManager {
* been enabled by the user can be queried using {@link RcsUceAdapter#isUceSettingEnabled()}.
* <p>
* This intent will always be handled by the system, however the application should only send
- * this Intent if the carrier supports RCS contact discovery, which can be queried using the key
- * {@link CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL}. Otherwise, the RCS contact discovery
- * opt-in dialog will not be shown.
+ * this Intent if the carrier supports bulk RCS contact exchange, which will be true if either
+ * key {@link android.telephony.CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL}
+ * or {@link android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL} is set to true.
+ * Otherwise, the RCS contact discovery opt-in dialog will not be shown.
* <p>
* Input: A mandatory {@link Settings#EXTRA_SUB_ID} extra containing the subscription that the
* setting will be be shown for.
@@ -396,6 +396,7 @@ public class ImsRcsManager {
* rather the subscription is capable of this service over IMS.
* @see #isAvailable(int)
* @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL
+ * @see android.telephony.CarrierConfigManager.Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL
* @throws ImsException if the IMS service is not available when calling this method.
* See {@link ImsException#getCode()} for more information on the error codes.
* @hide
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index ff240a0302b2..de4da3829e95 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -128,6 +128,15 @@ interface ITelephony {
*/
boolean isRadioOnForSubscriberWithFeature(int subId, String callingPackage, String callingFeatureId);
+ /**
+ * Set the user-set status for enriched calling with call composer.
+ */
+ void setCallComposerStatus(int subId, int status);
+
+ /**
+ * Get the user-set status for enriched calling with call composer.
+ */
+ int getCallComposerStatus(int subId);
/**
* Supply a pin to unlock the SIM for particular subId.
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index cf3b03cae72e..f7cebd12fcff 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -817,6 +817,11 @@ public class MockContext extends Context {
}
@Override
+ public @NonNull Context createWindowContext(Display display, int type, Bundle options) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public boolean isRestricted() {
throw new UnsupportedOperationException();
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 20b05b54f95d..799b64e7d63b 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -8109,7 +8109,16 @@ public class ConnectivityServiceTest {
@Test
public void testDumpDoesNotCrash() {
- StringWriter stringWriter = new StringWriter();
+ // Filing a couple requests prior to testing the dump.
+ final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback();
+ final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest genericRequest = new NetworkRequest.Builder()
+ .clearCapabilities().build();
+ final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+ mCm.registerNetworkCallback(genericRequest, genericNetworkCallback);
+ mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
+ final StringWriter stringWriter = new StringWriter();
mService.dump(new FileDescriptor(), new PrintWriter(stringWriter), new String[0]);
@@ -8131,11 +8140,11 @@ public class ConnectivityServiceTest {
mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
- ConnectivityService.NetworkRequestInfo[] nriOutput = mService.requestsSortedById();
+ final ConnectivityService.NetworkRequestInfo[] nriOutput = mService.requestsSortedById();
assertTrue(nriOutput.length > 1);
for (int i = 0; i < nriOutput.length - 1; i++) {
- boolean isRequestIdInOrder =
+ final boolean isRequestIdInOrder =
nriOutput[i].mRequests.get(0).requestId
< nriOutput[i + 1].mRequests.get(0).requestId;
assertTrue(isRequestIdInOrder);
diff --git a/tests/vcn/java/android/net/vcn/VcnConfigTest.java b/tests/vcn/java/android/net/vcn/VcnConfigTest.java
new file mode 100644
index 000000000000..77944deb26f1
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnConfigTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 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.vcn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnConfigTest {
+ private static final Set<VcnGatewayConnectionConfig> GATEWAY_CONNECTION_CONFIGS =
+ Collections.singleton(VcnGatewayConnectionConfigTest.buildTestConfig());
+
+ // Public visibility for VcnManagementServiceTest
+ public static VcnConfig buildTestConfig() {
+ VcnConfig.Builder builder = new VcnConfig.Builder();
+
+ for (VcnGatewayConnectionConfig gatewayConnectionConfig : GATEWAY_CONNECTION_CONFIGS) {
+ builder.addGatewayConnectionConfig(gatewayConnectionConfig);
+ }
+
+ return builder.build();
+ }
+
+ @Test
+ public void testBuilderRequiresGatewayConnectionConfig() {
+ try {
+ new VcnConfig.Builder().build();
+ fail("Expected exception due to no VcnGatewayConnectionConfigs provided");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testBuilderAndGetters() {
+ final VcnConfig config = buildTestConfig();
+
+ assertEquals(GATEWAY_CONNECTION_CONFIGS, config.getGatewayConnectionConfigs());
+ }
+
+ @Test
+ public void testPersistableBundle() {
+ final VcnConfig config = buildTestConfig();
+
+ assertEquals(config, new VcnConfig(config.toPersistableBundle()));
+ }
+
+ @Test
+ public void testParceling() {
+ final VcnConfig config = buildTestConfig();
+
+ Parcel parcel = Parcel.obtain();
+ config.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ assertEquals(config, VcnConfig.CREATOR.createFromParcel(parcel));
+ }
+}
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
new file mode 100644
index 000000000000..e98b6ef2b3a6
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2020 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.vcn;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.net.NetworkCapabilities;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionConfigTest {
+ private static final int[] EXPOSED_CAPS =
+ new int[] {
+ NetworkCapabilities.NET_CAPABILITY_INTERNET, NetworkCapabilities.NET_CAPABILITY_MMS
+ };
+ private static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN};
+ private static final long[] RETRY_INTERVALS_MS =
+ new long[] {
+ TimeUnit.SECONDS.toMillis(5),
+ TimeUnit.SECONDS.toMillis(30),
+ TimeUnit.MINUTES.toMillis(1),
+ TimeUnit.MINUTES.toMillis(5),
+ TimeUnit.MINUTES.toMillis(15),
+ TimeUnit.MINUTES.toMillis(30)
+ };
+ private static final int MAX_MTU = 1360;
+
+ // Package protected for use in VcnConfigTest
+ static VcnGatewayConnectionConfig buildTestConfig() {
+ final VcnGatewayConnectionConfig.Builder builder =
+ new VcnGatewayConnectionConfig.Builder()
+ .setRetryInterval(RETRY_INTERVALS_MS)
+ .setMaxMtu(MAX_MTU);
+
+ for (int caps : EXPOSED_CAPS) {
+ builder.addExposedCapability(caps);
+ }
+
+ for (int caps : UNDERLYING_CAPS) {
+ builder.addRequiredUnderlyingCapability(caps);
+ }
+
+ return builder.build();
+ }
+
+ @Test
+ public void testBuilderRequiresNonEmptyExposedCaps() {
+ try {
+ new VcnGatewayConnectionConfig.Builder()
+ .addRequiredUnderlyingCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build();
+
+ fail("Expected exception due to invalid exposed capabilities");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testBuilderRequiresNonEmptyUnderlyingCaps() {
+ try {
+ new VcnGatewayConnectionConfig.Builder()
+ .addExposedCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build();
+
+ fail("Expected exception due to invalid required underlying capabilities");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testBuilderRequiresNonNullRetryInterval() {
+ try {
+ new VcnGatewayConnectionConfig.Builder().setRetryInterval(null);
+ fail("Expected exception due to invalid retryIntervalMs");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testBuilderRequiresNonEmptyRetryInterval() {
+ try {
+ new VcnGatewayConnectionConfig.Builder().setRetryInterval(new long[0]);
+ fail("Expected exception due to invalid retryIntervalMs");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testBuilderRequiresValidMtu() {
+ try {
+ new VcnGatewayConnectionConfig.Builder()
+ .setMaxMtu(VcnGatewayConnectionConfig.MIN_MTU_V6 - 1);
+ fail("Expected exception due to invalid mtu");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testBuilderAndGetters() {
+ final VcnGatewayConnectionConfig config = buildTestConfig();
+
+ for (int cap : EXPOSED_CAPS) {
+ config.hasExposedCapability(cap);
+ }
+ for (int cap : UNDERLYING_CAPS) {
+ config.requiresUnderlyingCapability(cap);
+ }
+
+ assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMs());
+ assertEquals(MAX_MTU, config.getMaxMtu());
+ }
+
+ @Test
+ public void testPersistableBundle() {
+ final VcnGatewayConnectionConfig config = buildTestConfig();
+
+ assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle()));
+ }
+}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index c91fdbffd760..1cc953239fed 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -16,42 +16,123 @@
package com.android.server;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnConfigTest;
+import android.os.ParcelUuid;
+import android.os.PersistableBundle;
+import android.os.Process;
+import android.os.UserHandle;
import android.os.test.TestLooper;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.FileNotFoundException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.UUID;
+
/** Tests for {@link VcnManagementService}. */
@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnManagementServiceTest {
+ private static final ParcelUuid TEST_UUID_1 = new ParcelUuid(new UUID(0, 0));
+ private static final ParcelUuid TEST_UUID_2 = new ParcelUuid(new UUID(1, 1));
+ private static final VcnConfig TEST_VCN_CONFIG = VcnConfigTest.buildTestConfig();
+ private static final Map<ParcelUuid, VcnConfig> TEST_VCN_CONFIG_MAP =
+ Collections.unmodifiableMap(Collections.singletonMap(TEST_UUID_1, TEST_VCN_CONFIG));
+
+ private static final SubscriptionInfo TEST_SUBSCRIPTION_INFO =
+ new SubscriptionInfo(
+ 1 /* id */,
+ "" /* iccId */,
+ 0 /* simSlotIndex */,
+ "Carrier" /* displayName */,
+ "Carrier" /* carrierName */,
+ 0 /* nameSource */,
+ 255 /* iconTint */,
+ "12345" /* number */,
+ 0 /* roaming */,
+ null /* icon */,
+ "0" /* mcc */,
+ "0" /* mnc */,
+ "0" /* countryIso */,
+ false /* isEmbedded */,
+ null /* nativeAccessRules */,
+ null /* cardString */,
+ false /* isOpportunistic */,
+ TEST_UUID_1.toString() /* groupUUID */,
+ 0 /* carrierId */,
+ 0 /* profileClass */);
+
private final Context mMockContext = mock(Context.class);
private final VcnManagementService.Dependencies mMockDeps =
mock(VcnManagementService.Dependencies.class);
private final TestLooper mTestLooper = new TestLooper();
private final ConnectivityManager mConnMgr = mock(ConnectivityManager.class);
+ private final TelephonyManager mTelMgr = mock(TelephonyManager.class);
+ private final SubscriptionManager mSubMgr = mock(SubscriptionManager.class);
private final VcnManagementService mVcnMgmtSvc;
+ private final PersistableBundleUtils.LockingReadWriteHelper mConfigReadWriteHelper =
+ mock(PersistableBundleUtils.LockingReadWriteHelper.class);
- public VcnManagementServiceTest() {
- doReturn(Context.CONNECTIVITY_SERVICE)
- .when(mMockContext)
- .getSystemServiceName(ConnectivityManager.class);
- doReturn(mConnMgr).when(mMockContext).getSystemService(Context.CONNECTIVITY_SERVICE);
+ public VcnManagementServiceTest() throws Exception {
+ setupSystemService(mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+ setupSystemService(mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class);
+ setupSystemService(
+ mSubMgr, Context.TELEPHONY_SUBSCRIPTION_SERVICE, SubscriptionManager.class);
doReturn(mTestLooper.getLooper()).when(mMockDeps).getLooper();
+ doReturn(Process.FIRST_APPLICATION_UID).when(mMockDeps).getBinderCallingUid();
+ doReturn(mConfigReadWriteHelper)
+ .when(mMockDeps)
+ .newPersistableBundleLockingReadWriteHelper(any());
+
+ final PersistableBundle bundle =
+ PersistableBundleUtils.fromMap(
+ TEST_VCN_CONFIG_MAP,
+ PersistableBundleUtils::fromParcelUuid,
+ VcnConfig::toPersistableBundle);
+ doReturn(bundle).when(mConfigReadWriteHelper).readFromDisk();
+
+ setupMockedCarrierPrivilege(true);
mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps);
}
+ private void setupSystemService(Object service, String name, Class<?> serviceClass) {
+ doReturn(name).when(mMockContext).getSystemServiceName(serviceClass);
+ doReturn(service).when(mMockContext).getSystemService(name);
+ }
+
+ private void setupMockedCarrierPrivilege(boolean isPrivileged) {
+ doReturn(Collections.singletonList(TEST_SUBSCRIPTION_INFO))
+ .when(mSubMgr)
+ .getSubscriptionsInGroup(any());
+ doReturn(isPrivileged)
+ .when(mTelMgr)
+ .hasCarrierPrivileges(eq(TEST_SUBSCRIPTION_INFO.getSubscriptionId()));
+ }
+
@Test
public void testSystemReady() throws Exception {
mVcnMgmtSvc.systemReady();
@@ -59,4 +140,119 @@ public class VcnManagementServiceTest {
verify(mConnMgr)
.registerNetworkProvider(any(VcnManagementService.VcnNetworkProvider.class));
}
+
+ @Test
+ public void testNonSystemServerRealConfigFileAccessPermission() throws Exception {
+ // Attempt to build a real instance of the dependencies, and verify we cannot write to the
+ // file.
+ VcnManagementService.Dependencies deps = new VcnManagementService.Dependencies();
+ PersistableBundleUtils.LockingReadWriteHelper configReadWriteHelper =
+ deps.newPersistableBundleLockingReadWriteHelper(
+ VcnManagementService.VCN_CONFIG_FILE);
+
+ // Even tests should not be able to read/write configs from disk; SELinux policies restrict
+ // it to only the system server.
+ // Reading config should always return null since the file "does not exist", and writing
+ // should throw an IOException.
+ assertNull(configReadWriteHelper.readFromDisk());
+
+ try {
+ configReadWriteHelper.writeToDisk(new PersistableBundle());
+ fail("Expected IOException due to SELinux policy");
+ } catch (FileNotFoundException expected) {
+ }
+ }
+
+ @Test
+ public void testLoadVcnConfigsOnStartup() throws Exception {
+ mTestLooper.dispatchAll();
+
+ assertEquals(TEST_VCN_CONFIG_MAP, mVcnMgmtSvc.getConfigs());
+ verify(mConfigReadWriteHelper).readFromDisk();
+ }
+
+ @Test
+ public void testSetVcnConfigRequiresNonSystemServer() throws Exception {
+ doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid();
+
+ try {
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig());
+ fail("Expected IllegalStateException exception for system server");
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ @Test
+ public void testSetVcnConfigRequiresSystemUser() throws Exception {
+ doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, Process.FIRST_APPLICATION_UID))
+ .when(mMockDeps)
+ .getBinderCallingUid();
+
+ try {
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig());
+ fail("Expected security exception for non system user");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testSetVcnConfigRequiresCarrierPrivileges() throws Exception {
+ setupMockedCarrierPrivilege(false);
+
+ try {
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig());
+ fail("Expected security exception for missing carrier privileges");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testSetVcnConfig() throws Exception {
+ // Use a different UUID to simulate a new VCN config.
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG);
+ assertEquals(TEST_VCN_CONFIG, mVcnMgmtSvc.getConfigs().get(TEST_UUID_2));
+ verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
+ }
+
+ @Test
+ public void testClearVcnConfigRequiresNonSystemServer() throws Exception {
+ doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid();
+
+ try {
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1);
+ fail("Expected IllegalStateException exception for system server");
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ @Test
+ public void testClearVcnConfigRequiresSystemUser() throws Exception {
+ doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, Process.FIRST_APPLICATION_UID))
+ .when(mMockDeps)
+ .getBinderCallingUid();
+
+ try {
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1);
+ fail("Expected security exception for non system user");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testClearVcnConfigRequiresCarrierPrivileges() throws Exception {
+ setupMockedCarrierPrivilege(false);
+
+ try {
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1);
+ fail("Expected security exception for missing carrier privileges");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testClearVcnConfig() throws Exception {
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1);
+ assertTrue(mVcnMgmtSvc.getConfigs().isEmpty());
+ verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
new file mode 100644
index 000000000000..17b8f64a13fa
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2020 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.vcn;
+
+import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
+import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX;
+import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX;
+import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.ParcelUuid;
+import android.os.test.TestLooper;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.util.ArraySet;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+/** Tests for TelephonySubscriptionTracker */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TelephonySubscriptionTrackerTest {
+ private static final ParcelUuid TEST_PARCEL_UUID = new ParcelUuid(UUID.randomUUID());
+ private static final int TEST_SIM_SLOT_INDEX = 1;
+ private static final int TEST_SUBSCRIPTION_ID_1 = 2;
+ private static final SubscriptionInfo TEST_SUBINFO_1 = mock(SubscriptionInfo.class);
+ private static final int TEST_SUBSCRIPTION_ID_2 = 3;
+ private static final SubscriptionInfo TEST_SUBINFO_2 = mock(SubscriptionInfo.class);
+ private static final Map<Integer, ParcelUuid> TEST_SUBID_TO_GROUP_MAP;
+
+ static {
+ final Map<Integer, ParcelUuid> subIdToGroupMap = new HashMap<>();
+ subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_1, TEST_PARCEL_UUID);
+ subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_2, TEST_PARCEL_UUID);
+ TEST_SUBID_TO_GROUP_MAP = Collections.unmodifiableMap(subIdToGroupMap);
+ }
+
+ @NonNull private final Context mContext;
+ @NonNull private final TestLooper mTestLooper;
+ @NonNull private final Handler mHandler;
+ @NonNull private final TelephonySubscriptionTracker.Dependencies mDeps;
+
+ @NonNull private final SubscriptionManager mSubscriptionManager;
+ @NonNull private final CarrierConfigManager mCarrierConfigManager;
+
+ @NonNull private TelephonySubscriptionTrackerCallback mCallback;
+ @NonNull private TelephonySubscriptionTracker mTelephonySubscriptionTracker;
+
+ public TelephonySubscriptionTrackerTest() {
+ mContext = mock(Context.class);
+ mTestLooper = new TestLooper();
+ mHandler = new Handler(mTestLooper.getLooper());
+ mDeps = mock(TelephonySubscriptionTracker.Dependencies.class);
+
+ mSubscriptionManager = mock(SubscriptionManager.class);
+ mCarrierConfigManager = mock(CarrierConfigManager.class);
+
+ doReturn(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
+ .when(mContext)
+ .getSystemServiceName(SubscriptionManager.class);
+ doReturn(mSubscriptionManager)
+ .when(mContext)
+ .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+
+ doReturn(Context.CARRIER_CONFIG_SERVICE)
+ .when(mContext)
+ .getSystemServiceName(CarrierConfigManager.class);
+ doReturn(mCarrierConfigManager)
+ .when(mContext)
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+
+ // subId 1, 2 are in same subGrp, only subId 1 is active
+ doReturn(TEST_PARCEL_UUID).when(TEST_SUBINFO_1).getGroupUuid();
+ doReturn(TEST_PARCEL_UUID).when(TEST_SUBINFO_2).getGroupUuid();
+ doReturn(TEST_SIM_SLOT_INDEX).when(TEST_SUBINFO_1).getSimSlotIndex();
+ doReturn(INVALID_SIM_SLOT_INDEX).when(TEST_SUBINFO_2).getSimSlotIndex();
+ doReturn(TEST_SUBSCRIPTION_ID_1).when(TEST_SUBINFO_1).getSubscriptionId();
+ doReturn(TEST_SUBSCRIPTION_ID_2).when(TEST_SUBINFO_2).getSubscriptionId();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mCallback = mock(TelephonySubscriptionTrackerCallback.class);
+ mTelephonySubscriptionTracker =
+ new TelephonySubscriptionTracker(mContext, mHandler, mCallback, mDeps);
+ mTelephonySubscriptionTracker.register();
+
+ doReturn(true).when(mDeps).isConfigForIdentifiedCarrier(any());
+ doReturn(Arrays.asList(TEST_SUBINFO_1, TEST_SUBINFO_2))
+ .when(mSubscriptionManager)
+ .getAllSubscriptionInfoList();
+ }
+
+ private IntentFilter getIntentFilter() {
+ final ArgumentCaptor<IntentFilter> captor = ArgumentCaptor.forClass(IntentFilter.class);
+ verify(mContext).registerReceiver(any(), captor.capture(), any(), any());
+
+ return captor.getValue();
+ }
+
+ private OnSubscriptionsChangedListener getOnSubscriptionsChangedListener() {
+ final ArgumentCaptor<OnSubscriptionsChangedListener> captor =
+ ArgumentCaptor.forClass(OnSubscriptionsChangedListener.class);
+ verify(mSubscriptionManager).addOnSubscriptionsChangedListener(any(), captor.capture());
+
+ return captor.getValue();
+ }
+
+ private Intent buildTestBroadcastIntent(boolean hasValidSubscription) {
+ Intent intent = new Intent(ACTION_CARRIER_CONFIG_CHANGED);
+ intent.putExtra(EXTRA_SLOT_INDEX, TEST_SIM_SLOT_INDEX);
+ intent.putExtra(
+ EXTRA_SUBSCRIPTION_INDEX,
+ hasValidSubscription ? TEST_SUBSCRIPTION_ID_1 : INVALID_SUBSCRIPTION_ID);
+
+ return intent;
+ }
+
+ private TelephonySubscriptionSnapshot buildExpectedSnapshot(Set<ParcelUuid> activeSubGroups) {
+ return buildExpectedSnapshot(TEST_SUBID_TO_GROUP_MAP, activeSubGroups);
+ }
+
+ private TelephonySubscriptionSnapshot buildExpectedSnapshot(
+ Map<Integer, ParcelUuid> subIdToGroupMap, Set<ParcelUuid> activeSubGroups) {
+ return new TelephonySubscriptionSnapshot(subIdToGroupMap, activeSubGroups);
+ }
+
+ private void verifyNoActiveSubscriptions() {
+ verify(mCallback).onNewSnapshot(
+ argThat(snapshot -> snapshot.getActiveSubscriptionGroups().isEmpty()));
+ }
+
+ private void setupReadySubIds() {
+ mTelephonySubscriptionTracker.setReadySubIdsBySlotId(
+ Collections.singletonMap(TEST_SIM_SLOT_INDEX, TEST_SUBSCRIPTION_ID_1));
+ }
+
+ @Test
+ public void testRegister() throws Exception {
+ verify(mContext)
+ .registerReceiver(
+ eq(mTelephonySubscriptionTracker),
+ any(IntentFilter.class),
+ any(),
+ eq(mHandler));
+ final IntentFilter filter = getIntentFilter();
+ assertEquals(1, filter.countActions());
+ assertTrue(filter.hasAction(ACTION_CARRIER_CONFIG_CHANGED));
+
+ verify(mSubscriptionManager)
+ .addOnSubscriptionsChangedListener(any(HandlerExecutor.class), any());
+ assertNotNull(getOnSubscriptionsChangedListener());
+ }
+
+ @Test
+ public void testUnregister() throws Exception {
+ mTelephonySubscriptionTracker.unregister();
+
+ verify(mContext).unregisterReceiver(eq(mTelephonySubscriptionTracker));
+
+ final OnSubscriptionsChangedListener listener = getOnSubscriptionsChangedListener();
+ verify(mSubscriptionManager).removeOnSubscriptionsChangedListener(eq(listener));
+ }
+
+ @Test
+ public void testOnSubscriptionsChangedFired_NoReadySubIds() throws Exception {
+ final OnSubscriptionsChangedListener listener = getOnSubscriptionsChangedListener();
+ listener.onSubscriptionsChanged();
+ mTestLooper.dispatchAll();
+
+ verifyNoActiveSubscriptions();
+ }
+
+ @Test
+ public void testOnSubscriptionsChangedFired_WithReadySubIds() throws Exception {
+ setupReadySubIds();
+
+ final OnSubscriptionsChangedListener listener = getOnSubscriptionsChangedListener();
+ listener.onSubscriptionsChanged();
+ mTestLooper.dispatchAll();
+
+ final Set<ParcelUuid> activeSubGroups = Collections.singleton(TEST_PARCEL_UUID);
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(activeSubGroups)));
+ }
+
+ @Test
+ public void testReceiveBroadcast_ConfigReadyWithSubscriptions() throws Exception {
+ mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ mTestLooper.dispatchAll();
+
+ final Set<ParcelUuid> activeSubGroups = Collections.singleton(TEST_PARCEL_UUID);
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(activeSubGroups)));
+ }
+
+ @Test
+ public void testReceiveBroadcast_ConfigReadyNoSubscriptions() throws Exception {
+ doReturn(new ArrayList<SubscriptionInfo>())
+ .when(mSubscriptionManager)
+ .getAllSubscriptionInfoList();
+
+ mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ mTestLooper.dispatchAll();
+
+ // Expect an empty snapshot
+ verify(mCallback).onNewSnapshot(
+ eq(buildExpectedSnapshot(Collections.emptyMap(), Collections.emptySet())));
+ }
+
+ @Test
+ public void testReceiveBroadcast_SlotCleared() throws Exception {
+ setupReadySubIds();
+
+ mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false));
+ mTestLooper.dispatchAll();
+
+ verifyNoActiveSubscriptions();
+ assertTrue(mTelephonySubscriptionTracker.getReadySubIdsBySlotId().isEmpty());
+ }
+
+ @Test
+ public void testReceiveBroadcast_ConfigNotReady() throws Exception {
+ doReturn(false).when(mDeps).isConfigForIdentifiedCarrier(any());
+
+ mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ mTestLooper.dispatchAll();
+
+ // No interactions expected; config was not loaded
+ verifyNoMoreInteractions(mCallback);
+ }
+
+ @Test
+ public void testSubscriptionsClearedAfterValidTriggersCallbacks() throws Exception {
+ final Set<ParcelUuid> activeSubGroups = Collections.singleton(TEST_PARCEL_UUID);
+
+ mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ mTestLooper.dispatchAll();
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(activeSubGroups)));
+ assertNotNull(
+ mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
+
+ doReturn(Collections.emptyList()).when(mSubscriptionManager).getAllSubscriptionInfoList();
+ mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ mTestLooper.dispatchAll();
+ verify(mCallback).onNewSnapshot(
+ eq(buildExpectedSnapshot(Collections.emptyMap(), Collections.emptySet())));
+ }
+
+ @Test
+ public void testSlotClearedAfterValidTriggersCallbacks() throws Exception {
+ final Set<ParcelUuid> activeSubGroups = Collections.singleton(TEST_PARCEL_UUID);
+
+ mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ mTestLooper.dispatchAll();
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(activeSubGroups)));
+ assertNotNull(
+ mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
+
+ mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false));
+ mTestLooper.dispatchAll();
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(Collections.emptySet())));
+ assertNull(mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
+ }
+
+ @Test
+ public void testTelephonySubscriptionSnapshotGetGroupForSubId() throws Exception {
+ final TelephonySubscriptionSnapshot snapshot =
+ new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, Collections.emptySet());
+
+ assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_1));
+ assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_2));
+ }
+
+ @Test
+ public void testTelephonySubscriptionSnapshotGetAllSubIdsInGroup() throws Exception {
+ final TelephonySubscriptionSnapshot snapshot =
+ new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, Collections.emptySet());
+
+ assertEquals(
+ new ArraySet<>(Arrays.asList(TEST_SUBSCRIPTION_ID_1, TEST_SUBSCRIPTION_ID_2)),
+ snapshot.getAllSubIdsInGroup(TEST_PARCEL_UUID));
+ }
+}
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index 28330db966af..d7a8e6fe6ada 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -548,14 +548,14 @@ bool ExtractResFilePathParts(const StringPiece& path, StringPiece* out_prefix,
}
StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx) {
- if (auto str = pool.stringAt(idx)) {
+ if (auto str = pool.stringAt(idx); str.ok()) {
return *str;
}
return StringPiece16();
}
std::string GetString(const android::ResStringPool& pool, size_t idx) {
- if (auto str = pool.string8At(idx)) {
+ if (auto str = pool.string8At(idx); str.ok()) {
return ModifiedUtf8ToUtf8(str->to_string());
}
return Utf16ToUtf8(GetString16(pool, idx));
diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp
index 950473d223fd..991b28071515 100644
--- a/tools/validatekeymaps/Main.cpp
+++ b/tools/validatekeymaps/Main.cpp
@@ -96,7 +96,7 @@ static bool validateFile(const char* filename) {
case FileType::KEY_LAYOUT: {
base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(filename);
- if (!ret) {
+ if (!ret.ok()) {
error("Error %s parsing key layout file.\n\n", ret.error().message().c_str());
return false;
}
@@ -106,7 +106,7 @@ static bool validateFile(const char* filename) {
case FileType::KEY_CHARACTER_MAP: {
base::Result<std::shared_ptr<KeyCharacterMap>> ret =
KeyCharacterMap::load(filename, KeyCharacterMap::Format::ANY);
- if (!ret) {
+ if (!ret.ok()) {
error("Error %s parsing key character map file.\n\n",
ret.error().message().c_str());
return false;