diff options
94 files changed, 3289 insertions, 490 deletions
diff --git a/cmds/idmap2/include/idmap2/FileUtils.h b/cmds/idmap2/include/idmap2/FileUtils.h index c4e0e1fd8ef0..8fcaeb1f6b87 100644 --- a/cmds/idmap2/include/idmap2/FileUtils.h +++ b/cmds/idmap2/include/idmap2/FileUtils.h @@ -17,6 +17,8 @@ #ifndef IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_ #define IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_ +#include <sys/types.h> + #include <string> namespace android::idmap2::utils { diff --git a/core/api/current.txt b/core/api/current.txt index bc61d15f4eb0..e4e7d4eec77d 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -954,7 +954,7 @@ package android { field public static final int measureWithLargestChild = 16843476; // 0x10102d4 field public static final int mediaRouteButtonStyle = 16843693; // 0x10103ad field public static final int mediaRouteTypes = 16843694; // 0x10103ae - field public static final int memtagMode = 16844313; // 0x1010619 + field public static final int memtagMode = 16844324; // 0x1010624 field public static final int menuCategory = 16843230; // 0x10101de field public static final int mimeGroup = 16844309; // 0x1010615 field public static final int mimeType = 16842790; // 0x1010026 @@ -978,7 +978,7 @@ package android { field public static final int multiArch = 16843918; // 0x101048e field public static final int multiprocess = 16842771; // 0x1010013 field public static final int name = 16842755; // 0x1010003 - field public static final int nativeHeapZeroInitialized = 16844314; // 0x101061a + field public static final int nativeHeapZeroInitialized = 16844325; // 0x1010625 field public static final int navigationBarColor = 16843858; // 0x1010452 field public static final int navigationBarDividerColor = 16844141; // 0x101056d field public static final int navigationContentDescription = 16843969; // 0x10104c1 @@ -8657,6 +8657,15 @@ package android.bluetooth { field public static final int TELEPHONY = 4194304; // 0x400000 } + public final class BluetoothCsipSetCoordinator implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile { + method public void close(); + method protected void finalize(); + method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); + method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice); + method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]); + field @RequiresPermission(android.Manifest.permission.BLUETOOTH) public static final String ACTION_CSIS_CONNECTION_STATE_CHANGED = "android.bluetooth.action.CSIS_CONNECTION_STATE_CHANGED"; + } + public final class BluetoothDevice implements android.os.Parcelable { method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback); method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int); @@ -8703,6 +8712,7 @@ package android.bluetooth { field public static final String EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE"; field public static final String EXTRA_CLASS = "android.bluetooth.device.extra.CLASS"; field public static final String EXTRA_DEVICE = "android.bluetooth.device.extra.DEVICE"; + field public static final String EXTRA_IS_COORDINATED_SET_MEMBER = "android.bluetooth.extra.IS_COORDINATED_SET_MEMBER"; field public static final String EXTRA_NAME = "android.bluetooth.device.extra.NAME"; field public static final String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY"; field public static final String EXTRA_PAIRING_VARIANT = "android.bluetooth.device.extra.PAIRING_VARIANT"; @@ -29936,6 +29946,11 @@ package android.os { ctor public OperationCanceledException(String); } + public interface OutcomeReceiver<R, E extends java.lang.Throwable> { + method public default void onError(@NonNull E); + method public void onResult(@NonNull R); + } + public final class Parcel { method public void appendFrom(android.os.Parcel, int, int); method @Nullable public android.os.IBinder[] createBinderArray(); @@ -41031,6 +41046,7 @@ package android.telephony { method public String getNetworkOperator(); method public String getNetworkOperatorName(); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getNetworkSelectionMode(); + method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getNetworkSlicingConfiguration(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.data.SlicingConfig,android.telephony.TelephonyManager.SlicingException>); method public String getNetworkSpecifier(); method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getNetworkType(); method @Deprecated public int getPhoneCount(); @@ -41264,6 +41280,13 @@ package android.telephony { field public static final int ERROR_TIMEOUT = 1; // 0x1 } + public static class TelephonyManager.SlicingException extends java.lang.Exception { + ctor public TelephonyManager.SlicingException(int); + method public int getErrorCode(); + field public static final int ERROR_MODEM_ERROR = 2; // 0x2 + field public static final int ERROR_TIMEOUT = 1; // 0x1 + } + public abstract static class TelephonyManager.UssdResponseCallback { ctor public TelephonyManager.UssdResponseCallback(); method public void onReceiveUssdResponse(android.telephony.TelephonyManager, String, CharSequence); @@ -41439,6 +41462,88 @@ package android.telephony.data { method @NonNull public android.telephony.data.ApnSetting.Builder setUser(@Nullable String); } + public final class NetworkSliceInfo implements android.os.Parcelable { + method public int describeContents(); + method @IntRange(from=0xffffffff, to=0xfffffe) public int getMappedHplmnSliceDifferentiator(); + method public int getMappedHplmnSliceServiceType(); + method @IntRange(from=0xffffffff, to=0xfffffe) public int getSliceDifferentiator(); + method public int getSliceServiceType(); + method public int getStatus(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.NetworkSliceInfo> CREATOR; + field public static final int SLICE_DIFFERENTIATOR_NO_SLICE = -1; // 0xffffffff + field public static final int SLICE_SERVICE_TYPE_EMBB = 1; // 0x1 + field public static final int SLICE_SERVICE_TYPE_MIOT = 3; // 0x3 + field public static final int SLICE_SERVICE_TYPE_NONE = 0; // 0x0 + field public static final int SLICE_SERVICE_TYPE_URLLC = 2; // 0x2 + field public static final int SLICE_STATUS_ALLOWED = 2; // 0x2 + field public static final int SLICE_STATUS_CONFIGURED = 1; // 0x1 + field public static final int SLICE_STATUS_DEFAULT_CONFIGURED = 5; // 0x5 + field public static final int SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN = 3; // 0x3 + field public static final int SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA = 4; // 0x4 + field public static final int SLICE_STATUS_UNKNOWN = 0; // 0x0 + } + + public static final class NetworkSliceInfo.Builder { + ctor public NetworkSliceInfo.Builder(); + method @NonNull public android.telephony.data.NetworkSliceInfo build(); + method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setMappedHplmnSliceDifferentiator(@IntRange(from=0xffffffff, to=0xfffffe) int); + method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setMappedHplmnSliceServiceType(int); + method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setSliceDifferentiator(@IntRange(from=0xffffffff, to=0xfffffe) int); + method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setSliceServiceType(int); + method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setStatus(int); + } + + public final class RouteSelectionDescriptor implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List<java.lang.String> getDataNetworkName(); + method @IntRange(from=0x0, to=0xff) public int getPrecedence(); + method public int getSessionType(); + method @NonNull public java.util.List<android.telephony.data.NetworkSliceInfo> getSliceInfo(); + method public int getSscMode(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.RouteSelectionDescriptor> CREATOR; + field public static final int ROUTE_SSC_MODE_1 = 1; // 0x1 + field public static final int ROUTE_SSC_MODE_2 = 2; // 0x2 + field public static final int ROUTE_SSC_MODE_3 = 3; // 0x3 + field public static final int SESSION_TYPE_IPV4 = 0; // 0x0 + field public static final int SESSION_TYPE_IPV4V6 = 2; // 0x2 + field public static final int SESSION_TYPE_IPV6 = 1; // 0x1 + } + + public final class SlicingConfig implements android.os.Parcelable { + ctor public SlicingConfig(); + method public int describeContents(); + method @NonNull public java.util.List<android.telephony.data.NetworkSliceInfo> getSliceInfo(); + method @NonNull public java.util.List<android.telephony.data.UrspRule> getUrspRules(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.SlicingConfig> CREATOR; + } + + public final class TrafficDescriptor implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public String getDataNetworkName(); + method @Nullable public String getOsAppId(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.TrafficDescriptor> CREATOR; + } + + public static final class TrafficDescriptor.Builder { + ctor public TrafficDescriptor.Builder(); + method @NonNull public android.telephony.data.TrafficDescriptor build(); + method @NonNull public android.telephony.data.TrafficDescriptor.Builder setDataNetworkName(@NonNull String); + method @NonNull public android.telephony.data.TrafficDescriptor.Builder setOsAppId(@NonNull String); + } + + public final class UrspRule implements android.os.Parcelable { + method public int describeContents(); + method @IntRange(from=0x0, to=0xff) public int getPrecedence(); + method @NonNull public java.util.List<android.telephony.data.RouteSelectionDescriptor> getRouteSelectionDescriptor(); + method @NonNull public java.util.List<android.telephony.data.TrafficDescriptor> getTrafficDescriptors(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.UrspRule> CREATOR; + } + } package android.telephony.emergency { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index cd41e58ba882..af103ab460cf 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -1507,6 +1507,21 @@ package android.bluetooth { method public void onOobData(int, @NonNull android.bluetooth.OobData); } + public final class BluetoothCsipSetCoordinator implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile { + method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.List<java.lang.Integer> getAllGroupIds(@Nullable android.os.ParcelUuid); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice); + method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.Map getGroupUuidMapByDevice(@Nullable android.bluetooth.BluetoothDevice); + method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.UUID groupLock(int, @Nullable java.util.concurrent.Executor, @Nullable android.bluetooth.BluetoothCsipSetCoordinator.ClientLockCallback); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean groupUnlock(@NonNull java.util.UUID); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int); + field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CSIS_DEVICE_AVAILABLE = "android.bluetooth.action.CSIS_DEVICE_AVAILABLE"; + field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CSIS_SET_MEMBER_AVAILABLE = "android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE"; + } + + public static interface BluetoothCsipSetCoordinator.ClientLockCallback { + method public void onGroupLockSet(int, int, boolean); + } + public final class BluetoothDevice implements android.os.Parcelable { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean cancelBondProcess(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean createBondOutOfBand(int, @Nullable android.bluetooth.OobData, @Nullable android.bluetooth.OobData); @@ -1560,7 +1575,10 @@ package android.bluetooth { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean connect(android.bluetooth.BluetoothDevice); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(android.bluetooth.BluetoothDevice); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInbandRingingEnabled(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean startScoUsingVirtualVoiceCall(); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean stopScoUsingVirtualVoiceCall(); } public final class BluetoothHearingAid implements android.bluetooth.BluetoothProfile { @@ -7241,6 +7259,7 @@ package android.os { public class SystemConfigManager { method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps(); method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps(); + method @NonNull public java.util.List<java.lang.String> getEnabledComponentOverrides(@NonNull String); method @NonNull @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public int[] getSystemPermissionUids(@NonNull String); } @@ -9671,6 +9690,25 @@ package android.telephony { field public static final String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming"; } + public final class ModemActivityInfo implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.telephony.ModemActivityInfo getDelta(@NonNull android.telephony.ModemActivityInfo); + method public long getIdleTimeMillis(); + method public static int getNumTxPowerLevels(); + method public long getReceiveTimeMillis(); + method public long getSleepTimeMillis(); + method public long getTimestampMillis(); + method public long getTransmitDurationMillisAtPowerLevel(int); + method @NonNull public android.util.Range<java.lang.Integer> getTransmitPowerRange(int); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR; + field public static final int TX_POWER_LEVEL_0 = 0; // 0x0 + field public static final int TX_POWER_LEVEL_1 = 1; // 0x1 + field public static final int TX_POWER_LEVEL_2 = 2; // 0x2 + field public static final int TX_POWER_LEVEL_3 = 3; // 0x3 + field public static final int TX_POWER_LEVEL_4 = 4; // 0x4 + } + public final class NetworkRegistrationInfo implements android.os.Parcelable { method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo(); method public int getRegistrationState(); @@ -10337,6 +10375,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean); method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestModemActivityInfo(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.ModemActivityInfo,android.telephony.TelephonyManager.ModemActivityInfoException>); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetAllCarrierActions(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetCarrierKeysForImsiEncryption(); @@ -10509,6 +10548,14 @@ package android.telephony { field public static final int RESULT_SUCCESS = 0; // 0x0 } + public static class TelephonyManager.ModemActivityInfoException extends java.lang.Exception { + method public int getErrorCode(); + field public static final int ERROR_INVALID_INFO_RECEIVED = 2; // 0x2 + field public static final int ERROR_MODEM_RESPONSE_ERROR = 3; // 0x3 + field public static final int ERROR_PHONE_NOT_AVAILABLE = 1; // 0x1 + field public static final int ERROR_UNKNOWN = 0; // 0x0 + } + public final class ThermalMitigationRequest implements android.os.Parcelable { method public int describeContents(); method @Nullable public android.telephony.DataThrottlingRequest getDataThrottlingRequest(); @@ -10772,32 +10819,6 @@ package android.telephony.data { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.EpsBearerQosSessionAttributes> CREATOR; } - public final class NetworkSliceInfo implements android.os.Parcelable { - method public int describeContents(); - method @IntRange(from=android.telephony.data.NetworkSliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.NetworkSliceInfo.MAX_SLICE_DIFFERENTIATOR) public int getMappedHplmnSliceDifferentiator(); - method public int getMappedHplmnSliceServiceType(); - method @IntRange(from=android.telephony.data.NetworkSliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.NetworkSliceInfo.MAX_SLICE_DIFFERENTIATOR) public int getSliceDifferentiator(); - method public int getSliceServiceType(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.NetworkSliceInfo> CREATOR; - field public static final int MAX_SLICE_DIFFERENTIATOR = 16777214; // 0xfffffe - field public static final int MIN_SLICE_DIFFERENTIATOR = -1; // 0xffffffff - field public static final int SLICE_DIFFERENTIATOR_NO_SLICE = -1; // 0xffffffff - field public static final int SLICE_SERVICE_TYPE_EMBB = 1; // 0x1 - field public static final int SLICE_SERVICE_TYPE_MIOT = 3; // 0x3 - field public static final int SLICE_SERVICE_TYPE_NONE = 0; // 0x0 - field public static final int SLICE_SERVICE_TYPE_URLLC = 2; // 0x2 - } - - public static final class NetworkSliceInfo.Builder { - ctor public NetworkSliceInfo.Builder(); - method @NonNull public android.telephony.data.NetworkSliceInfo build(); - method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setMappedHplmnSliceDifferentiator(@IntRange(from=android.telephony.data.NetworkSliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.NetworkSliceInfo.MAX_SLICE_DIFFERENTIATOR) int); - method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setMappedHplmnSliceServiceType(int); - method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setSliceDifferentiator(@IntRange(from=android.telephony.data.NetworkSliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.NetworkSliceInfo.MAX_SLICE_DIFFERENTIATOR) int); - method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setSliceServiceType(int); - } - public final class NrQosSessionAttributes implements android.os.Parcelable android.net.QosSessionAttributes { method public int describeContents(); method @NonNull public java.time.Duration getBitRateWindowDuration(); @@ -10854,15 +10875,6 @@ package android.telephony.data { method @NonNull public android.telephony.data.ThrottleStatus.Builder setTransportType(int); } - public final class TrafficDescriptor implements android.os.Parcelable { - ctor public TrafficDescriptor(@Nullable String, @Nullable String); - method public int describeContents(); - method @Nullable public String getDnn(); - method @Nullable public String getOsAppId(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.TrafficDescriptor> CREATOR; - } - } package android.telephony.euicc { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 661394732a12..d905bbeba6b5 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1649,6 +1649,28 @@ package android.telephony { field public static final String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA = "mbms-streaming-service-override"; } + public final class ModemActivityInfo implements android.os.Parcelable { + ctor public ModemActivityInfo(long, int, int, @NonNull int[], int); + method public int describeContents(); + method @NonNull public android.telephony.ModemActivityInfo getDelta(@NonNull android.telephony.ModemActivityInfo); + method public long getIdleTimeMillis(); + method public static int getNumTxPowerLevels(); + method public long getReceiveTimeMillis(); + method public long getSleepTimeMillis(); + method public long getTimestampMillis(); + method public long getTransmitDurationMillisAtPowerLevel(int); + method @NonNull public android.util.Range<java.lang.Integer> getTransmitPowerRange(int); + method public boolean isEmpty(); + method public boolean isValid(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR; + field public static final int TX_POWER_LEVEL_0 = 0; // 0x0 + field public static final int TX_POWER_LEVEL_1 = 1; // 0x1 + field public static final int TX_POWER_LEVEL_2 = 2; // 0x2 + field public static final int TX_POWER_LEVEL_3 = 3; // 0x3 + field public static final int TX_POWER_LEVEL_4 = 4; // 0x4 + } + public class PhoneNumberUtils { method public static int getMinMatchForTest(); method public static void setMinMatchForTest(int); diff --git a/core/java/Android.bp b/core/java/Android.bp index eee696b0404c..675cea827216 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -341,6 +341,27 @@ filegroup { ], } +java_library { + name: "modules-utils-statemachine", + srcs: [ + "com/android/internal/util/IState.java", + "com/android/internal/util/State.java", + "com/android/internal/util/StateMachine.java", + ], + libs: [ + "framework-annotations-lib", + "unsupportedappusage", + ], + sdk_version: "module_current", + min_sdk_version: "29", + + visibility: ["//visibility:public"], + apex_available: [ + "//apex_available:anyapex", + "//apex_available:platform", + ], +} + filegroup { name: "framework-ims-common-shared-srcs", srcs: [ diff --git a/core/java/android/annotation/SuppressLint.java b/core/java/android/annotation/SuppressLint.java new file mode 100644 index 000000000000..2d3456b0ea46 --- /dev/null +++ b/core/java/android/annotation/SuppressLint.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2012 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.annotation; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Indicates that Lint should ignore the specified warnings for the annotated element. */ +@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) +@Retention(RetentionPolicy.CLASS) +public @interface SuppressLint { + /** + * The set of warnings (identified by the lint issue id) that should be + * ignored by lint. It is not an error to specify an unrecognized name. + */ + String[] value(); +} diff --git a/core/java/android/annotation/TargetApi.java b/core/java/android/annotation/TargetApi.java new file mode 100644 index 000000000000..975318e4de67 --- /dev/null +++ b/core/java/android/annotation/TargetApi.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2012 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.annotation; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Indicates that Lint should treat this type as targeting a given API level, no matter what the + project target is. */ +@Target({TYPE, METHOD, CONSTRUCTOR, FIELD}) +@Retention(RetentionPolicy.CLASS) +public @interface TargetApi { + /** + * This sets the target api level for the type.. + */ + int value(); +} diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 41170a4c2749..749e8f548fab 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -6299,7 +6299,13 @@ public final class ActivityThread extends ClientTransactionHandler { final File cacheDir = context.getCacheDir(); if (cacheDir != null) { // Provide a usable directory for temporary files - System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath()); + String tmpdir = cacheDir.getAbsolutePath(); + System.setProperty("java.io.tmpdir", tmpdir); + try { + android.system.Os.setenv("TMPDIR", tmpdir, true); + } catch (ErrnoException ex) { + Log.w(TAG, "Unable to initialize $TMPDIR", ex); + } } else { Log.v(TAG, "Unable to initialize \"java.io.tmpdir\" property " + "due to missing cache directory"); diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java index 71b28fba6019..6c435b9597f4 100644 --- a/core/java/android/app/IntentService.java +++ b/core/java/android/app/IntentService.java @@ -54,7 +54,7 @@ import android.os.Message; * @see android.support.v4.app.JobIntentService * * @deprecated IntentService is subject to all the - * <a href="/preview/features/background.html">background execution limits</a> + * <a href="{@docRoot}about/versions/oreo/background.html">background execution limits</a> * imposed with Android 8.0 (API level 26). Consider using {@link androidx.work.WorkManager} * or {@link androidx.core.app.JobIntentService}, which uses jobs * instead of services when running on Android 8.0 or higher. diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index d487025631c7..313dd3e2bed0 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -2819,6 +2819,10 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.VOLUME_CONTROL) { BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this); return true; + } else if (profile == BluetoothProfile.CSIP_SET_COORDINATOR) { + BluetoothCsipSetCoordinator csipSetCoordinator = + new BluetoothCsipSetCoordinator(context, listener); + return true; } else { return false; } @@ -2908,6 +2912,11 @@ public final class BluetoothAdapter { BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy; vcs.close(); break; + case BluetoothProfile.CSIP_SET_COORDINATOR: + BluetoothCsipSetCoordinator csipSetCoordinator = + (BluetoothCsipSetCoordinator) proxy; + csipSetCoordinator.close(); + break; } } diff --git a/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java b/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java new file mode 100644 index 000000000000..cb542e5ba388 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java @@ -0,0 +1,550 @@ +/* + * Copyright 2021 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * 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.bluetooth; + +import android.Manifest; +import android.annotation.CallbackExecutor; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.util.CloseGuard; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.Executor; + +/** + * This class provides the public APIs to control the Bluetooth CSIP set coordinator. + * + * <p>BluetoothCsipSetCoordinator is a proxy object for controlling the Bluetooth VC + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothCsipSetCoordinator proxy object. + * + */ +public final class BluetoothCsipSetCoordinator implements BluetoothProfile, AutoCloseable { + private static final String TAG = "BluetoothCsipSetCoordinator"; + private static final boolean DBG = false; + private static final boolean VDBG = false; + + private CloseGuard mCloseGuard; + + /** + * @hide + */ + @SystemApi + public interface ClientLockCallback { + /** + * @hide + */ + @SystemApi void onGroupLockSet(int groupId, int opStatus, boolean isLocked); + } + + private static class BluetoothCsipSetCoordinatorLockCallbackDelegate + extends IBluetoothCsipSetCoordinatorLockCallback.Stub { + private final ClientLockCallback mCallback; + private final Executor mExecutor; + + BluetoothCsipSetCoordinatorLockCallbackDelegate( + Executor executor, ClientLockCallback callback) { + mExecutor = executor; + mCallback = callback; + } + + @Override + public void onGroupLockSet(int groupId, int opStatus, boolean isLocked) { + mExecutor.execute(() -> mCallback.onGroupLockSet(groupId, opStatus, isLocked)); + } + }; + + /** + * Intent used to broadcast the change in connection state of the CSIS + * Client. + * + * <p>This intent will have 3 extras: + * <ul> + * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> + * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> + * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> + * </ul> + * + * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CSIS_CONNECTION_STATE_CHANGED = + "android.bluetooth.action.CSIS_CONNECTION_STATE_CHANGED"; + + /** + * Intent used to expose broadcast receiving device. + * + * <p>This intent will have 2 extras: + * <ul> + * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote Broadcast receiver device. </li> + * <li> {@link #EXTRA_CSIS_GROUP_ID} - Group identifier. </li> + * <li> {@link #EXTRA_CSIS_GROUP_SIZE} - Group size. </li> + * <li> {@link #EXTRA_CSIS_GROUP_TYPE_UUID} - Group type UUID. </li> + * </ul> + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CSIS_DEVICE_AVAILABLE = + "android.bluetooth.action.CSIS_DEVICE_AVAILABLE"; + + /** + * Used as an extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent. + * Contains the group id. + * + * @hide + */ + public static final String EXTRA_CSIS_GROUP_ID = "android.bluetooth.extra.CSIS_GROUP_ID"; + + /** + * Group size as int extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent. + * + * @hide + */ + public static final String EXTRA_CSIS_GROUP_SIZE = "android.bluetooth.extra.CSIS_GROUP_SIZE"; + + /** + * Group type uuid extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent. + * + * @hide + */ + public static final String EXTRA_CSIS_GROUP_TYPE_UUID = + "android.bluetooth.extra.CSIS_GROUP_TYPE_UUID"; + + /** + * Intent used to broadcast information about identified set member + * ready to connect. + * + * <p>This intent will have one extra: + * <ul> + * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can + * be null if no device is active. </li> + * <li> {@link #EXTRA_CSIS_GROUP_ID} - Group identifier. </li> + * </ul> + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CSIS_SET_MEMBER_AVAILABLE = + "android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE"; + + /** + * This represents an invalid group ID. + * + * @hide + */ + public static final int GROUP_ID_INVALID = IBluetoothCsipSetCoordinator.CSIS_GROUP_ID_INVALID; + + /** + * Indicating that group was locked with success. + * + * @hide + */ + public static final int GROUP_LOCK_SUCCESS = 0; + + /** + * Indicating that group locked failed due to invalid group ID. + * + * @hide + */ + public static final int GROUP_LOCK_FAILED_INVALID_GROUP = 1; + + /** + * Indicating that group locked failed due to empty group. + * + * @hide + */ + public static final int GROUP_LOCK_FAILED_GROUP_EMPTY = 2; + + /** + * Indicating that group locked failed due to group members being disconnected. + * + * @hide + */ + public static final int GROUP_LOCK_FAILED_GROUP_NOT_CONNECTED = 3; + + /** + * Indicating that group locked failed due to group member being already locked. + * + * @hide + */ + public static final int GROUP_LOCK_FAILED_LOCKED_BY_OTHER = 4; + + /** + * Indicating that group locked failed due to other reason. + * + * @hide + */ + public static final int GROUP_LOCK_FAILED_OTHER_REASON = 5; + + /** + * Indicating that group member in locked state was lost. + * + * @hide + */ + public static final int LOCKED_GROUP_MEMBER_LOST = 6; + + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector<IBluetoothCsipSetCoordinator> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.CSIP_SET_COORDINATOR, TAG, + IBluetoothCsipSetCoordinator.class.getName()) { + @Override + public IBluetoothCsipSetCoordinator getServiceInterface(IBinder service) { + return IBluetoothCsipSetCoordinator.Stub.asInterface( + Binder.allowBlocking(service)); + } + }; + + /** + * Create a BluetoothCsipSetCoordinator proxy object for interacting with the local + * Bluetooth CSIS service. + */ + /*package*/ BluetoothCsipSetCoordinator(Context context, ServiceListener listener) { + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mProfileConnector.connect(context, listener); + mCloseGuard = new CloseGuard(); + mCloseGuard.open("close"); + } + + /** + * @hide + */ + protected void finalize() { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + close(); + } + + /** + * @hide + */ + public void close() { + mProfileConnector.disconnect(); + } + + private IBluetoothCsipSetCoordinator getService() { + return mProfileConnector.getService(); + } + + /** + * Lock the set. + * @param groupId group ID to lock, + * @param executor callback executor, + * @param cb callback to report lock and unlock events - stays valid until the app unlocks + * using the returned lock identifier or the lock timeouts on the remote side, + * as per CSIS specification, + * @return unique lock identifier used for unlocking or null if lock has failed. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public + @Nullable UUID groupLock(int groupId, @Nullable @CallbackExecutor Executor executor, + @Nullable ClientLockCallback cb) { + if (VDBG) { + log("groupLockSet()"); + } + final IBluetoothCsipSetCoordinator service = getService(); + try { + if (service != null && isEnabled()) { + IBluetoothCsipSetCoordinatorLockCallback delegate = null; + if ((executor != null) && (cb != null)) { + delegate = new BluetoothCsipSetCoordinatorLockCallbackDelegate(executor, cb); + } + return service.groupLock(groupId, delegate).getUuid(); + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return null; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return null; + } + } + + /** + * Unlock the set. + * @param lockUuid unique lock identifier + * @return true if unlocked, false on error + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean groupUnlock(@NonNull UUID lockUuid) { + if (VDBG) { + log("groupLockSet()"); + } + if (lockUuid == null) { + return false; + } + + final IBluetoothCsipSetCoordinator service = getService(); + try { + if (service != null && isEnabled()) { + service.groupUnlock(new ParcelUuid(lockUuid)); + return true; + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** + * Get device's groups. + * @param device the active device + * @return Map of groups ids and related UUIDs + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @NonNull Map getGroupUuidMapByDevice(@Nullable BluetoothDevice device) { + if (VDBG) { + log("getGroupUuidMapByDevice()"); + } + final IBluetoothCsipSetCoordinator service = getService(); + try { + if (service != null && isEnabled()) { + return service.getGroupUuidMapByDevice(device); + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return new HashMap<>(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new HashMap<>(); + } + } + + /** + * Get group id for the given UUID + * @param uuid + * @return list of group IDs + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @NonNull List<Integer> getAllGroupIds(@Nullable ParcelUuid uuid) { + if (VDBG) { + log("getAllGroupIds()"); + } + final IBluetoothCsipSetCoordinator service = getService(); + try { + if (service != null && isEnabled()) { + return service.getAllGroupIds(uuid); + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return new ArrayList<Integer>(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList<Integer>(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public @NonNull List<BluetoothDevice> getConnectedDevices() { + if (VDBG) { + log("getConnectedDevices()"); + } + final IBluetoothCsipSetCoordinator service = getService(); + if (service != null && isEnabled()) { + try { + return service.getConnectedDevices(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList<BluetoothDevice>(); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return new ArrayList<BluetoothDevice>(); + } + + /** + * {@inheritDoc} + */ + @Override + public + @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates( + @NonNull int[] states) { + if (VDBG) { + log("getDevicesMatchingStates(states=" + Arrays.toString(states) + ")"); + } + final IBluetoothCsipSetCoordinator service = getService(); + if (service != null && isEnabled()) { + try { + return service.getDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList<BluetoothDevice>(); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return new ArrayList<BluetoothDevice>(); + } + + /** + * {@inheritDoc} + */ + @Override + public + @BluetoothProfile.BtProfileState int getConnectionState( + @Nullable BluetoothDevice device) { + if (VDBG) { + log("getState(" + device + ")"); + } + final IBluetoothCsipSetCoordinator service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.getConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return BluetoothProfile.STATE_DISCONNECTED; + } + + /** + * Set connection policy of the profile + * + * <p> The device should already be paired. + * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, + * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} + * + * @param device Paired bluetooth device + * @param connectionPolicy is the connection policy to set to for this profile + * @return true if connectionPolicy is set, false on error + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setConnectionPolicy( + @Nullable BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { + if (DBG) { + log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + } + final IBluetoothCsipSetCoordinator service = getService(); + try { + if (service != null && isEnabled() && isValidDevice(device)) { + if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { + return false; + } + return service.setConnectionPolicy(device, connectionPolicy); + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** + * Get the connection policy of the profile. + * + * <p> The connection policy can be any of: + * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN}, + * {@link #CONNECTION_POLICY_UNKNOWN} + * + * @param device Bluetooth device + * @return connection policy of the device + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { + if (VDBG) { + log("getConnectionPolicy(" + device + ")"); + } + final IBluetoothCsipSetCoordinator service = getService(); + try { + if (service != null && isEnabled() && isValidDevice(device)) { + return service.getConnectionPolicy(device); + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + } + } + + private boolean isEnabled() { + return mAdapter.getState() == BluetoothAdapter.STATE_ON; + } + + private static boolean isValidDevice(@Nullable BluetoothDevice device) { + return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 38fb90d9c4a7..b5ede8d1cdb6 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -106,7 +106,7 @@ public final class BluetoothDevice implements Parcelable { * <p>Sent when a remote device is found during discovery. * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link * #EXTRA_CLASS}. Can contain the extra fields {@link #EXTRA_NAME} and/or - * {@link #EXTRA_RSSI} if they are available. + * {@link #EXTRA_RSSI} and/or {@link #EXTRA_IS_COORDINATED_SET_MEMBER} if they are available. * <p>Requires {@link android.Manifest.permission#BLUETOOTH} and * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} to receive. */ @@ -257,6 +257,15 @@ public final class BluetoothDevice implements Parcelable { public static final String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI"; /** + * Used as an bool extra field in {@link #ACTION_FOUND} intents. + * It contains the information if device is discovered as member of a coordinated set or not. + * Pairing with device that belongs to a set would trigger pairing with the rest of set members. + * See Bluetooth CSIP specification for more details. + */ + public static final String EXTRA_IS_COORDINATED_SET_MEMBER = + "android.bluetooth.extra.IS_COORDINATED_SET_MEMBER"; + + /** * Used as a Parcelable {@link BluetoothClass} extra field in {@link * #ACTION_FOUND} and {@link #ACTION_CLASS_CHANGED} intents. */ diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index 632572dea3c6..18aa23a07b87 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -1018,8 +1018,8 @@ public final class BluetoothHeadset implements BluetoothProfile { * - binder is dead or Bluetooth is disabled or other error * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage public boolean startScoUsingVirtualVoiceCall() { if (DBG) log("startScoUsingVirtualVoiceCall()"); final IBluetoothHeadset service = mService; @@ -1048,8 +1048,8 @@ public final class BluetoothHeadset implements BluetoothProfile { * - binder is dead or Bluetooth is disabled or other error * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage public boolean stopScoUsingVirtualVoiceCall() { if (DBG) log("stopScoUsingVirtualVoiceCall()"); final IBluetoothHeadset service = mService; @@ -1227,7 +1227,8 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return true if in-band ringing is enabled, false if in-band ringing is disabled * @hide */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH) + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInbandRingingEnabled() { if (DBG) { log("isInbandRingingEnabled()"); diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java index 0ba92cc4fef7..504a7bd5a320 100644 --- a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java +++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java @@ -19,7 +19,6 @@ package android.content.pm.parsing.component; import android.annotation.Nullable; import android.content.IntentFilter; import android.os.Parcel; -import android.os.Parcelable; import android.util.Pair; import com.android.internal.util.DataClass; @@ -168,19 +167,6 @@ public final class ParsedIntentInfo extends IntentFilter { + '}'; } - public static final Parcelable.Creator<ParsedIntentInfo> CREATOR = - new Parcelable.Creator<ParsedIntentInfo>() { - @Override - public ParsedIntentInfo createFromParcel(Parcel source) { - return new ParsedIntentInfo(source); - } - - @Override - public ParsedIntentInfo[] newArray(int size) { - return new ParsedIntentInfo[size]; - } - }; - public boolean isHasDefault() { return hasDefault; } diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index 0a76a9c6bee3..9e78a6bb4760 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -1440,14 +1440,14 @@ public abstract class SensorManager { * Assuming that the bottom edge of the device faces the * user and that the screen is face-up, tilting the top edge * of the device toward the ground creates a positive pitch - * angle. The range of values is -π to π.</li> + * angle. The range of values is -π/2 to π/2.</li> * <li>values[2]: <i>Roll</i>, angle of rotation about the y axis. This * value represents the angle between a plane perpendicular * to the device's screen and a plane perpendicular to the * ground. Assuming that the bottom edge of the device faces * the user and that the screen is face-up, tilting the left * edge of the device toward the ground creates a positive - * roll angle. The range of values is -π/2 to π/2.</li> + * roll angle. The range of values is -π to π.</li> * </ul> * <p> * Applying these three rotations in the azimuth, pitch, roll order diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index 68917a82884b..08f75df5d82d 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -47,7 +47,6 @@ import android.text.TextUtils; import android.util.BackupUtils; import android.util.Log; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.net.module.util.NetworkIdentityUtils; @@ -151,24 +150,6 @@ public class NetworkTemplate implements Parcelable { } } - private static boolean sForceAllNetworkTypes = false; - - /** - * Results in matching against all mobile network types. - * - * <p>See {@link #matchesMobile} and {@link matchesMobileWildcard}. - */ - @VisibleForTesting - public static void forceAllNetworkTypes() { - sForceAllNetworkTypes = true; - } - - /** Resets the affect of {@link #forceAllNetworkTypes}. */ - @VisibleForTesting - public static void resetForceAllNetworkTypes() { - sForceAllNetworkTypes = false; - } - /** * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with * the given IMSI. @@ -611,7 +592,7 @@ public class NetworkTemplate implements Parcelable { // Only metered mobile network would be matched regardless of metered filter. // This is used to exclude non-metered APNs, e.g. IMS. See ag/908650. // TODO: Respect metered filter and remove mMetered condition. - return (sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered)) + return (ident.mType == TYPE_MOBILE && ident.mMetered) && !ArrayUtils.isEmpty(mMatchSubscriberIds) && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId) && matchesCollapsedRatType(ident); @@ -726,7 +707,7 @@ public class NetworkTemplate implements Parcelable { if (ident.mType == TYPE_WIMAX) { return true; } else { - return (sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered)) + return (ident.mType == TYPE_MOBILE && ident.mMetered) && matchesCollapsedRatType(ident); } } diff --git a/core/java/android/os/ISystemConfig.aidl b/core/java/android/os/ISystemConfig.aidl index 4d160da22ff8..d83d94a8ec77 100644 --- a/core/java/android/os/ISystemConfig.aidl +++ b/core/java/android/os/ISystemConfig.aidl @@ -40,4 +40,9 @@ interface ISystemConfig { * @see SystemConfigManager#getSystemPermissionUids */ int[] getSystemPermissionUids(String permissionName); + + /** + * @see SystemConfigManager#getEnabledComponentOverrides + */ + List<String> getEnabledComponentOverrides(String packageName); } diff --git a/core/java/android/os/OutcomeReceiver.java b/core/java/android/os/OutcomeReceiver.java new file mode 100644 index 000000000000..01b276411446 --- /dev/null +++ b/core/java/android/os/OutcomeReceiver.java @@ -0,0 +1,42 @@ +/* + * 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.os; + +import android.annotation.NonNull; + +/** + * Callback interface intended for use when an asynchronous operation may result in a failure. + * + * This interface may be used in cases where an asynchronous API may complete either with a value + * or with a {@link Throwable} that indicates an error. + * @param <R> The type of the result that's being sent. + * @param <E> The type of the {@link Throwable} that contains more information about the error. + */ +public interface OutcomeReceiver<R, E extends Throwable> { + /** + * Called when the asynchronous operation succeeds and delivers a result value. + * @param result The value delivered by the asynchronous operation. + */ + void onResult(@NonNull R result); + + /** + * Called when the asynchronous operation fails. The mode of failure is indicated by the + * {@link Throwable} passed as an argument to this method. + * @param error A subclass of {@link Throwable} with more details about the error that occurred. + */ + default void onError(@NonNull E error) {} +} diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 00db972bf709..a2716d211bbf 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -282,6 +282,8 @@ public final class Parcel { @CriticalNative private static native void nativeMarkSensitive(long nativePtr); + @FastNative + private static native void nativeMarkForBinder(long nativePtr, IBinder binder); @CriticalNative private static native int nativeDataSize(long nativePtr); @CriticalNative @@ -498,6 +500,16 @@ public final class Parcel { /** * Parcel data should be zero'd before realloc'd or deleted. + * + * Note: currently this feature requires multiple things to work in concert: + * - markSensitive must be called on every relative Parcel + * - FLAG_CLEAR_BUF must be passed into the kernel + * This requires having code which does the right thing in every method and in every backend + * of AIDL. Rather than exposing this API, it should be replaced with a single API on + * IBinder objects which can be called once, and the information should be fed into the + * Parcel using markForBinder APIs. In terms of code size and number of API calls, this is + * much more extensible. + * * @hide */ public final void markSensitive() { @@ -505,9 +517,23 @@ public final class Parcel { } /** + * Associate this parcel with a binder object. This marks the parcel as being prepared for a + * transaction on this specific binder object. Based on this, the format of the wire binder + * protocol may change. This should be called before any data is written to the parcel. If this + * is called multiple times, this will only be marked for the last binder. For future + * compatibility, it is recommended to call this on all parcels which are being sent over + * binder. + * + * @hide + */ + public void markForBinder(@NonNull IBinder binder) { + nativeMarkForBinder(mNativePtr, binder); + } + + /** * Returns the total amount of data contained in the parcel. */ - public final int dataSize() { + public int dataSize() { return nativeDataSize(mNativePtr); } @@ -3419,15 +3445,7 @@ public final class Parcel { public void writeToParcel(Parcel out) { if (mObject == null) { - int restore = mSource.dataPosition(); - try { - mSource.setDataPosition(mPosition); - out.writeInt(mSource.readInt()); // Type - out.writeInt(mSource.readInt()); // Length - out.appendFrom(mSource, mSource.dataPosition(), mLength); - } finally { - mSource.setDataPosition(restore); - } + out.appendFrom(mSource, mPosition, mLength + 8); } else { out.writeValue(mObject); } diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java index 06a2c875cf4f..4e8418bd60ec 100644 --- a/core/java/android/os/ServiceManager.java +++ b/core/java/android/os/ServiceManager.java @@ -29,7 +29,14 @@ import com.android.internal.util.StatLogger; import java.util.Map; -/** @hide */ +/** + * Manage binder services as registered with the binder context manager. These services must be + * declared statically on an Android device (SELinux access_vector service_manager, w/ service + * names in service_contexts files), and they do not follow the activity lifecycle. When + * building applications, android.app.Service should be preferred. + * + * @hide + **/ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public final class ServiceManager { private static final String TAG = "ServiceManager"; diff --git a/core/java/android/os/SystemConfigManager.java b/core/java/android/os/SystemConfigManager.java index 9bfa8adc8571..a6316df0780c 100644 --- a/core/java/android/os/SystemConfigManager.java +++ b/core/java/android/os/SystemConfigManager.java @@ -17,6 +17,7 @@ package android.os; import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; @@ -129,4 +130,21 @@ public class SystemConfigManager { throw e.rethrowFromSystemServer(); } } + + /** + * Get enabled component for a specific package + * + * @param packageName The target package. + * @return The enabled component + * {@hide} + */ + @SystemApi + @NonNull + public List<String> getEnabledComponentOverrides(@NonNull String packageName) { + try { + return mInterface.getEnabledComponentOverrides(packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/os/connectivity/CellularBatteryStats.java b/core/java/android/os/connectivity/CellularBatteryStats.java index 121fd333d17f..fc17002ba056 100644 --- a/core/java/android/os/connectivity/CellularBatteryStats.java +++ b/core/java/android/os/connectivity/CellularBatteryStats.java @@ -109,7 +109,7 @@ public final class CellularBatteryStats implements Parcelable { CellSignalStrength.getNumSignalStrengthLevels())); mTxTimeMs = Arrays.copyOfRange( txTimeMs, 0, - Math.min(txTimeMs.length, ModemActivityInfo.TX_POWER_LEVELS)); + Math.min(txTimeMs.length, ModemActivityInfo.getNumTxPowerLevels())); mMonitoredRailChargeConsumedMaMs = monitoredRailChargeConsumedMaMs; } diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index f0f0867d414b..505f400c60d2 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -2351,7 +2351,10 @@ public abstract class Layout { final int ellipsisStringLen = ellipsisString.length(); // Use the ellipsis string only if there are that at least as many characters to replace. final boolean useEllipsisString = ellipsisCount >= ellipsisStringLen; - for (int i = 0; i < ellipsisCount; i++) { + final int min = Math.max(0, start - ellipsisStart - lineStart); + final int max = Math.min(ellipsisCount, end - ellipsisStart - lineStart); + + for (int i = min; i < max; i++) { final char c; if (useEllipsisString && i < ellipsisStringLen) { c = ellipsisString.charAt(i); @@ -2360,9 +2363,7 @@ public abstract class Layout { } final int a = i + ellipsisStart + lineStart; - if (start <= a && a < end) { - dest[destoff + a - start] = c; - } + dest[destoff + a - start] = c; } } diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java index 844b156b84d3..c7609a6c18af 100644 --- a/core/java/android/webkit/URLUtil.java +++ b/core/java/android/webkit/URLUtil.java @@ -310,7 +310,7 @@ public final class URLUtil { String extension = null; // If we couldn't do anything with the hint, move toward the content disposition - if (filename == null && contentDisposition != null) { + if (contentDisposition != null) { filename = parseContentDisposition(contentDisposition); if (filename != null) { int index = filename.lastIndexOf('/') + 1; diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index dd2940f8c110..e8db609ee991 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -66,7 +66,6 @@ import android.provider.Settings; import android.telephony.CellSignalStrength; import android.telephony.DataConnectionRealTimeInfo; import android.telephony.ModemActivityInfo; -import android.telephony.ModemActivityInfo.TransmitPower; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.TelephonyManager; @@ -7205,7 +7204,7 @@ public class BatteryStatsImpl extends BatteryStats { public ControllerActivityCounterImpl getOrCreateModemControllerActivityLocked() { if (mModemControllerActivity == null) { mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase, - ModemActivityInfo.TX_POWER_LEVELS); + ModemActivityInfo.getNumTxPowerLevels()); } return mModemControllerActivity; } @@ -8687,7 +8686,7 @@ public class BatteryStatsImpl extends BatteryStats { if (in.readInt() != 0) { mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase, - ModemActivityInfo.TX_POWER_LEVELS, in); + ModemActivityInfo.getNumTxPowerLevels(), in); } else { mModemControllerActivity = null; } @@ -9953,7 +9952,7 @@ public class BatteryStatsImpl extends BatteryStats { mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, NUM_BT_TX_LEVELS); mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, - ModemActivityInfo.TX_POWER_LEVELS); + ModemActivityInfo.getNumTxPowerLevels()); mMobileRadioActiveTimer = new StopwatchTimer(mClocks, null, -400, null, mOnBatteryTimeBase); mMobileRadioActivePerAppTimer = new StopwatchTimer(mClocks, null, -401, null, mOnBatteryTimeBase); @@ -11131,26 +11130,7 @@ public class BatteryStatsImpl extends BatteryStats { } } - private ModemActivityInfo mLastModemActivityInfo = - new ModemActivityInfo(0, 0, 0, new int[0], 0); - - private ModemActivityInfo getDeltaModemActivityInfo(ModemActivityInfo activityInfo) { - if (activityInfo == null) { - return null; - } - int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS]; - for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) { - txTimeMs[i] = activityInfo.getTransmitPowerInfo().get(i).getTimeInMillis() - - mLastModemActivityInfo.getTransmitPowerInfo().get(i).getTimeInMillis(); - } - ModemActivityInfo deltaInfo = new ModemActivityInfo(activityInfo.getTimestamp(), - activityInfo.getSleepTimeMillis() - mLastModemActivityInfo.getSleepTimeMillis(), - activityInfo.getIdleTimeMillis() - mLastModemActivityInfo.getIdleTimeMillis(), - txTimeMs, - activityInfo.getReceiveTimeMillis() - mLastModemActivityInfo.getReceiveTimeMillis()); - mLastModemActivityInfo = activityInfo; - return deltaInfo; - } + private ModemActivityInfo mLastModemActivityInfo = null; /** * Distribute Cell radio energy info and network traffic to apps. @@ -11159,7 +11139,9 @@ public class BatteryStatsImpl extends BatteryStats { if (DEBUG_ENERGY) { Slog.d(TAG, "Updating mobile radio stats with " + activityInfo); } - ModemActivityInfo deltaInfo = getDeltaModemActivityInfo(activityInfo); + ModemActivityInfo deltaInfo = mLastModemActivityInfo == null ? activityInfo + : mLastModemActivityInfo.getDelta(activityInfo); + mLastModemActivityInfo = activityInfo; // Add modem tx power to history. addModemTxPowerToHistory(deltaInfo); @@ -11191,10 +11173,9 @@ public class BatteryStatsImpl extends BatteryStats { mModemActivity.getSleepTimeCounter().addCountLocked( deltaInfo.getSleepTimeMillis()); mModemActivity.getRxTimeCounter().addCountLocked(deltaInfo.getReceiveTimeMillis()); - for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) { + for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels(); lvl++) { mModemActivity.getTxTimeCounters()[lvl] - .addCountLocked(deltaInfo.getTransmitPowerInfo() - .get(lvl).getTimeInMillis()); + .addCountLocked(deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl)); } // POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V. @@ -11208,11 +11189,11 @@ public class BatteryStatsImpl extends BatteryStats { mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE) + deltaInfo.getReceiveTimeMillis() * mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX); - List<TransmitPower> txPowerInfo = deltaInfo.getTransmitPowerInfo(); - for (int i = 0; i < Math.min(txPowerInfo.size(), + for (int i = 0; i < Math.min(ModemActivityInfo.getNumTxPowerLevels(), CellSignalStrength.getNumSignalStrengthLevels()); i++) { - energyUsed += txPowerInfo.get(i).getTimeInMillis() * mPowerProfile - .getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i); + energyUsed += deltaInfo.getTransmitDurationMillisAtPowerLevel(i) + * mPowerProfile.getAveragePower( + PowerProfile.POWER_MODEM_CONTROLLER_TX, i); } // We store the power drain as mAms. @@ -11307,10 +11288,10 @@ public class BatteryStatsImpl extends BatteryStats { } if (totalTxPackets > 0 && entry.txPackets > 0) { - for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) { - long txMs = - entry.txPackets * deltaInfo.getTransmitPowerInfo() - .get(lvl).getTimeInMillis(); + for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels(); + lvl++) { + long txMs = entry.txPackets + * deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl); txMs /= totalTxPackets; activityCounter.getTxTimeCounters()[lvl].addCountLocked(txMs); } @@ -11341,20 +11322,16 @@ public class BatteryStatsImpl extends BatteryStats { if (activityInfo == null) { return; } - List<TransmitPower> txPowerInfo = activityInfo.getTransmitPowerInfo(); - if (txPowerInfo == null || txPowerInfo.size() != ModemActivityInfo.TX_POWER_LEVELS) { - return; - } final long elapsedRealtime = mClocks.elapsedRealtime(); final long uptime = mClocks.uptimeMillis(); int levelMaxTimeSpent = 0; - for (int i = 1; i < txPowerInfo.size(); i++) { - if (txPowerInfo.get(i).getTimeInMillis() > txPowerInfo.get(levelMaxTimeSpent) - .getTimeInMillis()) { + for (int i = 1; i < ModemActivityInfo.getNumTxPowerLevels(); i++) { + if (activityInfo.getTransmitDurationMillisAtPowerLevel(i) + > activityInfo.getTransmitDurationMillisAtPowerLevel(levelMaxTimeSpent)) { levelMaxTimeSpent = i; } } - if (levelMaxTimeSpent == ModemActivityInfo.TX_POWER_LEVELS - 1) { + if (levelMaxTimeSpent == ModemActivityInfo.getNumTxPowerLevels() - 1) { mHistoryCur.states2 |= HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG; addHistoryRecordLocked(elapsedRealtime, uptime); } @@ -12771,7 +12748,7 @@ public class BatteryStatsImpl extends BatteryStats { timeInRxSignalStrengthLevelMs[i] = getPhoneSignalStrengthTime(i, rawRealTime, which) / 1000; } - long[] txTimeMs = new long[Math.min(ModemActivityInfo.TX_POWER_LEVELS, + long[] txTimeMs = new long[Math.min(ModemActivityInfo.getNumTxPowerLevels(), counter.getTxTimeCounters().length)]; long totalTxTimeMs = 0; for (int i = 0; i < txTimeMs.length; i++) { @@ -14817,7 +14794,7 @@ public class BatteryStatsImpl extends BatteryStats { mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, NUM_BT_TX_LEVELS, in); mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, - ModemActivityInfo.TX_POWER_LEVELS, in); + ModemActivityInfo.getNumTxPowerLevels(), in); mHasWifiReporting = in.readInt() != 0; mHasBluetoothReporting = in.readInt() != 0; mHasModemReporting = in.readInt() != 0; diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS index ea3b3a7b4056..7766b77ab3c6 100644 --- a/core/java/com/android/internal/os/OWNERS +++ b/core/java/com/android/internal/os/OWNERS @@ -10,4 +10,6 @@ per-file BatteryUsageStats* = file:/BATTERY_STATS_OWNERS per-file *ChargeCalculator* = file:/BATTERY_STATS_OWNERS per-file *PowerCalculator* = file:/BATTERY_STATS_OWNERS per-file *PowerEstimator* = file:/BATTERY_STATS_OWNERS +per-file *Kernel* = file:/BATTERY_STATS_OWNERS +per-file *MultiState* = file:/BATTERY_STATS_OWNERS diff --git a/core/java/com/android/internal/util/IState.java b/core/java/com/android/internal/util/IState.java index 07837bf8f587..41b3d5e0a11f 100644 --- a/core/java/com/android/internal/util/IState.java +++ b/core/java/com/android/internal/util/IState.java @@ -27,12 +27,12 @@ import android.os.Message; public interface IState { /** - * Returned by processMessage to indicate the the message was processed. + * Returned by processMessage to indicate the message was processed. */ static final boolean HANDLED = true; /** - * Returned by processMessage to indicate the the message was NOT processed. + * Returned by processMessage to indicate the message was NOT processed. */ static final boolean NOT_HANDLED = false; diff --git a/core/java/com/android/internal/util/State.java b/core/java/com/android/internal/util/State.java index 4613dad8cc67..d5c0f60f4b37 100644 --- a/core/java/com/android/internal/util/State.java +++ b/core/java/com/android/internal/util/State.java @@ -16,6 +16,7 @@ package com.android.internal.util; +import android.annotation.SuppressLint; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Message; @@ -25,6 +26,7 @@ import android.os.Message; * * The class for implementing states in a StateMachine */ +@SuppressLint("AndroidFrameworkRequiresPermission") public class State implements IState { /** diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java index 4cff785cd671..cb8d9d127415 100644 --- a/core/java/com/android/internal/util/StateMachine.java +++ b/core/java/com/android/internal/util/StateMachine.java @@ -48,7 +48,7 @@ import java.util.Vector; * in Object Oriented programming and are used to perform initialization and * cleanup of the state respectively. The <code>getName</code> method returns the * name of the state; the default implementation returns the class name. It may be - * desirable to have <code>getName</code> return the the state instance name instead, + * desirable to have <code>getName</code> return the state instance name instead, * in particular if a particular state class has multiple instances.</p> * * <p>When a state machine is created, <code>addState</code> is used to build the @@ -433,14 +433,14 @@ public class StateMachine { /** * Convenience constant that maybe returned by processMessage - * to indicate the the message was processed and is not to be + * to indicate the message was processed and is not to be * processed by parent states */ public static final boolean HANDLED = true; /** * Convenience constant that maybe returned by processMessage - * to indicate the the message was NOT processed and is to be + * to indicate the message was NOT processed and is to be * processed by parent states */ public static final boolean NOT_HANDLED = false; diff --git a/core/jni/OWNERS b/core/jni/OWNERS index 701960eb4f11..6fb2904ab63c 100644 --- a/core/jni/OWNERS +++ b/core/jni/OWNERS @@ -75,3 +75,7 @@ per-file android_view_* = file:/graphics/java/android/graphics/OWNERS # VINTF per-file android_os_VintfObject* = file:platform/system/libvintf:/OWNERS per-file android_os_VintfRuntimeInfo* = file:platform/system/libvintf:/OWNERS + +# Battery +per-file com_android_internal_os_Kernel* = file:/BATTERY_STATS_OWNERS +per-file com_android_internal_os_*MultiStateCounter* = file:/BATTERY_STATS_OWNERS diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp index 241570a7f9d3..ac320386c3f3 100644 --- a/core/jni/android_os_Parcel.cpp +++ b/core/jni/android_os_Parcel.cpp @@ -98,6 +98,15 @@ static void android_os_Parcel_markSensitive(jlong nativePtr) } } +static void android_os_Parcel_markForBinder(JNIEnv* env, jclass clazz, jlong nativePtr, + jobject binder) +{ + Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr); + if (parcel) { + parcel->markForBinder(ibinderForJavaObject(env, binder)); + } +} + static jint android_os_Parcel_dataSize(jlong nativePtr) { Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr); @@ -766,7 +775,9 @@ static jboolean android_os_Parcel_replaceCallingWorkSourceUid(jlong nativePtr, j static const JNINativeMethod gParcelMethods[] = { // @CriticalNative - {"nativeMarkSensitive", "(J)V", (void*)android_os_Parcel_markSensitive}, + {"nativeMarkSensitive", "(J)V", (void*)android_os_Parcel_markSensitive}, + // @FastNative + {"nativeMarkForBinder", "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_markForBinder}, // @CriticalNative {"nativeDataSize", "(J)I", (void*)android_os_Parcel_dataSize}, // @CriticalNative diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 515c08d8517e..288327ef61ba 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -1129,14 +1129,14 @@ static void isolateAppDataPerPackage(int userId, std::string_view package_name, } // Relabel directory -static void relabelDir(const char* path, security_context_t context, fail_fn_t fail_fn) { +static void relabelDir(const char* path, const char* context, fail_fn_t fail_fn) { if (setfilecon(path, context) != 0) { fail_fn(CREATE_ERROR("Failed to setfilecon %s %s", path, strerror(errno))); } } // Relabel all directories under a path non-recursively. -static void relabelAllDirs(const char* path, security_context_t context, fail_fn_t fail_fn) { +static void relabelAllDirs(const char* path, const char* context, fail_fn_t fail_fn) { DIR* dir = opendir(path); if (dir == nullptr) { fail_fn(CREATE_ERROR("Failed to opendir %s", path)); @@ -1211,7 +1211,7 @@ static void isolateAppData(JNIEnv* env, const std::vector<std::string>& merged_d snprintf(internalDePath, PATH_MAX, "/data/user_de"); snprintf(externalPrivateMountPath, PATH_MAX, "/mnt/expand"); - security_context_t dataDataContext = nullptr; + char* dataDataContext = nullptr; if (getfilecon(internalDePath, &dataDataContext) < 0) { fail_fn(CREATE_ERROR("Unable to getfilecon on %s %s", internalDePath, strerror(errno))); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 0135e45c9b54..35a3cde2d8f9 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -196,6 +196,9 @@ android:name="android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED" /> <protected-broadcast android:name="android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED" /> + <protected-broadcast android:name="android.bluetooth.action.CSIS_CONNECTION_STATE_CHANGED" /> + <protected-broadcast android:name="android.bluetooth.action.CSIS_DEVICE_AVAILABLE" /> + <protected-broadcast android:name="android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE" /> <protected-broadcast android:name="android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED" /> <protected-broadcast diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 6a4702be35c9..c1c185823d33 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3046,6 +3046,9 @@ <public name="canPauseRecording" /> <!-- attribute definitions go here --> <public name="requireDeviceScreenOn" /> + </public-group> + + <public-group type="attr" first-id="0x01010624"> <public name="memtagMode" /> <public name="nativeHeapZeroInitialized" /> </public-group> diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml index 37d059a575f0..f1c66c5891f2 100644 --- a/core/res/res/xml/sms_short_codes.xml +++ b/core/res/res/xml/sms_short_codes.xml @@ -265,7 +265,7 @@ <!-- USA: 5-6 digits (premium codes from https://www.premiumsmsrefunds.com/ShortCodes.htm), visual voicemail code for T-Mobile: 122 --> - <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" standard="44567|244444" free="122|87902|21696|24614|28003|30356|33669|40196|41064|41270|43753|44034|46645|52413|56139|57969|61785|66975|75136|76227|81398|83952|85140|86566|86799|95737|96684|99245" /> + <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" standard="44567|244444" free="122|87902|21696|24614|28003|30356|33669|40196|41064|41270|43753|44034|46645|52413|56139|57969|61785|66975|75136|76227|81398|83952|85140|86566|86799|95737|96684|99245|611611" /> <!-- Vietnam: 1-5 digits (standard system default, not country specific) --> <shortcode country="vn" pattern="\\d{1,5}" free="5001|9055" /> diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index 000e870369db..2d63351b8303 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -88,6 +88,11 @@ public class ActivityThreadTest { false /* launchActivity */); @Test + public void testTemporaryDirectory() throws Exception { + assertEquals(System.getProperty("java.io.tmpdir"), System.getenv("TMPDIR")); + } + + @Test public void testDoubleRelaunch() throws Exception { final Activity activity = mActivityTestRule.launchActivity(new Intent()); final IApplicationThread appThread = activity.getActivityThread().getApplicationThread(); diff --git a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java index edf473eac1b1..b85cb9cec47d 100644 --- a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java @@ -542,83 +542,83 @@ public class StateMachineTest extends TestCase { public void testStateMachineEnterExitTransitionToTest() throws Exception { //if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger(); - StateMachineEnterExitTransitionToTest smEnterExitTranstionToTest = - new StateMachineEnterExitTransitionToTest("smEnterExitTranstionToTest"); - smEnterExitTranstionToTest.start(); - if (smEnterExitTranstionToTest.isDbg()) { + StateMachineEnterExitTransitionToTest smEnterExitTransitionToTest = + new StateMachineEnterExitTransitionToTest("smEnterExitTransitionToTest"); + smEnterExitTransitionToTest.start(); + if (smEnterExitTransitionToTest.isDbg()) { tlog("testStateMachineEnterExitTransitionToTest E"); } - synchronized (smEnterExitTranstionToTest) { - smEnterExitTranstionToTest.sendMessage(TEST_CMD_1); + synchronized (smEnterExitTransitionToTest) { + smEnterExitTransitionToTest.sendMessage(TEST_CMD_1); try { // wait for the messages to be handled - smEnterExitTranstionToTest.wait(); + smEnterExitTransitionToTest.wait(); } catch (InterruptedException e) { tloge("testStateMachineEnterExitTransitionToTest: exception while waiting " + e.getMessage()); } } - dumpLogRecs(smEnterExitTranstionToTest); + dumpLogRecs(smEnterExitTransitionToTest); - assertEquals(9, smEnterExitTranstionToTest.getLogRecCount()); + assertEquals(9, smEnterExitTransitionToTest.getLogRecCount()); LogRec lr; - lr = smEnterExitTranstionToTest.getLogRec(0); + lr = smEnterExitTransitionToTest.getLogRec(0); assertEquals(ENTER, lr.getInfo()); - assertEquals(smEnterExitTranstionToTest.mS1, lr.getState()); + assertEquals(smEnterExitTransitionToTest.mS1, lr.getState()); - lr = smEnterExitTranstionToTest.getLogRec(1); + lr = smEnterExitTransitionToTest.getLogRec(1); assertEquals(EXIT, lr.getInfo()); - assertEquals(smEnterExitTranstionToTest.mS1, lr.getState()); + assertEquals(smEnterExitTransitionToTest.mS1, lr.getState()); - lr = smEnterExitTranstionToTest.getLogRec(2); + lr = smEnterExitTransitionToTest.getLogRec(2); assertEquals(ENTER, lr.getInfo()); - assertEquals(smEnterExitTranstionToTest.mS2, lr.getState()); + assertEquals(smEnterExitTransitionToTest.mS2, lr.getState()); - lr = smEnterExitTranstionToTest.getLogRec(3); + lr = smEnterExitTransitionToTest.getLogRec(3); assertEquals(TEST_CMD_1, lr.getWhat()); - assertEquals(smEnterExitTranstionToTest.mS2, lr.getState()); - assertEquals(smEnterExitTranstionToTest.mS2, lr.getOriginalState()); - assertEquals(smEnterExitTranstionToTest.mS3, lr.getDestState()); + assertEquals(smEnterExitTransitionToTest.mS2, lr.getState()); + assertEquals(smEnterExitTransitionToTest.mS2, lr.getOriginalState()); + assertEquals(smEnterExitTransitionToTest.mS3, lr.getDestState()); - lr = smEnterExitTranstionToTest.getLogRec(4); + lr = smEnterExitTransitionToTest.getLogRec(4); assertEquals(TEST_CMD_1, lr.getWhat()); - assertEquals(smEnterExitTranstionToTest.mS2, lr.getState()); - assertEquals(smEnterExitTranstionToTest.mS2, lr.getOriginalState()); - assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState()); + assertEquals(smEnterExitTransitionToTest.mS2, lr.getState()); + assertEquals(smEnterExitTransitionToTest.mS2, lr.getOriginalState()); + assertEquals(smEnterExitTransitionToTest.mS4, lr.getDestState()); assertEquals(EXIT, lr.getInfo()); - lr = smEnterExitTranstionToTest.getLogRec(5); + lr = smEnterExitTransitionToTest.getLogRec(5); assertEquals(TEST_CMD_1, lr.getWhat()); assertEquals(ENTER, lr.getInfo()); - assertEquals(smEnterExitTranstionToTest.mS3, lr.getState()); - assertEquals(smEnterExitTranstionToTest.mS3, lr.getOriginalState()); - assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState()); + assertEquals(smEnterExitTransitionToTest.mS3, lr.getState()); + assertEquals(smEnterExitTransitionToTest.mS3, lr.getOriginalState()); + assertEquals(smEnterExitTransitionToTest.mS4, lr.getDestState()); - lr = smEnterExitTranstionToTest.getLogRec(6); + lr = smEnterExitTransitionToTest.getLogRec(6); assertEquals(TEST_CMD_1, lr.getWhat()); assertEquals(EXIT, lr.getInfo()); - assertEquals(smEnterExitTranstionToTest.mS3, lr.getState()); - assertEquals(smEnterExitTranstionToTest.mS3, lr.getOriginalState()); - assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState()); + assertEquals(smEnterExitTransitionToTest.mS3, lr.getState()); + assertEquals(smEnterExitTransitionToTest.mS3, lr.getOriginalState()); + assertEquals(smEnterExitTransitionToTest.mS4, lr.getDestState()); - lr = smEnterExitTranstionToTest.getLogRec(7); + lr = smEnterExitTransitionToTest.getLogRec(7); assertEquals(TEST_CMD_1, lr.getWhat()); assertEquals(ENTER, lr.getInfo()); - assertEquals(smEnterExitTranstionToTest.mS4, lr.getState()); - assertEquals(smEnterExitTranstionToTest.mS4, lr.getOriginalState()); - assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState()); + assertEquals(smEnterExitTransitionToTest.mS4, lr.getState()); + assertEquals(smEnterExitTransitionToTest.mS4, lr.getOriginalState()); + assertEquals(smEnterExitTransitionToTest.mS4, lr.getDestState()); - lr = smEnterExitTranstionToTest.getLogRec(8); + lr = smEnterExitTransitionToTest.getLogRec(8); assertEquals(TEST_CMD_1, lr.getWhat()); assertEquals(EXIT, lr.getInfo()); - assertEquals(smEnterExitTranstionToTest.mS4, lr.getState()); - assertEquals(smEnterExitTranstionToTest.mS4, lr.getOriginalState()); + assertEquals(smEnterExitTransitionToTest.mS4, lr.getState()); + assertEquals(smEnterExitTransitionToTest.mS4, lr.getOriginalState()); - if (smEnterExitTranstionToTest.isDbg()) { + if (smEnterExitTransitionToTest.isDbg()) { tlog("testStateMachineEnterExitTransitionToTest X"); } } diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java index 8f175bb63edb..1e685856d011 100644 --- a/identity/java/android/security/identity/IdentityCredential.java +++ b/identity/java/android/security/identity/IdentityCredential.java @@ -160,7 +160,7 @@ public abstract class IdentityCredential { * not the case, the {@link SessionTranscriptMismatchException} exception is thrown. * * <p>If not {@code null} the {@code requestMessage} parameter must contain data for the request - * from the verifier. The content can be defined in the way appropriate for the credential, byt + * from the verifier. The content can be defined in the way appropriate for the credential, but * there are three requirements that must be met to work with this API: * <ul> * <li>The content must be a CBOR-encoded structure.</li> @@ -205,9 +205,9 @@ public abstract class IdentityCredential { * must appear somewhere in {@code sessionTranscript} and ditto for the 32 bytes for the Y * coordinate. * - * <p>If {@code readerAuth} is not {@code null} it must be the bytes of a {@code COSE_Sign1} - * structure as defined in RFC 8152. For the payload nil shall be used and the - * detached payload is the ReaderAuthenticationBytes CBOR described below. + * <p>If {@code readerSignature} is not {@code null} it must be the bytes of a + * {@code COSE_Sign1} structure as defined in RFC 8152. For the payload nil shall be used and + * the detached payload is the ReaderAuthenticationBytes CBOR described below. * <pre> * ReaderAuthentication = [ * "ReaderAuthentication", diff --git a/libs/hwui/jni/PaintFilter.cpp b/libs/hwui/jni/PaintFilter.cpp index ec115b4e141c..86d4742b949e 100644 --- a/libs/hwui/jni/PaintFilter.cpp +++ b/libs/hwui/jni/PaintFilter.cpp @@ -74,7 +74,7 @@ int register_android_graphics_DrawFilter(JNIEnv* env) { result |= RegisterMethodsOrDie(env, "android/graphics/PaintFlagsDrawFilter", paintflags_methods, NELEM(paintflags_methods)); - return 0; + return result; } } diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 9657b25e7c18..c7c503d64801 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -2178,12 +2178,6 @@ public final class MediaCodecInfo { if (size == null || size.getWidth() * size.getHeight() <= 0) { continue; } - if (size.getWidth() > SIZE_RANGE.getUpper() - || size.getHeight() > SIZE_RANGE.getUpper()) { - size = new Size( - Math.min(size.getWidth(), SIZE_RANGE.getUpper()), - Math.min(size.getHeight(), SIZE_RANGE.getUpper())); - } Range<Long> range = Utils.parseLongRange(map.get(key), null); if (range == null || range.getLower() < 0 || range.getUpper() < 0) { continue; diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java index 559a61daec38..0f9e89a497d2 100644 --- a/media/java/android/media/MediaMetadataRetriever.java +++ b/media/java/android/media/MediaMetadataRetriever.java @@ -900,7 +900,7 @@ public class MediaMetadataRetriever implements AutoCloseable { private @NonNull List<Bitmap> getFramesAtIndexInternal( int frameIndex, int numFrames, @Nullable BitmapParams params) { if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO))) { - throw new IllegalStateException("Does not contail video or image sequences"); + throw new IllegalStateException("Does not contain video or image sequences"); } int frameCount = Integer.parseInt( extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT)); @@ -1018,7 +1018,7 @@ public class MediaMetadataRetriever implements AutoCloseable { private Bitmap getImageAtIndexInternal(int imageIndex, @Nullable BitmapParams params) { if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE))) { - throw new IllegalStateException("Does not contail still images"); + throw new IllegalStateException("Does not contain still images"); } String imageCount = extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT); diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp index 0d53ab152129..2691983a0e3a 100644 --- a/media/jni/audioeffect/android_media_AudioEffect.cpp +++ b/media/jni/audioeffect/android_media_AudioEffect.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ - #include <stdio.h> +#include <unordered_set> //#define LOG_NDEBUG 0 #define LOG_TAG "AudioEffects-JNI" @@ -58,22 +58,15 @@ static fields_t fields; struct effect_callback_cookie { jclass audioEffect_class; // AudioEffect class jobject audioEffect_ref; // AudioEffect object instance - }; + bool busy; + Condition cond; +}; // ---------------------------------------------------------------------------- -class AudioEffectJniStorage { - public: - effect_callback_cookie mCallbackData; - - AudioEffectJniStorage() { - } - - ~AudioEffectJniStorage() { - } - +struct AudioEffectJniStorage { + effect_callback_cookie mCallbackData{}; }; - jint AudioEffectJni::translateNativeErrorToJava(int code) { switch(code) { case NO_ERROR: @@ -101,6 +94,7 @@ jint AudioEffectJni::translateNativeErrorToJava(int code) { } static Mutex sLock; +static std::unordered_set<effect_callback_cookie*> sAudioEffectCallBackCookies; // ---------------------------------------------------------------------------- static void effectCallback(int event, void* user, void *info) { @@ -121,7 +115,13 @@ static void effectCallback(int event, void* user, void *info) { ALOGW("effectCallback error user %p, env %p", user, env); return; } - + { + Mutex::Autolock l(sLock); + if (sAudioEffectCallBackCookies.count(callbackInfo) == 0) { + return; + } + callbackInfo->busy = true; + } ALOGV("effectCallback: callbackInfo %p, audioEffect_ref %p audioEffect_class %p", callbackInfo, callbackInfo->audioEffect_ref, @@ -188,6 +188,11 @@ effectCallback_Exit: env->ExceptionDescribe(); env->ExceptionClear(); } + { + Mutex::Autolock l(sLock); + callbackInfo->busy = false; + callbackInfo->cond.broadcast(); + } } // ---------------------------------------------------------------------------- @@ -396,6 +401,10 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t setAudioEffect(env, thiz, lpAudioEffect); } + { + Mutex::Autolock l(sLock); + sAudioEffectCallBackCookies.insert(&lpJniStorage->mCallbackData); + } env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage); return (jint) AUDIOEFFECT_SUCCESS; @@ -427,6 +436,7 @@ setup_failure: // ---------------------------------------------------------------------------- +#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000 static void android_media_AudioEffect_native_release(JNIEnv *env, jobject thiz) { sp<AudioEffect> lpAudioEffect = setAudioEffect(env, thiz, 0); if (lpAudioEffect == 0) { @@ -442,7 +452,17 @@ static void android_media_AudioEffect_native_release(JNIEnv *env, jobject thiz) env->SetLongField(thiz, fields.fidJniData, 0); if (lpJniStorage) { - ALOGV("deleting pJniStorage: %p\n", lpJniStorage); + Mutex::Autolock l(sLock); + effect_callback_cookie *lpCookie = &lpJniStorage->mCallbackData; + ALOGV("deleting lpJniStorage: %p\n", lpJniStorage); + sAudioEffectCallBackCookies.erase(lpCookie); + while (lpCookie->busy) { + if (lpCookie->cond.waitRelative(sLock, + milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) != + NO_ERROR) { + break; + } + } env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_class); env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_ref); delete lpJniStorage; diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp index 4c5970a30a05..609fafee4d6a 100644 --- a/media/jni/audioeffect/android_media_Visualizer.cpp +++ b/media/jni/audioeffect/android_media_Visualizer.cpp @@ -15,6 +15,7 @@ */ #include <stdio.h> +#include <unordered_set> //#define LOG_NDEBUG 0 #define LOG_TAG "visualizers-JNI" @@ -63,6 +64,12 @@ struct visualizer_callback_cookie { jclass visualizer_class; // Visualizer class jobject visualizer_ref; // Visualizer object instance + // 'busy_count' and 'cond' together with 'sLock' are used to serialize + // concurrent access to the callback cookie from 'setup'/'release' + // and the callback. + int busy_count; + Condition cond; + // Lazily allocated arrays used to hold callback data provided to java // applications. These arrays are allocated during the first callback and // reallocated when the size of the callback data changes. Allocating on @@ -70,14 +77,12 @@ struct visualizer_callback_cookie { // reference to the provided data (they need to make a copy if they want to // hold onto outside of the callback scope), but it avoids GC thrash caused // by constantly allocating and releasing arrays to hold callback data. + // 'callback_data_lock' must never be held at the same time with 'sLock'. Mutex callback_data_lock; jbyteArray waveform_data; jbyteArray fft_data; - visualizer_callback_cookie() { - waveform_data = NULL; - fft_data = NULL; - } + // Assumes use of default initialization by the client. ~visualizer_callback_cookie() { cleanupBuffers(); @@ -102,15 +107,8 @@ struct visualizer_callback_cookie { }; // ---------------------------------------------------------------------------- -class VisualizerJniStorage { - public: - visualizer_callback_cookie mCallbackData; - - VisualizerJniStorage() { - } - - ~VisualizerJniStorage() { - } +struct VisualizerJniStorage { + visualizer_callback_cookie mCallbackData{}; }; @@ -136,6 +134,7 @@ static jint translateError(int code) { } static Mutex sLock; +static std::unordered_set<visualizer_callback_cookie*> sVisualizerCallBackCookies; // ---------------------------------------------------------------------------- static void ensureArraySize(JNIEnv *env, jbyteArray *array, uint32_t size) { @@ -173,11 +172,19 @@ static void captureCallback(void* user, return; } + { + Mutex::Autolock l(sLock); + if (sVisualizerCallBackCookies.count(callbackInfo) == 0) { + return; + } + callbackInfo->busy_count++; + } ALOGV("captureCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p", callbackInfo, callbackInfo->visualizer_ref, callbackInfo->visualizer_class); + { AutoMutex lock(&callbackInfo->callback_data_lock); if (waveformSize != 0 && waveform != NULL) { @@ -219,11 +226,17 @@ static void captureCallback(void* user, jArray); } } + } // callback_data_lock scope if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); } + { + Mutex::Autolock l(sLock); + callbackInfo->busy_count--; + callbackInfo->cond.broadcast(); + } } // ---------------------------------------------------------------------------- @@ -332,16 +345,41 @@ static void android_media_visualizer_effect_callback(int32_t event, void *info) { if ((event == AudioEffect::EVENT_ERROR) && (*((status_t*)info) == DEAD_OBJECT)) { - VisualizerJniStorage* lpJniStorage = (VisualizerJniStorage*)user; - visualizer_callback_cookie* callbackInfo = &lpJniStorage->mCallbackData; + visualizer_callback_cookie* callbackInfo = + (visualizer_callback_cookie *)user; JNIEnv *env = AndroidRuntime::getJNIEnv(); + if (!user || !env) { + ALOGW("effectCallback error user %p, env %p", user, env); + return; + } + { + Mutex::Autolock l(sLock); + if (sVisualizerCallBackCookies.count(callbackInfo) == 0) { + return; + } + callbackInfo->busy_count++; + } + ALOGV("effectCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p", + callbackInfo, + callbackInfo->visualizer_ref, + callbackInfo->visualizer_class); + env->CallStaticVoidMethod( callbackInfo->visualizer_class, fields.midPostNativeEvent, callbackInfo->visualizer_ref, NATIVE_EVENT_SERVER_DIED, 0, NULL); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + { + Mutex::Autolock l(sLock); + callbackInfo->busy_count--; + callbackInfo->cond.broadcast(); + } } } @@ -389,7 +427,7 @@ android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_th } lpVisualizer->set(0, android_media_visualizer_effect_callback, - lpJniStorage, + &lpJniStorage->mCallbackData, (audio_session_t) sessionId); lStatus = translateError(lpVisualizer->initCheck()); @@ -410,6 +448,10 @@ android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_th setVisualizer(env, thiz, lpVisualizer); + { + Mutex::Autolock l(sLock); + sVisualizerCallBackCookies.insert(&lpJniStorage->mCallbackData); + } env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage); return VISUALIZER_SUCCESS; @@ -432,13 +474,15 @@ setup_failure: } // ---------------------------------------------------------------------------- +#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000 static void android_media_visualizer_native_release(JNIEnv *env, jobject thiz) { - { //limit scope so that lpVisualizer is deleted before JNI storage data. + { sp<Visualizer> lpVisualizer = setVisualizer(env, thiz, 0); if (lpVisualizer == 0) { return; } lpVisualizer->release(); + // Visualizer can still can be held by AudioEffect::EffectClient } // delete the JNI data VisualizerJniStorage* lpJniStorage = @@ -449,9 +493,22 @@ static void android_media_visualizer_native_release(JNIEnv *env, jobject thiz) env->SetLongField(thiz, fields.fidJniData, 0); if (lpJniStorage) { + { + Mutex::Autolock l(sLock); + visualizer_callback_cookie *lpCookie = &lpJniStorage->mCallbackData; ALOGV("deleting pJniStorage: %p\n", lpJniStorage); + sVisualizerCallBackCookies.erase(lpCookie); + while (lpCookie->busy_count > 0) { + if (lpCookie->cond.waitRelative(sLock, + milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) != + NO_ERROR) { + break; + } + } + ALOG_ASSERT(lpCookie->busy_count == 0, "Unbalanced busy_count inc/dec"); env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_class); env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_ref); + } // sLock scope delete lpJniStorage; } } @@ -707,4 +764,3 @@ int register_android_media_visualizer(JNIEnv *env) { return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); } - diff --git a/media/tests/EffectsTest/AndroidManifest.xml b/media/tests/EffectsTest/AndroidManifest.xml index 9b59891fb279..ad0c10ee3807 100644 --- a/media/tests/EffectsTest/AndroidManifest.xml +++ b/media/tests/EffectsTest/AndroidManifest.xml @@ -13,6 +13,10 @@ See the License for the specific language governing permissions and limitations under the License. --> +<!-- +Make sure to enable access to the mic in settings and run: +adb shell am compat enable ALLOW_TEST_API_ACCESS com.android.effectstest +--> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.effectstest"> diff --git a/media/tests/EffectsTest/res/layout/bassboosttest.xml b/media/tests/EffectsTest/res/layout/bassboosttest.xml index ac912c84d107..5f9132cb8cc7 100644 --- a/media/tests/EffectsTest/res/layout/bassboosttest.xml +++ b/media/tests/EffectsTest/res/layout/bassboosttest.xml @@ -187,6 +187,11 @@ android:layout_height="wrap_content" android:scaleType="fitXY"/> + <Button android:id="@+id/hammer_on_release_bug" + android:layout_width="fill_parent" android:layout_height="wrap_content" + android:text="@string/hammer_on_release_bug_name"> + </Button> + </LinearLayout> </ScrollView> diff --git a/media/tests/EffectsTest/res/layout/visualizertest.xml b/media/tests/EffectsTest/res/layout/visualizertest.xml index 18d7a3648fbf..85dabbc115f3 100644 --- a/media/tests/EffectsTest/res/layout/visualizertest.xml +++ b/media/tests/EffectsTest/res/layout/visualizertest.xml @@ -175,6 +175,11 @@ </LinearLayout> + <Button android:id="@+id/hammer_on_release_bug" + android:layout_width="fill_parent" android:layout_height="wrap_content" + android:text="@string/hammer_on_release_bug_name"> + </Button> + <ImageView android:src="@android:drawable/divider_horizontal_dark" android:layout_width="fill_parent" diff --git a/media/tests/EffectsTest/res/values/strings.xml b/media/tests/EffectsTest/res/values/strings.xml index 7c12da1274e3..a44c7e93382a 100644 --- a/media/tests/EffectsTest/res/values/strings.xml +++ b/media/tests/EffectsTest/res/values/strings.xml @@ -37,4 +37,6 @@ <string name="send_level_name">Send Level</string> <!-- Toggles use of a multi-threaded client for an effect [CHAR LIMIT=24] --> <string name="effect_multithreaded">Multithreaded Use</string> + <!-- Runs a stress test for a bug related to simultaneous release of multiple effect instances [CHAR LIMIT=24] --> + <string name="hammer_on_release_bug_name">Hammer on release()</string> </resources> diff --git a/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java b/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java index cce2acc5869a..a207bf1d5359 100644 --- a/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java +++ b/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java @@ -17,29 +17,24 @@ package com.android.effectstest; import android.app.Activity; -import android.content.Context; -import android.content.Intent; +import android.media.audiofx.AudioEffect; +import android.media.audiofx.BassBoost; import android.os.Bundle; import android.util.Log; import android.view.KeyEvent; -import android.view.Menu; -import android.view.View.OnClickListener; import android.view.View; -import android.view.ViewGroup; +import android.view.View.OnClickListener; import android.widget.Button; -import android.widget.TextView; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.EditText; import android.widget.SeekBar; +import android.widget.TextView; import android.widget.ToggleButton; -import android.widget.CompoundButton; -import android.widget.CompoundButton.OnCheckedChangeListener; -import java.nio.ByteOrder; + import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.HashMap; -import java.util.Map; - -import android.media.audiofx.BassBoost; -import android.media.audiofx.AudioEffect; public class BassBoostTest extends Activity implements OnCheckedChangeListener { @@ -78,6 +73,9 @@ public class BassBoostTest extends Activity implements OnCheckedChangeListener { mReleaseButton = (ToggleButton)findViewById(R.id.bbReleaseButton); mOnOffButton = (ToggleButton)findViewById(R.id.bassboostOnOff); + final Button hammerReleaseTest = (Button) findViewById(R.id.hammer_on_release_bug); + hammerReleaseTest.setEnabled(false); + getEffect(sSession); if (mBassBoost != null) { @@ -93,6 +91,14 @@ public class BassBoostTest extends Activity implements OnCheckedChangeListener { mStrength = new BassBoostParam(mBassBoost, 0, 1000, seekBar, textView); seekBar.setOnSeekBarChangeListener(mStrength); mStrength.setEnabled(mBassBoost.getStrengthSupported()); + + hammerReleaseTest.setEnabled(true); + hammerReleaseTest.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + runHammerReleaseTest(hammerReleaseTest); + } + }); } } @@ -273,4 +279,52 @@ public class BassBoostTest extends Activity implements OnCheckedChangeListener { } } + // Stress-tests releasing of AudioEffect by doing repeated creation + // and subsequent releasing. Also forces emission of callbacks from + // the AudioFlinger by setting a control status listener. Since all + // effect instances are bound to the same session, the AF will + // notify them about the change in their status. This can reveal racy + // behavior w.r.t. releasing. + class HammerReleaseTest extends Thread { + private static final int NUM_EFFECTS = 10; + private static final int NUM_ITERATIONS = 100; + private final int mSession; + private final Runnable mOnComplete; + + HammerReleaseTest(int session, Runnable onComplete) { + mSession = session; + mOnComplete = onComplete; + } + + @Override + public void run() { + Log.w(TAG, "HammerReleaseTest started"); + BassBoost[] effects = new BassBoost[NUM_EFFECTS]; + for (int i = 0; i < NUM_ITERATIONS; i++) { + for (int j = 0; j < NUM_EFFECTS; j++) { + effects[j] = new BassBoost(0, mSession); + effects[j].setControlStatusListener(mEffectListener); + yield(); + } + for (int j = NUM_EFFECTS - 1; j >= 0; j--) { + Log.w(TAG, "HammerReleaseTest releasing effect " + (Object) effects[j]); + effects[j].release(); + effects[j] = null; + yield(); + } + } + Log.w(TAG, "HammerReleaseTest ended"); + runOnUiThread(mOnComplete); + } + } + + private void runHammerReleaseTest(Button controlButton) { + controlButton.setEnabled(false); + HammerReleaseTest thread = new HammerReleaseTest(sSession, + () -> { + controlButton.setEnabled(true); + }); + thread.start(); + } + } diff --git a/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java index 2e141c5ef7c8..dcfe11a79ef9 100644 --- a/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java +++ b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java @@ -17,6 +17,7 @@ package com.android.effectstest; import android.app.Activity; +import android.media.audiofx.Visualizer; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -24,6 +25,8 @@ import android.os.Message; import android.util.Log; import android.view.KeyEvent; import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.EditText; @@ -74,11 +77,22 @@ public class VisualizerTest extends Activity implements OnCheckedChangeListener mCallbackOn = false; mCallbackButton.setChecked(mCallbackOn); + final Button hammerReleaseTest = (Button) findViewById(R.id.hammer_on_release_bug); + hammerReleaseTest.setEnabled(false); + mMultithreadedButton.setOnCheckedChangeListener(this); if (getEffect(sSession) != null) { mReleaseButton.setOnCheckedChangeListener(this); mOnOffButton.setOnCheckedChangeListener(this); mCallbackButton.setOnCheckedChangeListener(this); + + hammerReleaseTest.setEnabled(true); + hammerReleaseTest.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + runHammerReleaseTest(hammerReleaseTest); + } + }); } } @@ -214,4 +228,50 @@ public class VisualizerTest extends Activity implements OnCheckedChangeListener } } + // Stress-tests releasing of AudioEffect by doing repeated creation + // and subsequent releasing. Unlike a similar class in BassBoostTest, + // this one doesn't sets a control status listener because Visualizer + // doesn't inherit from AudioEffect and doesn't implement this method + // by itself. + class HammerReleaseTest extends Thread { + private static final int NUM_EFFECTS = 10; + private static final int NUM_ITERATIONS = 100; + private final int mSession; + private final Runnable mOnComplete; + + HammerReleaseTest(int session, Runnable onComplete) { + mSession = session; + mOnComplete = onComplete; + } + + @Override + public void run() { + Log.w(TAG, "HammerReleaseTest started"); + Visualizer[] effects = new Visualizer[NUM_EFFECTS]; + for (int i = 0; i < NUM_ITERATIONS; i++) { + for (int j = 0; j < NUM_EFFECTS; j++) { + effects[j] = new Visualizer(mSession); + yield(); + } + for (int j = NUM_EFFECTS - 1; j >= 0; j--) { + Log.w(TAG, "HammerReleaseTest releasing effect " + (Object) effects[j]); + effects[j].release(); + effects[j] = null; + yield(); + } + } + Log.w(TAG, "HammerReleaseTest ended"); + runOnUiThread(mOnComplete); + } + } + + private void runHammerReleaseTest(Button controlButton) { + controlButton.setEnabled(false); + HammerReleaseTest thread = new HammerReleaseTest(sSession, + () -> { + controlButton.setEnabled(true); + }); + thread.start(); + } + } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index 59d8acb82196..a0869189a5bd 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -293,6 +293,8 @@ public class BluetoothEventManager { BluetoothDevice device) { short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE); String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME); + final boolean isCoordinatedSetMember = + intent.getBooleanExtra(BluetoothDevice.EXTRA_IS_COORDINATED_SET_MEMBER, false); // TODO Pick up UUID. They should be available for 2.1 devices. // Skip for now, there's a bluez problem and we are not getting uuids even for 2.1. CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); @@ -307,6 +309,7 @@ public class BluetoothEventManager { } cachedDevice.setRssi(rssi); cachedDevice.setJustDiscovered(true); + cachedDevice.setIsCoordinatedSetMember(isCoordinatedSetMember); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 4c80b91f300d..6a590c2ff382 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -80,6 +80,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> boolean mJustDiscovered; + boolean mIsCoordinatedSetMember = false; + private final Collection<Callback> mCallbacks = new CopyOnWriteArrayList<>(); /** @@ -297,6 +299,24 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> return mHiSyncId != BluetoothHearingAid.HI_SYNC_ID_INVALID; } + /** + * Mark the discovered device as member of coordinated set. + * + * @param isCoordinatedSetMember {@code true}, if the device is a member of a coordinated set. + */ + public void setIsCoordinatedSetMember(boolean isCoordinatedSetMember) { + mIsCoordinatedSetMember = isCoordinatedSetMember; + } + + /** + * Check if the device is a CSIP member device. + * + * @return {@code true}, if this device supports CSIP, otherwise returns {@code false}. + */ + public boolean isCoordinatedSetMemberDevice() { + return mIsCoordinatedSetMember; + } + void onBondingDockConnect() { // Attempt to connect if UUIDs are available. Otherwise, // we will connect when the ACTION_UUID intent arrives. diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java new file mode 100644 index 000000000000..754914ff794f --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java @@ -0,0 +1,236 @@ +/* + * Copyright 2021 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.bluetooth; + +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothClass; +import android.bluetooth.BluetoothCsipSetCoordinator; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; +import android.content.Context; +import android.util.Log; + +import androidx.annotation.RequiresApi; + +import com.android.settingslib.R; + +import java.util.ArrayList; +import java.util.List; + +/** + * CSIP Set Coordinator handles Bluetooth CSIP Set Coordinator role profile. + */ +public class CsipSetCoordinatorProfile implements LocalBluetoothProfile { + private static final String TAG = "CsipSetCoordinatorProfile"; + private static final boolean VDBG = true; + + private Context mContext; + + private BluetoothCsipSetCoordinator mService; + private boolean mIsProfileReady; + + private final CachedBluetoothDeviceManager mDeviceManager; + + static final String NAME = "CSIP Set Coordinator"; + private final LocalBluetoothProfileManager mProfileManager; + + // Order of this profile in device profiles list + private static final int ORDINAL = 1; + + // These callbacks run on the main thread. + private final class CoordinatedSetServiceListener implements BluetoothProfile.ServiceListener { + @RequiresApi(32) + public void onServiceConnected(int profile, BluetoothProfile proxy) { + if (VDBG) { + Log.d(TAG, "Bluetooth service connected"); + } + mService = (BluetoothCsipSetCoordinator) proxy; + // We just bound to the service, so refresh the UI for any connected CSIP devices. + List<BluetoothDevice> deviceList = mService.getConnectedDevices(); + while (!deviceList.isEmpty()) { + BluetoothDevice nextDevice = deviceList.remove(0); + CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice); + // we may add a new device here, but generally this should not happen + if (device == null) { + if (VDBG) { + Log.d(TAG, "CsipSetCoordinatorProfile found new device: " + nextDevice); + } + device = mDeviceManager.addDevice(nextDevice); + } + device.onProfileStateChanged( + CsipSetCoordinatorProfile.this, BluetoothProfile.STATE_CONNECTED); + device.refresh(); + } + + mProfileManager.callServiceConnectedListeners(); + mIsProfileReady = true; + } + + public void onServiceDisconnected(int profile) { + if (VDBG) { + Log.d(TAG, "Bluetooth service disconnected"); + } + mProfileManager.callServiceDisconnectedListeners(); + mIsProfileReady = false; + } + } + + CsipSetCoordinatorProfile(Context context, CachedBluetoothDeviceManager deviceManager, + LocalBluetoothProfileManager profileManager) { + mContext = context; + mDeviceManager = deviceManager; + mProfileManager = profileManager; + + BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, + new CoordinatedSetServiceListener(), BluetoothProfile.CSIP_SET_COORDINATOR); + } + + /** + * Get CSIP devices matching connection states{ + * + * @code BluetoothProfile.STATE_CONNECTED, + * @code BluetoothProfile.STATE_CONNECTING, + * @code BluetoothProfile.STATE_DISCONNECTING} + * + * @return Matching device list + */ + public List<BluetoothDevice> getConnectedDevices() { + if (mService == null) { + return new ArrayList<BluetoothDevice>(0); + } + return mService.getDevicesMatchingConnectionStates( + new int[] {BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING, + BluetoothProfile.STATE_DISCONNECTING}); + } + + /** + * Gets the connection status of the device. + * + * @code BluetoothProfile.STATE_CONNECTED, + * @code BluetoothProfile.STATE_CONNECTING, + * @code BluetoothProfile.STATE_DISCONNECTING} + * + * @return Connection status, {@code BluetoothProfile.STATE_DISCONNECTED} if unknown. + */ + public int getConnectionStatus(BluetoothDevice device) { + if (mService == null) { + return BluetoothProfile.STATE_DISCONNECTED; + } + return mService.getConnectionState(device); + } + + @Override + public boolean isProfileReady() { + return mIsProfileReady; + } + + @Override + public int getProfileId() { + return BluetoothProfile.CSIP_SET_COORDINATOR; + } + + @Override + public boolean accessProfileEnabled() { + return false; + } + + @Override + public boolean isAutoConnectable() { + return true; + } + + @Override + public boolean isEnabled(BluetoothDevice device) { + if (mService == null || device == null) { + return false; + } + return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; + } + + @Override + public int getConnectionPolicy(BluetoothDevice device) { + if (mService == null || device == null) { + return CONNECTION_POLICY_FORBIDDEN; + } + return mService.getConnectionPolicy(device); + } + + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; + if (mService == null || device == null) { + return false; + } + if (enabled) { + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); + } + } else { + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); + } + + return isEnabled; + } + + @Override + public int getOrdinal() { + return ORDINAL; + } + + @Override + public int getNameResource(BluetoothDevice device) { + return R.string.summary_empty; + } + + @Override + public int getSummaryResourceForDevice(BluetoothDevice device) { + int state = getConnectionStatus(device); + return BluetoothUtils.getConnectionStateSummary(state); + } + + @Override + public int getDrawableResource(BluetoothClass btClass) { + return 0; + } + + /** + * Return the profile name as a string. + */ + public String toString() { + return NAME; + } + + @RequiresApi(32) + protected void finalize() { + if (VDBG) { + Log.d(TAG, "finalize()"); + } + if (mService != null) { + try { + BluetoothAdapter.getDefaultAdapter().closeProfileProxy( + BluetoothProfile.CSIP_SET_COORDINATOR, mService); + mService = null; + } catch (Throwable t) { + Log.w(TAG, "Error cleaning up CSIP Set Coordinator proxy", t); + } + } + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java index c4cb6a1e7197..25fd430372f2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java @@ -19,6 +19,7 @@ package com.android.settingslib.bluetooth; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothA2dpSink; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothCsipSetCoordinator; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHeadsetClient; @@ -100,6 +101,7 @@ public class LocalBluetoothProfileManager { private PbapClientProfile mPbapClientProfile; private PbapServerProfile mPbapProfile; private HearingAidProfile mHearingAidProfile; + private CsipSetCoordinatorProfile mCsipSetCoordinatorProfile; private SapProfile mSapProfile; private VolumeControlProfile mVolumeControlProfile; @@ -230,7 +232,16 @@ public class LocalBluetoothProfileManager { // Note: no event handler for VCP, only for being connectable. mProfileNameMap.put(VolumeControlProfile.NAME, mVolumeControlProfile); } - + if (mCsipSetCoordinatorProfile == null + && supportedList.contains(BluetoothProfile.CSIP_SET_COORDINATOR)) { + if (DEBUG) { + Log.d(TAG, "Adding local CSIP set coordinator profile"); + } + mCsipSetCoordinatorProfile = + new CsipSetCoordinatorProfile(mContext, mDeviceManager, this); + addProfile(mCsipSetCoordinatorProfile, mCsipSetCoordinatorProfile.NAME, + BluetoothCsipSetCoordinator.ACTION_CSIS_CONNECTION_STATE_CHANGED); + } mEventManager.registerProfileIntentReceiver(); } @@ -462,6 +473,10 @@ public class LocalBluetoothProfileManager { return mHidDeviceProfile; } + public CsipSetCoordinatorProfile getCsipSetCoordinatorProfile() { + return mCsipSetCoordinatorProfile; + } + /** * Fill in a list of LocalBluetoothProfile objects that are supported by * the local device and the remote device. @@ -582,6 +597,12 @@ public class LocalBluetoothProfileManager { removedProfiles.remove(mVolumeControlProfile); } + if (mCsipSetCoordinatorProfile != null + && ArrayUtils.contains(uuids, BluetoothUuid.COORDINATED_SET)) { + profiles.add(mCsipSetCoordinatorProfile); + removedProfiles.remove(mCsipSetCoordinatorProfile); + } + if (DEBUG) { Log.d(TAG,"New Profiles" + profiles.toString()); } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 10600e37b220..0e9a51dbb63c 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -513,7 +513,7 @@ <provider android:name=".HeapDumpProvider" android:authorities="com.android.shell.heapdump" android:grantUriPermissions="true" - android:exported="true" /> + android:exported="false" /> <activity android:name=".BugreportWarningActivity" diff --git a/services/OWNERS b/services/OWNERS index b7128a32fcee..a08331996556 100644 --- a/services/OWNERS +++ b/services/OWNERS @@ -4,3 +4,4 @@ per-file Android.bp = file:platform/build/soong:/OWNERS per-file art-profile* = calin@google.com, ngeoffray@google.com, vmarko@google.com per-file java/com/android/server/* = toddke@google.com,patb@google.com +per-file tests/servicestests/src/com/android/server/systemconfig/* = patb@google.com diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 00cc75352633..bdeb4d56bfa4 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -175,8 +175,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock(); private boolean mBinding; private boolean mUnbinding; - private int mWaitForEnableRetry; - private int mWaitForDisableRetry; private BluetoothModeChangeHelper mBluetoothModeChangeHelper; @@ -933,14 +931,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mState == BluetoothAdapter.STATE_ON || mState == BluetoothAdapter.STATE_BLE_ON || mState == BluetoothAdapter.STATE_TURNING_ON - || mState == BluetoothAdapter.STATE_TURNING_OFF) { - Log.d(TAG, "enableBLE(): Bluetooth already enabled"); + || mState == BluetoothAdapter.STATE_TURNING_OFF + || mState == BluetoothAdapter.STATE_BLE_TURNING_ON) { + Log.d(TAG, "enableBLE(): Bluetooth is already enabled or is turning on"); return true; } synchronized (mReceiver) { // waive WRITE_SECURE_SETTINGS permission check - sendEnableMsg(false, - BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName); + sendEnableMsg(false, BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, + packageName, true); } return true; } @@ -1734,6 +1733,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private class BluetoothHandler extends Handler { boolean mGetNameAddressOnly = false; + private int mWaitForEnableRetry; + private int mWaitForDisableRetry; BluetoothHandler(Looper looper) { super(looper); @@ -1781,11 +1782,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { case MESSAGE_ENABLE: int quietEnable = msg.arg1; + int isBle = msg.arg2; if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) { // We are handling enable or disable right now, wait for it. mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ENABLE, - quietEnable, 0), ENABLE_DISABLE_DELAY_MS); + quietEnable, isBle), ENABLE_DISABLE_DELAY_MS); break; } @@ -1800,13 +1802,28 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { + boolean isHandled = true; int state = mBluetooth.getState(); - if (state == BluetoothAdapter.STATE_BLE_ON) { - Slog.w(TAG, "BT Enable in BLE_ON State, going to ON"); - mBluetooth.onLeServiceUp(); - persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); - break; + switch (state) { + case BluetoothAdapter.STATE_BLE_ON: + if (isBle == 1) { + Slog.i(TAG, "Already at BLE_ON State"); + } else { + Slog.w(TAG, "BT Enable in BLE_ON State, going to ON"); + mBluetooth.onLeServiceUp(); + persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); + } + break; + case BluetoothAdapter.STATE_BLE_TURNING_ON: + case BluetoothAdapter.STATE_TURNING_ON: + case BluetoothAdapter.STATE_ON: + Slog.i(TAG, "MESSAGE_ENABLE: already enabled"); + break; + default: + isHandled = false; + break; } + if (isHandled) break; } } catch (RemoteException e) { Slog.e(TAG, "", e); @@ -2559,7 +2576,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private void sendEnableMsg(boolean quietMode, int reason, String packageName) { - mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0)); + sendEnableMsg(quietMode, reason, packageName, false); + } + + private void sendEnableMsg(boolean quietMode, int reason, String packageName, boolean isBle) { + mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, + isBle ? 1 : 0)); addActiveLog(reason, packageName, true); mLastEnabledTime = SystemClock.elapsedRealtime(); } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index b1ffaeb50912..6d85273de142 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -734,11 +734,8 @@ public final class ActiveServices { } ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting); - if (!r.mAllowWhileInUsePermissionInFgs) { - r.mAllowWhileInUsePermissionInFgs = - shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingPid, - callingUid, service, r, allowBackgroundActivityStarts); - } + setFgsRestrictionLocked(callingPackage, callingPid, callingUid, r, + allowBackgroundActivityStarts); return cmp; } @@ -1411,14 +1408,6 @@ public final class ActiveServices { + String.format("0x%08X", manifestType) + " in service element of manifest file"); } - // If the foreground service is not started from TOP process, do not allow it to - // have while-in-use location/camera/microphone access. - if (!r.mAllowWhileInUsePermissionInFgs) { - Slog.w(TAG, - "Foreground service started from background can not have " - + "location/camera/microphone access: service " - + r.shortInstanceName); - } } boolean alreadyStartedOp = false; boolean stopProcStatsOp = false; @@ -1466,6 +1455,57 @@ public final class ActiveServices { ignoreForeground = true; } + if (!ignoreForeground) { + if (r.mStartForegroundCount == 0) { + /* + If the service was started with startService(), not + startForegroundService(), and if startForeground() isn't called within + mFgsStartForegroundTimeoutMs, then we check the state of the app + (who owns the service, which is the app that called startForeground()) + again. If the app is in the foreground, or in any other cases where + FGS-starts are allowed, then we still allow the FGS to be started. + Otherwise, startForeground() would fail. + + If the service was started with startForegroundService(), then the service + must call startForeground() within a timeout anyway, so we don't need this + check. + */ + if (!r.fgRequired) { + final long delayMs = SystemClock.elapsedRealtime() - r.createRealTime; + if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) { + resetFgsRestrictionLocked(r); + setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.pid, + r.appInfo.uid, r, false); + EventLog.writeEvent(0x534e4554, "183147114", + r.appInfo.uid, + "call setFgsRestrictionLocked again due to " + + "startForegroundTimeout"); + } + } + } else if (r.mStartForegroundCount >= 1) { + // The second or later time startForeground() is called after service is + // started. Check for app state again. + final long delayMs = SystemClock.elapsedRealtime() - + r.mLastSetFgsRestrictionTime; + if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) { + resetFgsRestrictionLocked(r); + setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.pid, + r.appInfo.uid, r, false); + EventLog.writeEvent(0x534e4554, "183147114", r.appInfo.uid, + "call setFgsRestrictionLocked for " + + (r.mStartForegroundCount + 1) + "th startForeground"); + } + } + // If the foreground service is not started from TOP process, do not allow it to + // have while-in-use location/camera/microphone access. + if (!r.mAllowWhileInUsePermissionInFgs) { + Slog.w(TAG, + "Foreground service started from background can not have " + + "location/camera/microphone access: service " + + r.shortInstanceName); + } + } + // Apps under strict background restrictions simply don't get to have foreground // services, so now that we've enforced the startForegroundService() contract // we only do the machinery of making the service foreground when the app @@ -1501,6 +1541,7 @@ public final class ActiveServices { active.mNumActive++; } r.isForeground = true; + r.mStartForegroundCount++; if (!stopProcStatsOp) { ServiceState stracker = r.getTracker(); if (stracker != null) { @@ -1559,6 +1600,7 @@ public final class ActiveServices { decActiveForegroundAppLocked(smap, r); } r.isForeground = false; + resetFgsRestrictionLocked(r); ServiceState stracker = r.getTracker(); if (stracker != null) { stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(), @@ -2118,12 +2160,7 @@ public final class ActiveServices { } } - if (!s.mAllowWhileInUsePermissionInFgs) { - s.mAllowWhileInUsePermissionInFgs = - shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, - callingPid, callingUid, - service, s, false); - } + setFgsRestrictionLocked(callingPackage, callingPid, callingUid, s, false); if (s.app != null) { if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) { @@ -3419,7 +3456,7 @@ public final class ActiveServices { r.isForeground = false; r.foregroundId = 0; r.foregroundNoti = null; - r.mAllowWhileInUsePermissionInFgs = false; + resetFgsRestrictionLocked(r); // Clear start entries. r.clearDeliveredStartsLocked(); @@ -4900,7 +4937,7 @@ public final class ActiveServices { * @return true if allow, false otherwise. */ private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage, - int callingPid, int callingUid, Intent intent, ServiceRecord r, + int callingPid, int callingUid, ServiceRecord r, boolean allowBackgroundActivityStarts) { // Is the background FGS start restriction turned on? if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) { @@ -4986,6 +5023,28 @@ public final class ActiveServices { boolean canAllowWhileInUsePermissionInFgsLocked(int callingPid, int callingUid, String callingPackage) { return shouldAllowWhileInUsePermissionInFgsLocked( - callingPackage, callingPid, callingUid, null, null, false); + callingPackage, callingPid, callingUid, null, false); + } + + /** + * In R, mAllowWhileInUsePermissionInFgs is to allow while-in-use permissions in foreground + * service or not. while-in-use permissions in FGS started from background might be restricted. + * @param callingPackage caller app's package name. + * @param callingUid caller app's uid. + * @param r the service to start. + * @return true if allow, false otherwise. + */ + private void setFgsRestrictionLocked(String callingPackage, + int callingPid, int callingUid, ServiceRecord r, + boolean allowBackgroundActivityStarts) { + r.mLastSetFgsRestrictionTime = SystemClock.elapsedRealtime(); + if (!r.mAllowWhileInUsePermissionInFgs) { + r.mAllowWhileInUsePermissionInFgs = shouldAllowWhileInUsePermissionInFgsLocked( + callingPackage, callingPid, callingUid, r, allowBackgroundActivityStarts); + } + } + + private void resetFgsRestrictionLocked(ServiceRecord r) { + r.mAllowWhileInUsePermissionInFgs = false; } } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 7be843f17863..00d8208ea118 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -88,6 +88,7 @@ final class ActivityManagerConstants extends ContentObserver { static final String KEY_PROCESS_START_ASYNC = "process_start_async"; static final String KEY_MEMORY_INFO_THROTTLE_TIME = "memory_info_throttle_time"; static final String KEY_TOP_TO_FGS_GRACE_DURATION = "top_to_fgs_grace_duration"; + static final String KEY_FGS_START_FOREGROUND_TIMEOUT = "fgs_start_foreground_timeout"; static final String KEY_PENDINGINTENT_WARNING_THRESHOLD = "pendingintent_warning_threshold"; private static final int DEFAULT_MAX_CACHED_PROCESSES = 32; @@ -121,6 +122,7 @@ final class ActivityManagerConstants extends ContentObserver { private static final boolean DEFAULT_PROCESS_START_ASYNC = true; private static final long DEFAULT_MEMORY_INFO_THROTTLE_TIME = 5*60*1000; private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000; + private static final int DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS = 10 * 1000; private static final int DEFAULT_PENDINGINTENT_WARNING_THRESHOLD = 2000; // Flag stored in the DeviceConfig API. @@ -273,6 +275,12 @@ final class ActivityManagerConstants extends ContentObserver { // this long. public long TOP_TO_FGS_GRACE_DURATION = DEFAULT_TOP_TO_FGS_GRACE_DURATION; + /** + * When service started from background, before the timeout it can be promoted to FGS by calling + * Service.startForeground(). + */ + volatile long mFgsStartForegroundTimeoutMs = DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS; + // Indicates whether the activity starts logging is enabled. // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED volatile boolean mFlagActivityStartsLoggingEnabled; @@ -421,6 +429,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_MIN_ASSOC_LOG_DURATION: updateMinAssocLogDuration(); break; + case KEY_FGS_START_FOREGROUND_TIMEOUT: + updateFgsStartForegroundTimeout(); + break; default: break; } @@ -697,6 +708,13 @@ final class ActivityManagerConstants extends ContentObserver { /* defaultValue */ DEFAULT_MIN_ASSOC_LOG_DURATION); } + private void updateFgsStartForegroundTimeout() { + mFgsStartForegroundTimeoutMs = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_FGS_START_FOREGROUND_TIMEOUT, + DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS); + } + void dump(PrintWriter pw) { pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) " + Settings.Global.ACTIVITY_MANAGER_CONSTANTS + ":"); @@ -769,6 +787,8 @@ final class ActivityManagerConstants extends ContentObserver { pw.println(Arrays.toString(IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.toArray())); pw.print(" "); pw.print(KEY_MIN_ASSOC_LOG_DURATION); pw.print("="); pw.println(MIN_ASSOC_LOG_DURATION); + pw.print(" "); pw.print(KEY_FGS_START_FOREGROUND_TIMEOUT); pw.print("="); + pw.println(mFgsStartForegroundTimeoutMs); pw.println(); if (mOverrideMaxCachedProcesses >= 0) { diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index af8990778773..50f3520e6291 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -94,7 +94,6 @@ import com.android.internal.util.MemInfoReader; import com.android.server.compat.PlatformCompat; import java.io.BufferedReader; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -787,8 +786,7 @@ final class ActivityManagerShellCommand extends ShellCommand { return -1; } - File file = new File(filename); - file.delete(); + // Writes an error message to stderr on failure ParcelFileDescriptor fd = openFileForSystem(filename, "w"); if (fd == null) { return -1; @@ -942,16 +940,16 @@ final class ActivityManagerShellCommand extends ShellCommand { String logNameTimeString = LOG_NAME_TIME_FORMATTER.format(localDateTime); heapFile = "/data/local/tmp/heapdump-" + logNameTimeString + ".prof"; } - pw.println("File: " + heapFile); - pw.flush(); - File file = new File(heapFile); - file.delete(); + // Writes an error message to stderr on failure ParcelFileDescriptor fd = openFileForSystem(heapFile, "w"); if (fd == null) { return -1; } + pw.println("File: " + heapFile); + pw.flush(); + final CountDownLatch latch = new CountDownLatch(1); final RemoteCallback finishCallback = new RemoteCallback(new OnResultListener() { diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 39f79ca2f13b..ef47b1ebc7ec 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -22,6 +22,7 @@ import android.content.Context; import android.net.wifi.WifiManager; import android.os.BatteryStats; import android.os.Bundle; +import android.os.OutcomeReceiver; import android.os.Parcelable; import android.os.Process; import android.os.ServiceManager; @@ -40,6 +41,7 @@ import com.android.internal.os.BatteryStatsImpl; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.function.pooled.PooledLambda; +import java.util.concurrent.ExecutionException; import libcore.util.EmptyArray; import java.util.concurrent.CompletableFuture; @@ -405,7 +407,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { // We will request data from external processes asynchronously, and wait on a timeout. SynchronousResultReceiver wifiReceiver = null; SynchronousResultReceiver bluetoothReceiver = null; - SynchronousResultReceiver modemReceiver = null; + CompletableFuture<ModemActivityInfo> modemFuture = CompletableFuture.completedFuture(null); boolean railUpdated = false; if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) { @@ -460,8 +462,22 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } if (mTelephony != null) { - modemReceiver = new SynchronousResultReceiver("telephony"); - mTelephony.requestModemActivityInfo(modemReceiver); + CompletableFuture<ModemActivityInfo> temp = new CompletableFuture<>(); + mTelephony.requestModemActivityInfo(Runnable::run, + new OutcomeReceiver<ModemActivityInfo, + TelephonyManager.ModemActivityInfoException>() { + @Override + public void onResult(ModemActivityInfo result) { + temp.complete(result); + } + + @Override + public void onError(TelephonyManager.ModemActivityInfoException e) { + Slog.w(TAG, "error reading modem stats:" + e); + temp.complete(null); + } + }); + modemFuture = temp; } if (!railUpdated) { synchronized (mStats) { @@ -472,7 +488,17 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver); final BluetoothActivityEnergyInfo bluetoothInfo = awaitControllerInfo(bluetoothReceiver); - final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver); + ModemActivityInfo modemInfo = null; + try { + modemInfo = modemFuture.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, + TimeUnit.MILLISECONDS); + } catch (TimeoutException | InterruptedException e) { + Slog.w(TAG, "timeout or interrupt reading modem stats: " + e); + } catch (ExecutionException e) { + Slog.w(TAG, "exception reading modem stats: " + e.getCause()); + } + final long elapsedRealtime = SystemClock.elapsedRealtime(); + final long uptime = SystemClock.uptimeMillis(); synchronized (mStats) { mStats.addHistoryEventLocked( @@ -519,11 +545,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } if (modemInfo != null) { - if (modemInfo.isValid()) { - mStats.updateMobileRadioState(modemInfo); - } else { - Slog.w(TAG, "modem info is invalid: " + modemInfo); - } + mStats.updateMobileRadioState(modemInfo); } } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 580ceca3b197..34ba3e078860 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -1208,7 +1208,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void noteModemControllerActivity(ModemActivityInfo info) { enforceCallingPermission(); - if (info == null || !info.isValid()) { + if (info == null) { Slog.e(TAG, "invalid modem data given: " + info); return; } diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 1b65dbac2294..0e628289a09f 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -142,6 +142,10 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN // allow while-in-use permissions in foreground service or not. // while-in-use permissions in FGS started from background might be restricted. boolean mAllowWhileInUsePermissionInFgs; + // The number of times Service.startForeground() is called; + int mStartForegroundCount; + // Last time mAllowWhileInUsePermissionInFgs is set. + long mLastSetFgsRestrictionTime; // the most recent package that start/bind this service. String mRecentCallingPackage; @@ -406,6 +410,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs="); pw.println(mAllowWhileInUsePermissionInFgs); + pw.print(prefix); pw.print("startForegroundCount="); + pw.println(mStartForegroundCount); pw.print(prefix); pw.print("recentCallingPackage="); pw.println(mRecentCallingPackage); if (delayed) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 5babfeb08f76..f83d059ce212 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -123,6 +123,7 @@ import android.app.INotificationManager; import android.app.ITransientNotification; import android.app.ITransientNotificationCallback; import android.app.IUriGrantsManager; +import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; @@ -479,6 +480,8 @@ public class NotificationManagerService extends SystemService { final ArrayMap<NotificationRecord, ArrayList<CancelNotificationRunnable>> mDelayedCancelations = new ArrayMap<>(); + private KeyguardManager mKeyguardManager; + // The last key in this list owns the hardware. ArrayList<String> mLights = new ArrayList<>(); @@ -1726,6 +1729,11 @@ public class NotificationManagerService extends SystemService { } @VisibleForTesting + void setKeyguardManager(KeyguardManager keyguardManager) { + mKeyguardManager = keyguardManager; + } + + @VisibleForTesting ShortcutHelper getShortcutHelper() { return mShortcutHelper; } @@ -2330,6 +2338,7 @@ public class NotificationManagerService extends SystemService { mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); mAudioManagerInternal = getLocalService(AudioManagerInternal.class); mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); + mKeyguardManager = getContext().getSystemService(KeyguardManager.class); mZenModeHelper.onSystemReady(); mRoleObserver = new RoleObserver(getContext().getSystemService(RoleManager.class), mPackageManager, getContext().getMainExecutor()); @@ -6902,7 +6911,6 @@ public class NotificationManagerService extends SystemService { boolean beep = false; boolean blink = false; - final Notification notification = record.getSbn().getNotification(); final String key = record.getKey(); // Should this notification make noise, vibe, or use the LED? @@ -6924,7 +6932,7 @@ public class NotificationManagerService extends SystemService { if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN && !suppressedByDnd) { - sendAccessibilityEvent(notification, record.getSbn().getPackageName()); + sendAccessibilityEvent(record); sentAccessibilityEvent = true; } @@ -6946,7 +6954,7 @@ public class NotificationManagerService extends SystemService { boolean hasAudibleAlert = hasValidSound || hasValidVibrate; if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) { if (!sentAccessibilityEvent) { - sendAccessibilityEvent(notification, record.getSbn().getPackageName()); + sendAccessibilityEvent(record); sentAccessibilityEvent = true; } if (DBG) Slog.v(TAG, "Interrupting!"); @@ -7663,17 +7671,30 @@ public class NotificationManagerService extends SystemService { return (x < low) ? low : ((x > high) ? high : x); } - void sendAccessibilityEvent(Notification notification, CharSequence packageName) { + void sendAccessibilityEvent(NotificationRecord record) { if (!mAccessibilityManager.isEnabled()) { return; } - AccessibilityEvent event = + final Notification notification = record.getNotification(); + final CharSequence packageName = record.getSbn().getPackageName(); + final AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); event.setPackageName(packageName); event.setClassName(Notification.class.getName()); - event.setParcelableData(notification); - CharSequence tickerText = notification.tickerText; + final int visibilityOverride = record.getPackageVisibilityOverride(); + final int notifVisibility = visibilityOverride == NotificationManager.VISIBILITY_NO_OVERRIDE + ? notification.visibility : visibilityOverride; + final int userId = record.getUser().getIdentifier(); + final boolean needPublic = userId >= 0 && mKeyguardManager.isDeviceLocked(userId); + if (needPublic && notifVisibility != Notification.VISIBILITY_PUBLIC) { + // Emit the public version if we're on the lockscreen and this notification isn't + // publicly visible. + event.setParcelableData(notification.publicVersion); + } else { + event.setParcelableData(notification); + } + final CharSequence tickerText = notification.tickerText; if (!TextUtils.isEmpty(tickerText)) { event.getText().add(tickerText); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 1173df629796..4767823c8963 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -14289,9 +14289,15 @@ public class PackageManagerService extends IPackageManager.Stub return new ParceledListSlice<IntentFilter>(result) { @Override protected void writeElement(IntentFilter parcelable, Parcel dest, int callFlags) { - // IntentFilter has final Parcelable methods, so redirect to the subclass - ((ParsedIntentInfo) parcelable).writeIntentInfoToParcel(dest, - callFlags); + parcelable.writeToParcel(dest, callFlags); + } + + @Override + protected void writeParcelableCreator(IntentFilter parcelable, Parcel dest) { + // All Parcel#writeParcelableCreator does is serialize the class name to + // access via reflection to grab its CREATOR. This does that manually, pointing + // to the parent IntentFilter so that all of the subclass fields are ignored. + dest.writeString(IntentFilter.class.getName()); } }; } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 836e6150414c..ae940d0e3c1c 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -608,6 +608,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { private int mPowerButtonSuppressionDelayMillis = POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS; + private boolean mLockNowPending = false; + private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3; private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4; private static final int MSG_KEYGUARD_DRAWN_COMPLETE = 5; @@ -4941,6 +4943,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mKeyguardDelegate.doKeyguardTimeout(options); } mLockScreenTimerActive = false; + mLockNowPending = false; options = null; } } @@ -4950,7 +4953,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - ScreenLockTimeout mScreenLockTimeout = new ScreenLockTimeout(); + final ScreenLockTimeout mScreenLockTimeout = new ScreenLockTimeout(); @Override public void lockNow(Bundle options) { @@ -4962,6 +4965,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { mScreenLockTimeout.setLockOptions(options); } mHandler.post(mScreenLockTimeout); + synchronized (mScreenLockTimeout) { + mLockNowPending = true; + } } // TODO (b/113840485): Move this logic to DisplayPolicy when lockscreen supports multi-display. @@ -4977,6 +4983,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { private void updateLockScreenTimeout() { synchronized (mScreenLockTimeout) { + if (mLockNowPending) { + Log.w(TAG, "lockNow pending, ignore updating lockscreen timeout"); + return; + } final boolean enable = !mAllowLockscreenWhenOnDisplays.isEmpty() && mDefaultDisplayPolicy.isAwake() && mKeyguardDelegate != null && mKeyguardDelegate.isSecure(mCurrentUserId); diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index 392792dbae69..b75bce833e04 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -662,6 +662,12 @@ public class RoleManagerService extends SystemService implements RoleUserState.C @Override public String getDefaultSmsPackage(int userId) { + userId = handleIncomingUser(userId, false, "getDefaultSmsPackage"); + if (!mUserManagerInternal.exists(userId)) { + Slog.e(LOG_TAG, "user " + userId + " does not exist"); + return null; + } + long identity = Binder.clearCallingIdentity(); try { return CollectionUtils.firstOrNull( diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 03900150cfae..71b3e61a9844 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -102,6 +102,7 @@ import android.os.Environment; import android.os.IStoraged; import android.os.IThermalEventListener; import android.os.IThermalService; +import android.os.OutcomeReceiver; import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.os.RemoteException; @@ -172,6 +173,7 @@ import com.android.server.stats.pull.netstats.SubInfo; import com.android.server.storage.DiskStatsFileLogger; import com.android.server.storage.DiskStatsLoggingService; +import java.util.concurrent.ExecutionException; import libcore.io.IoUtils; import org.json.JSONArray; @@ -1731,22 +1733,47 @@ public class StatsPullAtomService extends SystemService { int pullModemActivityInfoLocked(int atomTag, List<StatsEvent> pulledData) { long token = Binder.clearCallingIdentity(); try { - SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony"); - mTelephony.requestModemActivityInfo(modemReceiver); - final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver); + CompletableFuture<ModemActivityInfo> modemFuture = new CompletableFuture<>(); + mTelephony.requestModemActivityInfo(Runnable::run, + new OutcomeReceiver<ModemActivityInfo, + TelephonyManager.ModemActivityInfoException>() { + @Override + public void onResult(ModemActivityInfo result) { + modemFuture.complete(result); + } + + @Override + public void onError(TelephonyManager.ModemActivityInfoException e) { + Slog.w(TAG, "error reading modem stats:" + e); + modemFuture.complete(null); + } + }); + + ModemActivityInfo modemInfo; + try { + modemInfo = modemFuture.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, + TimeUnit.MILLISECONDS); + } catch (TimeoutException | InterruptedException e) { + Slog.w(TAG, "timeout or interrupt reading modem stats: " + e); + return StatsManager.PULL_SKIP; + } catch (ExecutionException e) { + Slog.w(TAG, "exception reading modem stats: " + e.getCause()); + return StatsManager.PULL_SKIP; + } + if (modemInfo == null) { return StatsManager.PULL_SKIP; } StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) - .writeLong(modemInfo.getTimestamp()) + .writeLong(modemInfo.getTimestampMillis()) .writeLong(modemInfo.getSleepTimeMillis()) .writeLong(modemInfo.getIdleTimeMillis()) - .writeLong(modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis()) - .writeLong(modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis()) - .writeLong(modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis()) - .writeLong(modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis()) - .writeLong(modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis()) + .writeLong(modemInfo.getTransmitDurationMillisAtPowerLevel(0)) + .writeLong(modemInfo.getTransmitDurationMillisAtPowerLevel(1)) + .writeLong(modemInfo.getTransmitDurationMillisAtPowerLevel(2)) + .writeLong(modemInfo.getTransmitDurationMillisAtPowerLevel(3)) + .writeLong(modemInfo.getTransmitDurationMillisAtPowerLevel(4)) .writeLong(modemInfo.getReceiveTimeMillis()) .build(); pulledData.add(e); diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 277163a6e3ec..630317a150f6 100755 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -1265,12 +1265,14 @@ class TvInputHardwareManager implements TvInputHal.Callback { if (inputId != null) { if (connection.updateCableConnectionStatusLocked(cableConnectionStatus)) { if (previousCableConnectionStatus != connection.getInputStateLocked()) { - mListener.onStateChanged(inputId, connection.getInputStateLocked()); + mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, + connection.getInputStateLocked(), 0, inputId).sendToTarget(); } } else { if ((previousConfigsLength == 0) != (connection.getConfigsLengthLocked() == 0)) { - mListener.onStateChanged(inputId, connection.getInputStateLocked()); + mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, + connection.getInputStateLocked(), 0, inputId).sendToTarget(); } } } diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java index 382398a210bb..e0cc8e182079 100644 --- a/services/core/java/com/android/server/vcn/Vcn.java +++ b/services/core/java/com/android/server/vcn/Vcn.java @@ -352,7 +352,7 @@ public class Vcn extends Handler { } private void handleSafeModeStatusChanged() { - logDbg("VcnGatewayConnection safe mode status changed"); + logVdbg("VcnGatewayConnection safe mode status changed"); boolean hasSafeModeGatewayConnection = false; // If any VcnGatewayConnection is in safe mode, mark the entire VCN as being in safe mode @@ -368,7 +368,7 @@ public class Vcn extends Handler { hasSafeModeGatewayConnection ? VCN_STATUS_CODE_SAFE_MODE : VCN_STATUS_CODE_ACTIVE; if (oldStatus != mCurrentStatus) { mVcnCallback.onSafeModeStatusChanged(hasSafeModeGatewayConnection); - logDbg( + logInfo( "Safe mode " + (mCurrentStatus == VCN_STATUS_CODE_SAFE_MODE ? "entered" : "exited")); } @@ -539,6 +539,16 @@ public class Vcn extends Handler { Slog.d(TAG, getLogPrefix() + msg, tr); } + private void logInfo(String msg) { + Slog.i(TAG, getLogPrefix() + msg); + LOCAL_LOG.log(getLogPrefix() + "INFO: " + msg); + } + + private void logInfo(String msg, Throwable tr) { + Slog.i(TAG, getLogPrefix() + msg, tr); + LOCAL_LOG.log(getLogPrefix() + "INFO: " + msg + tr); + } + private void logErr(String msg) { Slog.e(TAG, getLogPrefix() + msg); LOCAL_LOG.log(getLogPrefix() + "ERR: " + msg); diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 450257fcdecb..7dec4e785f5c 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -1677,10 +1677,8 @@ public class VcnGatewayConnection extends StateMachine { mFailedAttempts = 0; cancelSafeModeAlarm(); - if (mIsInSafeMode) { - mIsInSafeMode = false; - mGatewayStatusCallback.onSafeModeStatusChanged(); - } + mIsInSafeMode = false; + mGatewayStatusCallback.onSafeModeStatusChanged(); } protected void applyTransform( diff --git a/services/java/com/android/server/SystemConfigService.java b/services/java/com/android/server/SystemConfigService.java index a2768c637d79..3a9b2dca3921 100644 --- a/services/java/com/android/server/SystemConfigService.java +++ b/services/java/com/android/server/SystemConfigService.java @@ -21,6 +21,7 @@ import static java.util.stream.Collectors.toMap; import android.Manifest; import android.content.Context; import android.os.ISystemConfig; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.SparseArray; @@ -84,6 +85,21 @@ public class SystemConfigService extends SystemService { } return ArrayUtils.convertToIntArray(uids); } + + @Override + public List<String> getEnabledComponentOverrides(String packageName) { + ArrayMap<String, Boolean> systemComponents = SystemConfig.getInstance() + .getComponentsEnabledStates(packageName); + List<String> enabledComponent = new ArrayList<>(); + if (systemComponents != null) { + for (Map.Entry<String, Boolean> entry : systemComponents.entrySet()) { + if (Boolean.TRUE.equals(entry.getValue())) { + enabledComponent.add(entry.getKey()); + } + } + } + return enabledComponent; + } }; public SystemConfigService(Context context) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java index ad15a99a0d78..11ea4a46b73b 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -46,6 +46,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; +import android.app.KeyguardManager; import android.app.Notification; import android.app.Notification.Builder; import android.app.NotificationChannel; @@ -103,6 +104,8 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { NotificationUsageStats mUsageStats; @Mock IAccessibilityManager mAccessibilityService; + @Mock + KeyguardManager mKeyguardManager; NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake(); private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake( 1 << 30); @@ -147,6 +150,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10); when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); when(mUsageStats.isAlertRateLimited(any())).thenReturn(false); + when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(false); long serviceReturnValue = IntPair.of( AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED, @@ -168,6 +172,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { mService.setFallbackVibrationPattern(FALLBACK_VIBRATION_PATTERN); mService.setUsageStats(mUsageStats); mService.setAccessibilityManager(accessibilityManager); + mService.setKeyguardManager(mKeyguardManager); mService.mScreenOn = false; mService.mInCallStateOffHook = false; mService.mNotificationPulseEnabled = true; @@ -484,6 +489,94 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { } @Test + public void testLockedPrivateA11yRedaction() throws Exception { + NotificationRecord r = getBeepyNotification(); + r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE); + r.getNotification().visibility = Notification.VISIBILITY_PRIVATE; + when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true); + AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class); + when(accessibilityManager.isEnabled()).thenReturn(true); + mService.setAccessibilityManager(accessibilityManager); + + mService.buzzBeepBlinkLocked(r); + + ArgumentCaptor<AccessibilityEvent> eventCaptor = + ArgumentCaptor.forClass(AccessibilityEvent.class); + + verify(accessibilityManager, times(1)) + .sendAccessibilityEvent(eventCaptor.capture()); + + AccessibilityEvent event = eventCaptor.getValue(); + assertEquals(r.getNotification().publicVersion, event.getParcelableData()); + } + + @Test + public void testLockedOverridePrivateA11yRedaction() throws Exception { + NotificationRecord r = getBeepyNotification(); + r.setPackageVisibilityOverride(Notification.VISIBILITY_PRIVATE); + r.getNotification().visibility = Notification.VISIBILITY_PUBLIC; + when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true); + AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class); + when(accessibilityManager.isEnabled()).thenReturn(true); + mService.setAccessibilityManager(accessibilityManager); + + mService.buzzBeepBlinkLocked(r); + + ArgumentCaptor<AccessibilityEvent> eventCaptor = + ArgumentCaptor.forClass(AccessibilityEvent.class); + + verify(accessibilityManager, times(1)) + .sendAccessibilityEvent(eventCaptor.capture()); + + AccessibilityEvent event = eventCaptor.getValue(); + assertEquals(r.getNotification().publicVersion, event.getParcelableData()); + } + + @Test + public void testLockedPublicA11yNoRedaction() throws Exception { + NotificationRecord r = getBeepyNotification(); + r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE); + r.getNotification().visibility = Notification.VISIBILITY_PUBLIC; + when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true); + AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class); + when(accessibilityManager.isEnabled()).thenReturn(true); + mService.setAccessibilityManager(accessibilityManager); + + mService.buzzBeepBlinkLocked(r); + + ArgumentCaptor<AccessibilityEvent> eventCaptor = + ArgumentCaptor.forClass(AccessibilityEvent.class); + + verify(accessibilityManager, times(1)) + .sendAccessibilityEvent(eventCaptor.capture()); + + AccessibilityEvent event = eventCaptor.getValue(); + assertEquals(r.getNotification(), event.getParcelableData()); + } + + @Test + public void testUnlockedPrivateA11yNoRedaction() throws Exception { + NotificationRecord r = getBeepyNotification(); + r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE); + r.getNotification().visibility = Notification.VISIBILITY_PRIVATE; + when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(false); + AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class); + when(accessibilityManager.isEnabled()).thenReturn(true); + mService.setAccessibilityManager(accessibilityManager); + + mService.buzzBeepBlinkLocked(r); + + ArgumentCaptor<AccessibilityEvent> eventCaptor = + ArgumentCaptor.forClass(AccessibilityEvent.class); + + verify(accessibilityManager, times(1)) + .sendAccessibilityEvent(eventCaptor.capture()); + + AccessibilityEvent event = eventCaptor.getValue(); + assertEquals(r.getNotification(), event.getParcelableData()); + } + + @Test public void testBeepInsistently() throws Exception { NotificationRecord r = getInsistentBeepyNotification(); diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java index 5e3d26a5179d..705b4918726e 100644 --- a/telecomm/java/android/telecom/InCallService.java +++ b/telecomm/java/android/telecom/InCallService.java @@ -81,7 +81,8 @@ import java.util.List; * <pre> * {@code * <service android:name="your.package.YourInCallServiceImplementation" - * android:permission="android.permission.BIND_INCALL_SERVICE"> + * android:permission="android.permission.BIND_INCALL_SERVICE" + * android:exported="true"> * <meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" /> * <meta-data android:name="android.telecom.IN_CALL_SERVICE_RINGING" * android:value="true" /> @@ -91,6 +92,10 @@ import java.util.List; * </service> * } * </pre> + * + * <em>Note: You should NOT mark your {@link InCallService} with the attribute + * {@code android:exported="false"}; doing so can result in a failure to bind to your implementation + * during calls.</em> * <p> * In addition to implementing the {@link InCallService} API, you must also declare an activity in * your manifest which handles the {@link Intent#ACTION_DIAL} intent. The example below illustrates diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java index debb119c94bc..0bf8ce620eb7 100644 --- a/telephony/java/android/telephony/ModemActivityInfo.java +++ b/telephony/java/android/telephony/ModemActivityInfo.java @@ -16,8 +16,12 @@ package android.telephony; +import android.annotation.DurationMillisLong; +import android.annotation.ElapsedRealtimeLong; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; @@ -25,46 +29,50 @@ import android.util.Range; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.List; +import java.util.Arrays; +import java.util.Objects; /** - * Reports modem activity information. + * Contains information about the modem's activity. May be useful for power stats reporting. * @hide */ +@SystemApi +@TestApi public final class ModemActivityInfo implements Parcelable { + private static final int TX_POWER_LEVELS = 5; + /** - * Tx(transmit) power level. see power index below - * <ul> - * <li> index 0 = tx_power < 0dBm. </li> - * <li> index 1 = 0dBm < tx_power < 5dBm. </li> - * <li> index 2 = 5dBm < tx_power < 15dBm. </li> - * <li> index 3 = 15dBm < tx_power < 20dBm. </li> - * <li> index 4 = tx_power > 20dBm. </li> - * </ul> - */ - public static final int TX_POWER_LEVELS = 5; - /** - * Tx(transmit) power level 0: tx_power < 0dBm + * Corresponds to transmit power of less than 0dBm. */ public static final int TX_POWER_LEVEL_0 = 0; + /** - * Tx(transmit) power level 1: 0dBm < tx_power < 5dBm + * Corresponds to transmit power between 0dBm and 5dBm. */ public static final int TX_POWER_LEVEL_1 = 1; + /** - * Tx(transmit) power level 2: 5dBm < tx_power < 15dBm + * Corresponds to transmit power between 5dBm and 15dBm. */ public static final int TX_POWER_LEVEL_2 = 2; + /** - * Tx(transmit) power level 3: 15dBm < tx_power < 20dBm. + * Corresponds to transmit power between 15dBm and 20dBm. */ public static final int TX_POWER_LEVEL_3 = 3; + /** - * Tx(transmit) power level 4: tx_power > 20dBm + * Corresponds to transmit power above 20dBm. */ public static final int TX_POWER_LEVEL_4 = 4; + /** + * The number of transmit power levels. Fixed by HAL definition. + */ + public static int getNumTxPowerLevels() { + return TX_POWER_LEVELS; + } + /** @hide */ @IntDef(prefix = {"TX_POWER_LEVEL_"}, value = { TX_POWER_LEVEL_0, @@ -82,34 +90,39 @@ public final class ModemActivityInfo implements Parcelable { new Range<>(5, 15), new Range<>(15, 20), new Range<>(20, Integer.MAX_VALUE) - }; private long mTimestamp; private int mSleepTimeMs; private int mIdleTimeMs; - private List<TransmitPower> mTransmitPowerInfo = new ArrayList<>(TX_POWER_LEVELS); + private int[] mTxTimeMs; private int mRxTimeMs; + /** + * @hide + */ + @TestApi public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs, @NonNull int[] txTimeMs, int rxTimeMs) { + Objects.requireNonNull(txTimeMs); + if (txTimeMs.length != TX_POWER_LEVELS) { + throw new IllegalArgumentException("txTimeMs must have length == TX_POWER_LEVELS"); + } mTimestamp = timestamp; mSleepTimeMs = sleepTimeMs; mIdleTimeMs = idleTimeMs; - populateTransmitPowerRange(txTimeMs); + mTxTimeMs = txTimeMs; mRxTimeMs = rxTimeMs; } - /** helper API to populate tx power range for each bucket **/ - private void populateTransmitPowerRange(@NonNull int[] transmitPowerMs) { - int i = 0; - for ( ; i < Math.min(transmitPowerMs.length, TX_POWER_LEVELS); i++) { - mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], transmitPowerMs[i])); - } - // Make sure that mTransmitPowerInfo is fully initialized. - for ( ; i < TX_POWER_LEVELS; i++) { - mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], 0)); - } + /** + * Provided for convenience in manipulation since the API exposes long values but internal + * representations are ints. + * @hide + */ + public ModemActivityInfo(long timestamp, long sleepTimeMs, long idleTimeMs, + @NonNull int[] txTimeMs, long rxTimeMs) { + this(timestamp, (int) sleepTimeMs, (int) idleTimeMs, txTimeMs, (int) rxTimeMs); } @Override @@ -118,7 +131,7 @@ public final class ModemActivityInfo implements Parcelable { + " mTimestamp=" + mTimestamp + " mSleepTimeMs=" + mSleepTimeMs + " mIdleTimeMs=" + mIdleTimeMs - + " mTransmitPowerInfo[]=" + mTransmitPowerInfo.toString() + + " mTxTimeMs[]=" + Arrays.toString(mTxTimeMs) + " mRxTimeMs=" + mRxTimeMs + "}"; } @@ -129,14 +142,12 @@ public final class ModemActivityInfo implements Parcelable { public static final @android.annotation.NonNull Parcelable.Creator<ModemActivityInfo> CREATOR = new Parcelable.Creator<ModemActivityInfo>() { - public ModemActivityInfo createFromParcel(Parcel in) { + public ModemActivityInfo createFromParcel(@NonNull Parcel in) { long timestamp = in.readLong(); int sleepTimeMs = in.readInt(); int idleTimeMs = in.readInt(); int[] txTimeMs = new int[TX_POWER_LEVELS]; - for (int i = 0; i < TX_POWER_LEVELS; i++) { - txTimeMs[i] = in.readInt(); - } + in.readIntArray(txTimeMs); int rxTimeMs = in.readInt(); return new ModemActivityInfo(timestamp, sleepTimeMs, idleTimeMs, txTimeMs, rxTimeMs); @@ -147,21 +158,25 @@ public final class ModemActivityInfo implements Parcelable { } }; - public void writeToParcel(Parcel dest, int flags) { + /** + * @param dest The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + */ + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeLong(mTimestamp); dest.writeInt(mSleepTimeMs); dest.writeInt(mIdleTimeMs); - for (int i = 0; i < TX_POWER_LEVELS; i++) { - dest.writeInt(mTransmitPowerInfo.get(i).getTimeInMillis()); - } + dest.writeIntArray(mTxTimeMs); dest.writeInt(mRxTimeMs); } /** - * @return milliseconds since boot, including mTimeInMillis spent in sleep. - * @see SystemClock#elapsedRealtime() + * Gets the timestamp at which this modem activity info was recorded. + * + * @return The timestamp, as returned by {@link SystemClock#elapsedRealtime()}, when this + * {@link ModemActivityInfo} was recorded. */ - public long getTimestamp() { + public @ElapsedRealtimeLong long getTimestampMillis() { return mTimestamp; } @@ -171,35 +186,48 @@ public final class ModemActivityInfo implements Parcelable { } /** - * @return an arrayList of {@link TransmitPower} with each element representing the total time where - * transmitter is awake time (in ms) for a given power range (in dbm). + * Gets the amount of time the modem spent transmitting at a certain power level. * - * @see #TX_POWER_LEVELS + * @param powerLevel The power level to query. + * @return The amount of time, in milliseconds, that the modem spent transmitting at the + * given power level. */ - @NonNull - public List<TransmitPower> getTransmitPowerInfo() { - return mTransmitPowerInfo; + public @DurationMillisLong long getTransmitDurationMillisAtPowerLevel( + @TxPowerLevel int powerLevel) { + return mTxTimeMs[powerLevel]; + } + + /** + * Gets the range of transmit powers corresponding to a certain power level. + * + * @param powerLevel The power level to query + * @return A {@link Range} object representing the range of intensities (in dBm) to which this + * power level corresponds. + */ + public @NonNull Range<Integer> getTransmitPowerRange(@TxPowerLevel int powerLevel) { + return TX_POWER_RANGES[powerLevel]; } /** @hide */ public void setTransmitTimeMillis(int[] txTimeMs) { - populateTransmitPowerRange(txTimeMs); + mTxTimeMs = Arrays.copyOf(txTimeMs, TX_POWER_LEVELS); } - /** @hide */ + /** + * @return The raw array of transmit power durations + * @hide + */ @NonNull public int[] getTransmitTimeMillis() { - int[] transmitTimeMillis = new int[TX_POWER_LEVELS]; - for (int i = 0; i < transmitTimeMillis.length; i++) { - transmitTimeMillis[i] = mTransmitPowerInfo.get(i).getTimeInMillis(); - } - return transmitTimeMillis; + return mTxTimeMs; } /** - * @return total mTimeInMillis (in ms) when modem is in a low power or sleep state. + * Gets the amount of time (in milliseconds) when the modem is in a low power or sleep state. + * + * @return Time in milliseconds. */ - public int getSleepTimeMillis() { + public @DurationMillisLong long getSleepTimeMillis() { return mSleepTimeMs; } @@ -209,10 +237,44 @@ public final class ModemActivityInfo implements Parcelable { } /** - * @return total mTimeInMillis (in ms) when modem is awake but neither the transmitter nor receiver are - * active. + * Provided for convenience, since the API surface needs to return longs but internal + * representations are ints. + * @hide */ - public int getIdleTimeMillis() { + public void setSleepTimeMillis(long sleepTimeMillis) { + mSleepTimeMs = (int) sleepTimeMillis; + } + + /** + * Computes the difference between this instance of {@link ModemActivityInfo} and another + * instance. + * + * This method should be used to compute the amount of activity that has happened between two + * samples of modem activity taken at separate times. The sample passed in as an argument to + * this method should be the one that's taken later in time (and therefore has more activity). + * @param other The other instance of {@link ModemActivityInfo} to diff against. + * @return An instance of {@link ModemActivityInfo} representing the difference in modem + * activity. + */ + public @NonNull ModemActivityInfo getDelta(@NonNull ModemActivityInfo other) { + int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS]; + for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) { + txTimeMs[i] = other.mTxTimeMs[i] - mTxTimeMs[i]; + } + return new ModemActivityInfo(other.getTimestampMillis(), + other.getSleepTimeMillis() - getSleepTimeMillis(), + other.getIdleTimeMillis() - getIdleTimeMillis(), + txTimeMs, + other.getReceiveTimeMillis() - getReceiveTimeMillis()); + } + + /** + * Gets the amount of time (in milliseconds) when the modem is awake but neither transmitting + * nor receiving. + * + * @return Time in milliseconds. + */ + public @DurationMillisLong long getIdleTimeMillis() { return mIdleTimeMs; } @@ -222,9 +284,20 @@ public final class ModemActivityInfo implements Parcelable { } /** - * @return rx(receive) mTimeInMillis in ms. + * Provided for convenience, since the API surface needs to return longs but internal + * representations are ints. + * @hide + */ + public void setIdleTimeMillis(long idleTimeMillis) { + mIdleTimeMs = (int) idleTimeMillis; + } + + /** + * Gets the amount of time (in milliseconds) when the modem is awake and receiving data. + * + * @return Time in milliseconds. */ - public int getReceiveTimeMillis() { + public @DurationMillisLong long getReceiveTimeMillis() { return mRxTimeMs; } @@ -234,71 +307,56 @@ public final class ModemActivityInfo implements Parcelable { } /** + * Provided for convenience, since the API surface needs to return longs but internal + * representations are ints. + * @hide + */ + public void setReceiveTimeMillis(long receiveTimeMillis) { + mRxTimeMs = (int) receiveTimeMillis; + } + + /** * Indicates if the modem has reported valid {@link ModemActivityInfo}. * * @return {@code true} if this {@link ModemActivityInfo} record is valid, * {@code false} otherwise. + * @hide */ + @TestApi public boolean isValid() { - for (TransmitPower powerInfo : getTransmitPowerInfo()) { - if(powerInfo.getTimeInMillis() < 0) { - return false; - } - } + boolean isTxPowerValid = Arrays.stream(mTxTimeMs).allMatch((i) -> i >= 0); - return ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0) + return isTxPowerValid && ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0) && (getReceiveTimeMillis() >= 0) && !isEmpty()); } - private boolean isEmpty() { - for (TransmitPower txVal : getTransmitPowerInfo()) { - if(txVal.getTimeInMillis() != 0) { - return false; - } - } + /** @hide */ + @TestApi + public boolean isEmpty() { + boolean isTxPowerEmpty = mTxTimeMs == null || mTxTimeMs.length == 0 + || Arrays.stream(mTxTimeMs).allMatch((i) -> i == 0); - return ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0) + return isTxPowerEmpty && ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0) && (getReceiveTimeMillis() == 0)); } - /** - * Transmit power Information, including the power range in dbm and the total time (in ms) where - * the transmitter is active/awake for this power range. - * e.g, range: 0dbm(lower) ~ 5dbm(upper) - * time: 5ms - */ - public class TransmitPower { - private int mTimeInMillis; - private Range<Integer> mPowerRangeInDbm; - /** @hide */ - public TransmitPower(@NonNull Range<Integer> range, int time) { - this.mTimeInMillis = time; - this.mPowerRangeInDbm = range; - } - - /** - * @return the total time in ms where the transmitter is active/wake for this power range - * {@link #getPowerRangeInDbm()}. - */ - public int getTimeInMillis() { - return mTimeInMillis; - } - /** - * @return the power range in dbm. e.g, range: 0dbm(lower) ~ 5dbm(upper) - */ - @NonNull - public Range<Integer> getPowerRangeInDbm() { - return mPowerRangeInDbm; - } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ModemActivityInfo that = (ModemActivityInfo) o; + return mTimestamp == that.mTimestamp + && mSleepTimeMs == that.mSleepTimeMs + && mIdleTimeMs == that.mIdleTimeMs + && mRxTimeMs == that.mRxTimeMs + && Arrays.equals(mTxTimeMs, that.mTxTimeMs); + } - @Override - public String toString() { - return "TransmitPower{" - + " mTimeInMillis=" + mTimeInMillis - + " mPowerRangeInDbm={" + mPowerRangeInDbm.getLower() - + "," + mPowerRangeInDbm.getUpper() - + "}}"; - } + @Override + public int hashCode() { + int result = Objects.hash(mTimestamp, mSleepTimeMs, mIdleTimeMs, mRxTimeMs); + result = 31 * result + Arrays.hashCode(mTxTimeMs); + return result; } } diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 6da61b712916..1677c61eb9cf 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -444,7 +444,9 @@ public class ServiceState implements Parcelable { mArfcnRsrpBoost = s.mArfcnRsrpBoost; synchronized (mNetworkRegistrationInfos) { mNetworkRegistrationInfos.clear(); - mNetworkRegistrationInfos.addAll(s.getNetworkRegistrationInfoList()); + for (NetworkRegistrationInfo nri : s.getNetworkRegistrationInfoList()) { + mNetworkRegistrationInfos.add(new NetworkRegistrationInfo(nri)); + } } mNrFrequencyRange = s.mNrFrequencyRange; mOperatorAlphaLongRaw = s.mOperatorAlphaLongRaw; diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 4189784bf5e0..f55dc8b6f8f1 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -57,7 +57,9 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.OutcomeReceiver; import android.os.ParcelFileDescriptor; +import android.os.Parcelable; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.ResultReceiver; @@ -84,6 +86,7 @@ import android.telephony.CallForwardingInfo.CallForwardingReason; import android.telephony.VisualVoicemailService.VisualVoicemailTask; import android.telephony.data.ApnSetting; import android.telephony.data.ApnSetting.MvnoType; +import android.telephony.data.SlicingConfig; import android.telephony.emergency.EmergencyNumber; import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories; import android.telephony.gba.UaSecurityProtocolIdentifier; @@ -176,6 +179,9 @@ public class TelephonyManager { */ public static final String MODEM_ACTIVITY_RESULT_KEY = "controller_activity"; + /** @hide */ + public static final String EXCEPTION_RESULT_KEY = "exception"; + /** * The process name of the Phone app as well as many other apps that use this process name, such * as settings and vendor components. @@ -10855,26 +10861,149 @@ public class TelephonyManager { return null; } + /** + * Exception that may be supplied to the callback provided in {@link #requestModemActivityInfo}. + * @hide + */ + @SystemApi + public static class ModemActivityInfoException extends Exception { + /** Indicates that an unknown error occurred */ + public static final int ERROR_UNKNOWN = 0; + + /** + * Indicates that the modem or phone processes are not available (such as when the device + * is in airplane mode). + */ + public static final int ERROR_PHONE_NOT_AVAILABLE = 1; + + /** + * Indicates that the modem supplied an invalid instance of {@link ModemActivityInfo} + */ + public static final int ERROR_INVALID_INFO_RECEIVED = 2; + + /** + * Indicates that the modem encountered an internal failure when processing the request + * for activity info. + */ + public static final int ERROR_MODEM_RESPONSE_ERROR = 3; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"ERROR_"}, + value = { + ERROR_UNKNOWN, + ERROR_PHONE_NOT_AVAILABLE, + ERROR_INVALID_INFO_RECEIVED, + ERROR_MODEM_RESPONSE_ERROR, + }) + public @interface ModemActivityInfoError {} + + private final int mErrorCode; + + /** @hide */ + public ModemActivityInfoException(@ModemActivityInfoError int errorCode) { + mErrorCode = errorCode; + } + + public @ModemActivityInfoError int getErrorCode() { + return mErrorCode; + } + + @Override + public String toString() { + switch (mErrorCode) { + case ERROR_UNKNOWN: return "ERROR_UNKNOWN"; + case ERROR_PHONE_NOT_AVAILABLE: return "ERROR_PHONE_NOT_AVAILABLE"; + case ERROR_INVALID_INFO_RECEIVED: return "ERROR_INVALID_INFO_RECEIVED"; + case ERROR_MODEM_RESPONSE_ERROR: return "ERROR_MODEM_RESPONSE_ERROR"; + default: return "UNDEFINED"; + } + } + } /** - * Requests the modem activity info. The recipient will place the result - * in `result`. - * @param result The object on which the recipient will send the resulting - * {@link android.telephony.ModemActivityInfo} object with key of - * {@link #MODEM_ACTIVITY_RESULT_KEY}. + * Requests the current modem activity info. + * + * The provided instance of {@link ModemActivityInfo} represents the cumulative activity since + * the last restart of the phone process. + * + * @param callback A callback object to which the result will be delivered. If there was an + * error processing the request, {@link OutcomeReceiver#onError} will be called + * with more details about the error. * @hide */ - public void requestModemActivityInfo(@NonNull ResultReceiver result) { + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void requestModemActivityInfo(@NonNull @CallbackExecutor Executor executor, + @NonNull OutcomeReceiver<ModemActivityInfo, ModemActivityInfoException> callback) { + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + + // Pass no handler into the receiver, since we're going to be trampolining the call to the + // listener onto the provided executor. + ResultReceiver wrapperResultReceiver = new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle data) { + if (data == null) { + Log.w(TAG, "requestModemActivityInfo: received null bundle"); + sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN); + return; + } + data.setDefusable(true); + if (data.containsKey(EXCEPTION_RESULT_KEY)) { + int receivedErrorCode = data.getInt(EXCEPTION_RESULT_KEY); + sendErrorToListener(receivedErrorCode); + return; + } + + if (!data.containsKey(MODEM_ACTIVITY_RESULT_KEY)) { + Log.w(TAG, "requestModemActivityInfo: Bundle did not contain expected key"); + sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN); + return; + } + Parcelable receivedResult = data.getParcelable(MODEM_ACTIVITY_RESULT_KEY); + if (!(receivedResult instanceof ModemActivityInfo)) { + Log.w(TAG, "requestModemActivityInfo: Bundle contained something that wasn't " + + "a ModemActivityInfo."); + sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN); + return; + } + ModemActivityInfo modemActivityInfo = (ModemActivityInfo) receivedResult; + if (!modemActivityInfo.isValid()) { + Log.w(TAG, "requestModemActivityInfo: Received an invalid ModemActivityInfo"); + sendErrorToListener(ModemActivityInfoException.ERROR_INVALID_INFO_RECEIVED); + return; + } + Log.d(TAG, "requestModemActivityInfo: Sending result to app: " + modemActivityInfo); + sendResultToListener(modemActivityInfo); + } + + private void sendResultToListener(ModemActivityInfo info) { + Binder.withCleanCallingIdentity(() -> + executor.execute(() -> + callback.onResult(info))); + } + + private void sendErrorToListener(int code) { + ModemActivityInfoException e = new ModemActivityInfoException(code); + Binder.withCleanCallingIdentity(() -> + executor.execute(() -> + callback.onError(e))); + } + }; + try { ITelephony service = getITelephony(); if (service != null) { - service.requestModemActivityInfo(result); + service.requestModemActivityInfo(wrapperResultReceiver); return; } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#getModemActivityInfo", e); } - result.send(0, null); + executor.execute(() -> callback.onError( + new ModemActivityInfoException( + ModemActivityInfoException.ERROR_PHONE_NOT_AVAILABLE))); } /** @@ -15093,4 +15222,96 @@ public class TelephonyManager { return PhoneCapability.DEFAULT_SSSS_CAPABILITY; } } + + /** + * Exception that may be supplied to the callback in {@link #getNetworkSlicingConfiguration} if + * something goes awry. + */ + public static class SlicingException extends Exception { + /** + * Getting the current slicing configuration successfully. Used internally only. + * @hide + */ + public static final int SUCCESS = 0; + + /** + * The system timed out waiting for a response from the Radio. + */ + public static final int ERROR_TIMEOUT = 1; + + /** + * The modem returned a failure. + */ + public static final int ERROR_MODEM_ERROR = 2; + + /** @hide */ + @IntDef(prefix = {"ERROR_"}, value = { + ERROR_TIMEOUT, + ERROR_MODEM_ERROR, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SlicingError {} + + private final int mErrorCode; + + public SlicingException(@SlicingError int errorCode) { + mErrorCode = errorCode; + } + + /** + * Fetches the error code associated with this exception. + * @return An error code. + */ + public @SlicingError int getErrorCode() { + return mErrorCode; + } + } + + /** @hide */ + public static final String KEY_SLICING_CONFIG_HANDLE = "slicing_config_handle"; + + /** + * Request to get the current slicing configuration including URSP rules and + * NSSAIs (configured, allowed and rejected). + * + * This method can be invoked if one of the following requirements is met: + * <ul> + * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this + * is a privileged permission that can only be granted to apps preloaded on the device. + * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * </ul> + * + * @param executor the executor on which callback will be invoked. + * @param callback a callback to receive the current slicing configuration. + */ + @SuppressAutoDoc // No support for carrier privileges (b/72967236). + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void getNetworkSlicingConfiguration( + @NonNull @CallbackExecutor Executor executor, + @NonNull OutcomeReceiver<SlicingConfig, SlicingException> callback) { + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + + try { + ITelephony telephony = getITelephony(); + if (telephony == null) { + throw new IllegalStateException("telephony service is null."); + } + telephony.getSlicingConfig(new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle result) { + if (resultCode != SlicingException.SUCCESS) { + executor.execute(() -> callback.onError( + new SlicingException(resultCode))); + return; + } + SlicingConfig slicingConfig = + result.getParcelable(KEY_SLICING_CONFIG_HANDLE); + executor.execute(() -> callback.onResult(slicingConfig)); + } + }); + } catch (RemoteException ex) { + ex.rethrowAsRuntimeException(); + } + } } diff --git a/telephony/java/android/telephony/data/NetworkSliceInfo.java b/telephony/java/android/telephony/data/NetworkSliceInfo.java index 1d9009533685..232a93012f4b 100644 --- a/telephony/java/android/telephony/data/NetworkSliceInfo.java +++ b/telephony/java/android/telephony/data/NetworkSliceInfo.java @@ -19,8 +19,6 @@ package android.telephony.data; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; -import android.annotation.SuppressLint; -import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -35,10 +33,7 @@ import java.util.Objects; * SliceServiceType defines the type of service provided by the slice, and SliceDifferentiator is * used to differentiate between multiple slices of the same type. If the devices is not on HPLMN, * the mappedHplmn versions of these 2 fields indicate the corresponding values in HPLMN. - * - * @hide */ -@SystemApi public final class NetworkSliceInfo implements Parcelable { /** * When set on a Slice Differentiator, this value indicates that there is no corresponding @@ -68,14 +63,14 @@ public final class NetworkSliceInfo implements Parcelable { /** * The min acceptable value for a Slice Differentiator + * @hide */ - @SuppressLint("MinMaxConstant") public static final int MIN_SLICE_DIFFERENTIATOR = -1; /** * The max acceptable value for a Slice Differentiator + * @hide */ - @SuppressLint("MinMaxConstant") public static final int MAX_SLICE_DIFFERENTIATOR = 0xFFFFFE; /** @hide */ @@ -88,6 +83,62 @@ public final class NetworkSliceInfo implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface SliceServiceType {} + /** + * The slice status is unknown. This can happen during IWLAN->cellular handover when the + * NetworkSliceInfo is received over IWLAN. + */ + public static final int SLICE_STATUS_UNKNOWN = 0; + + /** + * The slice is configured but not allowed or rejected yet. + */ + public static final int SLICE_STATUS_CONFIGURED = 1; + + /** + * The slice is allowed to be used. + */ + public static final int SLICE_STATUS_ALLOWED = 2; + + /** + * The slice is rejected because not available in PLMN. + */ + public static final int SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN = 3; + + /** + * The slice is rejected because not available in registered area. + */ + public static final int SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA = 4; + + /** + * The slice is configured by home operator(HPLMN) in default and is used if configured/allowed + * slices are not available for the serving PLMN. + */ + public static final int SLICE_STATUS_DEFAULT_CONFIGURED = 5; + + /** + * The min acceptable value for a slice status. + * @hide + */ + public static final int MIN_SLICE_STATUS = SLICE_STATUS_UNKNOWN; + + /** + * The max acceptable value for a slice status. + * @hide + */ + public static final int MAX_SLICE_STATUS = SLICE_STATUS_DEFAULT_CONFIGURED; + + /** @hide */ + @IntDef(prefix = { "SLICE_STATUS_" }, value = { + SLICE_STATUS_UNKNOWN, + SLICE_STATUS_CONFIGURED, + SLICE_STATUS_ALLOWED, + SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN, + SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA, + SLICE_STATUS_DEFAULT_CONFIGURED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SliceStatus {} + @SliceServiceType private final int mSliceServiceType; @@ -97,14 +148,18 @@ public final class NetworkSliceInfo implements Parcelable { private final int mMappedHplmnSliceServiceType; @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR) private final int mMappedHplmnSliceDifferentiator; + @SliceStatus + @IntRange(from = MIN_SLICE_STATUS, to = MAX_SLICE_STATUS) + private final int mStatus; private NetworkSliceInfo(@SliceServiceType int sliceServiceType, int sliceDifferentiator, int mappedHplmnSliceServiceType, - int mappedHplmnSliceDifferentiator) { + int mappedHplmnSliceDifferentiator, int status) { mSliceServiceType = sliceServiceType; mSliceDifferentiator = sliceDifferentiator; mMappedHplmnSliceDifferentiator = mappedHplmnSliceDifferentiator; mMappedHplmnSliceServiceType = mappedHplmnSliceServiceType; + mStatus = status; } /** @@ -157,11 +212,21 @@ public final class NetworkSliceInfo implements Parcelable { return mMappedHplmnSliceDifferentiator; } + /** + * Field to indicate the current status of the slice. + * @return the current status for this slice info. + */ + @SliceStatus + public int getStatus() { + return mStatus; + } + private NetworkSliceInfo(@NonNull Parcel in) { mSliceServiceType = in.readInt(); mSliceDifferentiator = in.readInt(); mMappedHplmnSliceServiceType = in.readInt(); mMappedHplmnSliceDifferentiator = in.readInt(); + mStatus = in.readInt(); } @Override @@ -175,6 +240,7 @@ public final class NetworkSliceInfo implements Parcelable { dest.writeInt(mSliceDifferentiator); dest.writeInt(mMappedHplmnSliceServiceType); dest.writeInt(mMappedHplmnSliceDifferentiator); + dest.writeInt(mStatus); } public static final @android.annotation.NonNull Parcelable.Creator<NetworkSliceInfo> CREATOR = @@ -200,6 +266,7 @@ public final class NetworkSliceInfo implements Parcelable { + ", mMappedHplmnSliceServiceType=" + sliceServiceTypeToString(mMappedHplmnSliceServiceType) + ", mMappedHplmnSliceDifferentiator=" + mMappedHplmnSliceDifferentiator + + ", mStatus=" + sliceStatusToString(mStatus) + '}'; } @@ -218,6 +285,25 @@ public final class NetworkSliceInfo implements Parcelable { } } + private static String sliceStatusToString(@SliceStatus int sliceStatus) { + switch(sliceStatus) { + case SLICE_STATUS_UNKNOWN: + return "UNKNOWN"; + case SLICE_STATUS_CONFIGURED: + return "CONFIGURED"; + case SLICE_STATUS_ALLOWED: + return "ALLOWED"; + case SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN: + return "REJECTED_NOT_AVAILABLE_IN_PLMN"; + case SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA: + return "REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA"; + case SLICE_STATUS_DEFAULT_CONFIGURED: + return "DEFAULT_CONFIGURED"; + default: + return Integer.toString(sliceStatus); + } + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -226,13 +312,14 @@ public final class NetworkSliceInfo implements Parcelable { return mSliceServiceType == sliceInfo.mSliceServiceType && mSliceDifferentiator == sliceInfo.mSliceDifferentiator && mMappedHplmnSliceServiceType == sliceInfo.mMappedHplmnSliceServiceType - && mMappedHplmnSliceDifferentiator == sliceInfo.mMappedHplmnSliceDifferentiator; + && mMappedHplmnSliceDifferentiator == sliceInfo.mMappedHplmnSliceDifferentiator + && mStatus == sliceInfo.mStatus; } @Override public int hashCode() { return Objects.hash(mSliceServiceType, mSliceDifferentiator, mMappedHplmnSliceServiceType, - mMappedHplmnSliceDifferentiator); + mMappedHplmnSliceDifferentiator, mStatus); } /** @@ -257,6 +344,9 @@ public final class NetworkSliceInfo implements Parcelable { private int mMappedHplmnSliceServiceType = SLICE_SERVICE_TYPE_NONE; @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR) private int mMappedHplmnSliceDifferentiator = SLICE_DIFFERENTIATOR_NO_SLICE; + @SliceStatus + @IntRange(from = MIN_SLICE_STATUS, to = MAX_SLICE_STATUS) + private int mStatus = SLICE_STATUS_UNKNOWN; /** * Default constructor for Builder. @@ -281,8 +371,7 @@ public final class NetworkSliceInfo implements Parcelable { * A value of {@link #SLICE_DIFFERENTIATOR_NO_SLICE} indicates that there is no * corresponding Slice. * - * @throws IllegalArgumentException if the parameter is not between - * {@link #MIN_SLICE_DIFFERENTIATOR} and {@link #MAX_SLICE_DIFFERENTIATOR}. + * @throws IllegalArgumentException if the parameter is not in the expected range. * * @return The same instance of the builder. */ @@ -316,8 +405,7 @@ public final class NetworkSliceInfo implements Parcelable { * A value of {@link #SLICE_DIFFERENTIATOR_NO_SLICE} indicates that there is no * corresponding Slice of the HPLMN. * - * @throws IllegalArgumentException if the parameter is not between - * {@link #MIN_SLICE_DIFFERENTIATOR} and {@link #MAX_SLICE_DIFFERENTIATOR}. + * @throws IllegalArgumentException if the parameter is not in the expected range. * * @return The same instance of the builder. */ @@ -334,6 +422,22 @@ public final class NetworkSliceInfo implements Parcelable { } /** + * Set the slice status. + * + * @throws IllegalArgumentException if the status is invalid. + * + * @return The same instance of the builder. + */ + @NonNull + public Builder setStatus(@SliceStatus int status) { + if (status < MIN_SLICE_STATUS || status > MAX_SLICE_STATUS) { + throw new IllegalArgumentException("The slice status is not valid"); + } + this.mStatus = status; + return this; + } + + /** * Build the {@link NetworkSliceInfo}. * * @return the {@link NetworkSliceInfo} object. @@ -341,7 +445,8 @@ public final class NetworkSliceInfo implements Parcelable { @NonNull public NetworkSliceInfo build() { return new NetworkSliceInfo(this.mSliceServiceType, this.mSliceDifferentiator, - this.mMappedHplmnSliceServiceType, this.mMappedHplmnSliceDifferentiator); + this.mMappedHplmnSliceServiceType, this.mMappedHplmnSliceDifferentiator, + this.mStatus); } } } diff --git a/telephony/java/android/telephony/data/RouteSelectionDescriptor.aidl b/telephony/java/android/telephony/data/RouteSelectionDescriptor.aidl new file mode 100644 index 000000000000..563a00e5d177 --- /dev/null +++ b/telephony/java/android/telephony/data/RouteSelectionDescriptor.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2021 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.telephony.data; + +parcelable RouteSelectionDescriptor; diff --git a/telephony/java/android/telephony/data/RouteSelectionDescriptor.java b/telephony/java/android/telephony/data/RouteSelectionDescriptor.java new file mode 100644 index 000000000000..c2457f21f5ef --- /dev/null +++ b/telephony/java/android/telephony/data/RouteSelectionDescriptor.java @@ -0,0 +1,263 @@ +/** + * Copyright 2021 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.telephony.data; + +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Represents a single route selection descriptor as defined in + * 3GPP TS 24.526. + */ +public final class RouteSelectionDescriptor implements Parcelable { + /** + * The min acceptable value for the precedence of a route selection descriptor. + * @hide + */ + public static final int MIN_ROUTE_PRECEDENCE = 0; + + /** + * The max acceptable value for the precedence of a route selection descriptor. + * @hide + */ + public static final int MAX_ROUTE_PRECEDENCE = 255; + + /** + * The route selection descriptor is for the session with IPV4 type. + */ + public static final int SESSION_TYPE_IPV4 = 0; + + /** + * The route selection descriptor is for the session with IPV6 type. + */ + public static final int SESSION_TYPE_IPV6 = 1; + + /** + * The route selection descriptor is for the session with both IP and IPV6 types. + */ + public static final int SESSION_TYPE_IPV4V6 = 2; + + /** @hide */ + @IntDef(prefix = { "SESSION_TYPE_" }, value = { + SESSION_TYPE_IPV4, + SESSION_TYPE_IPV6, + SESSION_TYPE_IPV4V6, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RouteSessionType {} + + /** + * The route selection descriptor is using SSC mode 1. The session will provide continual + * support when UE's location is updated. + */ + public static final int ROUTE_SSC_MODE_1 = 1; + + /** + * The route selection descriptor is using SSC mode 2. The new session for the same network + * will be established after releasing the old session when UE's location is updated. + */ + public static final int ROUTE_SSC_MODE_2 = 2; + + /** + * The route selection descriptor is using SSC mode 3. The new session for the same network is + * allowed to be established before releasing the old session when UE's location is updated. + */ + public static final int ROUTE_SSC_MODE_3 = 3; + + /** + * The min acceptable value for the SSC mode of a route selection descriptor. + * @hide + */ + public static final int MIN_ROUTE_SSC_MODE = ROUTE_SSC_MODE_1; + + /** + * The max acceptable value for the SSC mode of a route selection descriptor. + * @hide + */ + public static final int MAX_ROUTE_SSC_MODE = ROUTE_SSC_MODE_3; + + /** @hide */ + @IntDef(prefix = { "ROUTE_SSC_MODE_" }, value = { + ROUTE_SSC_MODE_1, + ROUTE_SSC_MODE_2, + ROUTE_SSC_MODE_3, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RouteSscMode {} + + @IntRange(from = MIN_ROUTE_PRECEDENCE, to = MAX_ROUTE_PRECEDENCE) + private final int mPrecedence; + @RouteSessionType + private final int mSessionType; + @RouteSscMode + @IntRange(from = MIN_ROUTE_SSC_MODE, to = MAX_ROUTE_SSC_MODE) + private final int mSscMode; + private final List<NetworkSliceInfo> mSliceInfo; + private final List<String> mDnn; + + /** @hide */ + RouteSelectionDescriptor(android.hardware.radio.V1_6.RouteSelectionDescriptor rsd) { + this(rsd.precedence, rsd.sessionType.value(), rsd.sscMode.value(), rsd.sliceInfo, + rsd.dnn); + } + + /** @hide */ + public RouteSelectionDescriptor(int precedence, int sessionType, int sscMode, + List<android.hardware.radio.V1_6.SliceInfo> sliceInfo, List<String> dnn) { + mPrecedence = precedence; + mSessionType = sessionType; + mSscMode = sscMode; + mSliceInfo = new ArrayList<NetworkSliceInfo>(); + for (android.hardware.radio.V1_6.SliceInfo si : sliceInfo) { + mSliceInfo.add(sliceInfoBuilder(si)); + } + mDnn = new ArrayList<String>(); + mDnn.addAll(dnn); + } + + private NetworkSliceInfo sliceInfoBuilder(android.hardware.radio.V1_6.SliceInfo si) { + NetworkSliceInfo.Builder builder = new NetworkSliceInfo.Builder() + .setSliceServiceType(si.sst) + .setMappedHplmnSliceServiceType(si.mappedHplmnSst); + if (si.sliceDifferentiator != NetworkSliceInfo.SLICE_DIFFERENTIATOR_NO_SLICE) { + builder + .setSliceDifferentiator(si.sliceDifferentiator) + .setMappedHplmnSliceDifferentiator(si.mappedHplmnSD); + } + return builder.build(); + } + + private RouteSelectionDescriptor(Parcel p) { + mPrecedence = p.readInt(); + mSessionType = p.readInt(); + mSscMode = p.readInt(); + mSliceInfo = p.createTypedArrayList(NetworkSliceInfo.CREATOR); + mDnn = new ArrayList<String>(); + p.readStringList(mDnn); + } + + /** + * Precedence value in the range of 0 to 255. Higher value has lower precedence. + * @return the precedence value for this route selection descriptor. + */ + @IntRange(from = MIN_ROUTE_PRECEDENCE, to = MAX_ROUTE_PRECEDENCE) + public int getPrecedence() { + return mPrecedence; + } + + /** + * This is used for checking which session type defined in 3GPP TS 23.501 is allowed for the + * route in a route selection descriptor. + * @return the session type for this route selection descriptor. + */ + @RouteSessionType + public int getSessionType() { + return mSessionType; + } + + /** + * SSC mode stands for Session and Service Continuity mode (which specifies the IP continuity + * mode) as defined in 3GPP TS 23.501. + * @return the SSC mode for this route selection descriptor. + */ + @RouteSscMode + public int getSscMode() { + return mSscMode; + } + + /** + * This is the list of all the slices available in the route selection descriptor as indicated + * by the network. These are the slices that can be used by the device if this route selection + * descriptor is used based the traffic (see 3GPP TS 23.501 for details). + * @return the list of all the slices available in the route selection descriptor. + */ + public @NonNull List<NetworkSliceInfo> getSliceInfo() { + return mSliceInfo; + } + + /** + * DNN stands for Data Network Name and represents an APN as defined in 3GPP TS 23.003. There + * can be 0 or more DNNs specified in a route selection descriptor. + * @return the list of DNN for this route selection descriptor. + */ + public @NonNull List<String> getDataNetworkName() { + return mDnn; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mPrecedence); + dest.writeInt(mSessionType); + dest.writeInt(mSscMode); + dest.writeTypedList(mSliceInfo, flags); + dest.writeStringList(mDnn); + } + + public static final @NonNull Parcelable.Creator<RouteSelectionDescriptor> CREATOR = + new Parcelable.Creator<RouteSelectionDescriptor>() { + @Override + public RouteSelectionDescriptor createFromParcel(Parcel source) { + return new RouteSelectionDescriptor(source); + } + + @Override + public RouteSelectionDescriptor[] newArray(int size) { + return new RouteSelectionDescriptor[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RouteSelectionDescriptor that = (RouteSelectionDescriptor) o; + return mPrecedence == that.mPrecedence + && mSessionType == that.mSessionType + && mSscMode == that.mSscMode + && mSliceInfo.size() == that.mSliceInfo.size() + && mSliceInfo.containsAll(that.mSliceInfo) + && mDnn.size() == that.mDnn.size() + && mDnn.containsAll(that.mDnn); + } + + @Override + public int hashCode() { + return Objects.hash(mPrecedence, mSessionType, mSscMode, mSliceInfo, mDnn); + } + + @Override + public String toString() { + return "{.precedence = " + mPrecedence + ", .sessionType = " + mSessionType + + ", .sscMode = " + mSscMode + ", .sliceInfo = " + mSliceInfo + + ", .dnn = " + mDnn + "}"; + } +} diff --git a/telephony/java/android/telephony/data/SlicingConfig.aidl b/telephony/java/android/telephony/data/SlicingConfig.aidl new file mode 100644 index 000000000000..ad93d8cafeba --- /dev/null +++ b/telephony/java/android/telephony/data/SlicingConfig.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2021 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.telephony.data; + +parcelable SlicingConfig; diff --git a/telephony/java/android/telephony/data/SlicingConfig.java b/telephony/java/android/telephony/data/SlicingConfig.java new file mode 100644 index 000000000000..990e4d218930 --- /dev/null +++ b/telephony/java/android/telephony/data/SlicingConfig.java @@ -0,0 +1,137 @@ +/** + * Copyright 2021 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.telephony.data; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Represents a slicing configuration + */ +public final class SlicingConfig implements Parcelable { + private final List<UrspRule> mUrspRules; + private final List<NetworkSliceInfo> mSliceInfo; + + public SlicingConfig() { + mUrspRules = new ArrayList<UrspRule>(); + mSliceInfo = new ArrayList<NetworkSliceInfo>(); + } + + /** @hide */ + public SlicingConfig(android.hardware.radio.V1_6.SlicingConfig sc) { + this(sc.urspRules, sc.sliceInfo); + } + + /** @hide */ + public SlicingConfig(List<android.hardware.radio.V1_6.UrspRule> urspRules, + List<android.hardware.radio.V1_6.SliceInfo> sliceInfo) { + mUrspRules = new ArrayList<UrspRule>(); + for (android.hardware.radio.V1_6.UrspRule ur : urspRules) { + mUrspRules.add(new UrspRule(ur.precedence, ur.trafficDescriptors, + ur.routeSelectionDescriptor)); + } + mSliceInfo = new ArrayList<NetworkSliceInfo>(); + for (android.hardware.radio.V1_6.SliceInfo si : sliceInfo) { + mSliceInfo.add(sliceInfoBuilder(si)); + } + } + + private NetworkSliceInfo sliceInfoBuilder(android.hardware.radio.V1_6.SliceInfo si) { + NetworkSliceInfo.Builder builder = new NetworkSliceInfo.Builder() + .setSliceServiceType(si.sst) + .setMappedHplmnSliceServiceType(si.mappedHplmnSst); + if (si.sliceDifferentiator != NetworkSliceInfo.SLICE_DIFFERENTIATOR_NO_SLICE) { + builder + .setSliceDifferentiator(si.sliceDifferentiator) + .setMappedHplmnSliceDifferentiator(si.mappedHplmnSD); + } + return builder.build(); + } + + /** @hide */ + public SlicingConfig(Parcel p) { + mUrspRules = p.createTypedArrayList(UrspRule.CREATOR); + mSliceInfo = p.createTypedArrayList(NetworkSliceInfo.CREATOR); + } + + /** + * This list contains the current URSP rules. Empty list represents that no rules are + * configured. + * @return the current URSP rules for this slicing configuration. + */ + public @NonNull List<UrspRule> getUrspRules() { + return mUrspRules; + } + + /** + * @return the list of all slices for this slicing configuration. + */ + public @NonNull List<NetworkSliceInfo> getSliceInfo() { + return mSliceInfo; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedList(mUrspRules, flags); + dest.writeTypedList(mSliceInfo, flags); + } + + public static final @NonNull Parcelable.Creator<SlicingConfig> CREATOR = + new Parcelable.Creator<SlicingConfig>() { + @Override + public SlicingConfig createFromParcel(Parcel source) { + return new SlicingConfig(source); + } + + @Override + public SlicingConfig[] newArray(int size) { + return new SlicingConfig[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SlicingConfig that = (SlicingConfig) o; + return mUrspRules.size() == that.mUrspRules.size() + && mUrspRules.containsAll(that.mUrspRules) + && mSliceInfo.size() == that.mSliceInfo.size() + && mSliceInfo.containsAll(that.mSliceInfo); + } + + @Override + public int hashCode() { + return Objects.hash(mUrspRules, mSliceInfo); + } + + @Override + public String toString() { + return "{.urspRules = " + mUrspRules + ", .sliceInfo = " + mSliceInfo + "}"; + } +} diff --git a/telephony/java/android/telephony/data/TrafficDescriptor.java b/telephony/java/android/telephony/data/TrafficDescriptor.java index 480379d641b6..d813bc58fe6f 100644 --- a/telephony/java/android/telephony/data/TrafficDescriptor.java +++ b/telephony/java/android/telephony/data/TrafficDescriptor.java @@ -18,20 +18,17 @@ package android.telephony.data; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import java.util.Objects; /** - * A traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2. It is used for URSP traffic - * matching as described in 3GPP TS 24.526 Section 4.2.2. It includes an optional DNN, which, - * if present, must be used for traffic matching; it does not specify the end point to be used for - * the data call. - * @hide + * A traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2. It is used for UE Route Selection + * Policy(URSP) traffic matching as described in 3GPP TS 24.526 Section 4.2.2. It includes an + * optional Data Network Name(DNN), which, if present, must be used for traffic matching; it does + * not specify the end point to be used for the data call. */ -@SystemApi public final class TrafficDescriptor implements Parcelable { private final String mDnn; private final String mOsAppId; @@ -45,8 +42,10 @@ public final class TrafficDescriptor implements Parcelable { * Create a traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2 * @param dnn optional DNN, which must be used for traffic matching, if present * @param osAppId OsId + osAppId of the traffic descriptor + * + * @hide */ - public TrafficDescriptor(@Nullable String dnn, @Nullable String osAppId) { + public TrafficDescriptor(String dnn, String osAppId) { mDnn = dnn; mOsAppId = osAppId; } @@ -55,12 +54,13 @@ public final class TrafficDescriptor implements Parcelable { * DNN stands for Data Network Name and represents an APN as defined in 3GPP TS 23.003. * @return the DNN of this traffic descriptor. */ - public @Nullable String getDnn() { + public @Nullable String getDataNetworkName() { return mDnn; } /** - * OsAppId represents the OsId + OsAppId as defined in 3GPP TS 24.526 Section 5.2. + * OsAppId is the app id as defined in 3GPP TS 24.526 Section 5.2, and it identifies a traffic + * category. * @return the OS App ID of this traffic descriptor. */ public @Nullable String getOsAppId() { @@ -108,4 +108,65 @@ public final class TrafficDescriptor implements Parcelable { public int hashCode() { return Objects.hash(mDnn, mOsAppId); } + + /** + * Provides a convenient way to set the fields of a {@link TrafficDescriptor} when creating a + * new instance. + * + * <p>The example below shows how you might create a new {@code TrafficDescriptor}: + * + * <pre><code> + * + * TrafficDescriptor response = new TrafficDescriptor.Builder() + * .setDnn("") + * .build(); + * </code></pre> + */ + public static final class Builder { + private String mDnn = null; + private String mOsAppId = null; + + /** + * Default constructor for Builder. + */ + public Builder() { + } + + /** + * Set the Data Network Name(DNN). + * + * @return The same instance of the builder. + */ + @NonNull + public Builder setDataNetworkName(@NonNull String dnn) { + this.mDnn = dnn; + return this; + } + + /** + * Set the OS App ID. + * + * @return The same instance of the builder. + */ + @NonNull + public Builder setOsAppId(@NonNull String osAppId) { + this.mOsAppId = osAppId; + return this; + } + + /** + * Build the {@link TrafficDescriptor}. + * + * @throws IllegalArgumentException if DNN and OS App ID are null. + * + * @return the {@link TrafficDescriptor} object. + */ + @NonNull + public TrafficDescriptor build() { + if (this.mDnn == null && this.mOsAppId == null) { + throw new IllegalArgumentException("DNN and OS App ID are null"); + } + return new TrafficDescriptor(this.mDnn, this.mOsAppId); + } + } } diff --git a/telephony/java/android/telephony/data/UrspRule.aidl b/telephony/java/android/telephony/data/UrspRule.aidl new file mode 100644 index 000000000000..2bed583c750e --- /dev/null +++ b/telephony/java/android/telephony/data/UrspRule.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2021 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.telephony.data; + +parcelable UrspRule; diff --git a/telephony/java/android/telephony/data/UrspRule.java b/telephony/java/android/telephony/data/UrspRule.java new file mode 100644 index 000000000000..e2c47fd86b4b --- /dev/null +++ b/telephony/java/android/telephony/data/UrspRule.java @@ -0,0 +1,178 @@ +/** + * Copyright 2021 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.telephony.data; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.radio.V1_6.OptionalDnn; +import android.hardware.radio.V1_6.OptionalOsAppId; +import android.os.Parcel; +import android.os.Parcelable; + + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Represents a single URSP rule as defined in 3GPP TS 24.526. URSP stands for UE Route Selection + * Policy. In 5G, network can provide URSP information to devices which provides information on + * what connection parameters should be used for what traffic. + */ +public final class UrspRule implements Parcelable { + /** + * The min acceptable value for the precedence of a URSP rule. + * @hide + */ + public static final int MIN_URSP_PRECEDENCE = 0; + + /** + * The max acceptable value for the precedence of a URSP rule. + * @hide + */ + public static final int MAX_URSP_PRECEDENCE = 255; + + @IntRange(from = MIN_URSP_PRECEDENCE, to = MAX_URSP_PRECEDENCE) + private final int mPrecedence; + private final List<TrafficDescriptor> mTrafficDescriptors; + private final List<RouteSelectionDescriptor> mRouteSelectionDescriptor; + + UrspRule(android.hardware.radio.V1_6.UrspRule ur) { + this(ur.precedence, ur.trafficDescriptors, ur.routeSelectionDescriptor); + } + + /** @hide */ + public UrspRule(int precedence, + List<android.hardware.radio.V1_6.TrafficDescriptor> trafficDescriptors, + List<android.hardware.radio.V1_6.RouteSelectionDescriptor> routeSelectionDescriptor) { + mPrecedence = precedence; + mTrafficDescriptors = new ArrayList<TrafficDescriptor>(); + for (android.hardware.radio.V1_6.TrafficDescriptor td : trafficDescriptors) { + mTrafficDescriptors.add(convertToTrafficDescriptor(td)); + } + mRouteSelectionDescriptor = new ArrayList<RouteSelectionDescriptor>(); + for (android.hardware.radio.V1_6.RouteSelectionDescriptor rsd : routeSelectionDescriptor) { + mRouteSelectionDescriptor.add(new RouteSelectionDescriptor(rsd)); + } + } + + /** Convert an ArrayList of Bytes to an exactly-sized primitive array */ + private byte[] arrayListToPrimitiveArray(ArrayList<Byte> bytes) { + byte[] ret = new byte[bytes.size()]; + for (int i = 0; i < ret.length; i++) { + ret[i] = bytes.get(i); + } + return ret; + } + + private TrafficDescriptor convertToTrafficDescriptor( + android.hardware.radio.V1_6.TrafficDescriptor td) { + String dnn = td.dnn.getDiscriminator() == OptionalDnn.hidl_discriminator.noinit + ? null : td.dnn.value(); + String osAppId = td.osAppId.getDiscriminator() == OptionalOsAppId.hidl_discriminator.noinit + ? null : new String(arrayListToPrimitiveArray(td.osAppId.value().osAppId)); + TrafficDescriptor.Builder builder = new TrafficDescriptor.Builder(); + if (dnn != null) { + builder.setDataNetworkName(dnn); + } + if (osAppId != null) { + builder.setOsAppId(osAppId); + } + return builder.build(); + } + + private UrspRule(Parcel p) { + mPrecedence = p.readInt(); + mTrafficDescriptors = p.createTypedArrayList(TrafficDescriptor.CREATOR); + mRouteSelectionDescriptor = p.createTypedArrayList(RouteSelectionDescriptor.CREATOR); + } + + /** + * Precedence value in the range of 0 to 255. Higher value has lower precedence. + * @return the precedence value for this URSP rule. + */ + @IntRange(from = MIN_URSP_PRECEDENCE, to = MAX_URSP_PRECEDENCE) + public int getPrecedence() { + return mPrecedence; + } + + /** + * These traffic descriptors are used as a matcher for network requests. + * @return the traffic descriptors which are associated to this URSP rule. + */ + public @NonNull List<TrafficDescriptor> getTrafficDescriptors() { + return mTrafficDescriptors; + } + + /** + * List of routes (connection parameters) that must be used by the device for requests matching + * a traffic descriptor. + * @return the route selection descriptors which are associated to this URSP rule. + */ + public @NonNull List<RouteSelectionDescriptor> getRouteSelectionDescriptor() { + return mRouteSelectionDescriptor; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mPrecedence); + dest.writeTypedList(mTrafficDescriptors, flags); + dest.writeTypedList(mRouteSelectionDescriptor, flags); + } + + public static final @NonNull Parcelable.Creator<UrspRule> CREATOR = + new Parcelable.Creator<UrspRule>() { + @Override + public UrspRule createFromParcel(Parcel source) { + return new UrspRule(source); + } + + @Override + public UrspRule[] newArray(int size) { + return new UrspRule[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + UrspRule that = (UrspRule) o; + return mPrecedence == that.mPrecedence + && mTrafficDescriptors.size() == that.mTrafficDescriptors.size() + && mTrafficDescriptors.containsAll(that.mTrafficDescriptors) + && mRouteSelectionDescriptor.size() == that.mRouteSelectionDescriptor.size() + && mRouteSelectionDescriptor.containsAll(that.mRouteSelectionDescriptor); + } + + @Override + public int hashCode() { + return Objects.hash(mPrecedence, mTrafficDescriptors, mRouteSelectionDescriptor); + } + + @Override + public String toString() { + return "{.precedence = " + mPrecedence + ", .trafficDescriptors = " + mTrafficDescriptors + + ", .routeSelectionDescriptor = " + mRouteSelectionDescriptor + "}"; + } +} diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java index 0aff99709a52..dfe5e6c93f53 100755 --- a/telephony/java/android/telephony/ims/ImsCallSession.java +++ b/telephony/java/android/telephony/ims/ImsCallSession.java @@ -766,7 +766,10 @@ public class ImsCallSession { * The method is only valid to call when the session state is in * {@link ImsCallSession.State#IDLE}. * - * @param callee dialed string to make the call to + * @param callee dial string to make the call to. The platform passes the dialed number + * entered by the user as-is. The {@link ImsService} should ensure that the + * number is formatted in SIP messages appropriately (e.g. using + * {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}). * @param profile call profile to make the call with the specified service type, * call type and media information * @see Listener#callSessionStarted, Listener#callSessionStartFailed @@ -788,7 +791,10 @@ public class ImsCallSession { * The method is only valid to call when the session state is in * {@link ImsCallSession.State#IDLE}. * - * @param participants participant list to initiate an IMS conference call + * @param participants participant list to initiate an IMS conference call. The platform passes + * the dialed numbers entered by the user as-is. The {@link ImsService} should + * ensure that the number is formatted in SIP messages appropriately (e.g. using + * {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}). * @param profile call profile to make the call with the specified service type, * call type and media information * @see Listener#callSessionStarted, Listener#callSessionStartFailed diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index ae5f997360cd..d812b4641748 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -2447,4 +2447,10 @@ interface ITelephony { * Gets the current phone capability. */ PhoneCapability getPhoneCapability(); + + /** + * Request to get the current slicing configuration including URSP rules and + * NSSAIs (configured, allowed and rejected). + */ + void getSlicingConfig(in ResultReceiver callback); } diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 822fc44146d6..fe8e6715fe35 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -527,6 +527,7 @@ public interface RILConstants { int RIL_REQUEST_SET_DATA_THROTTLING = 221; int RIL_REQUEST_SET_ALLOWED_NETWORK_TYPES_BITMAP = 222; int RIL_REQUEST_GET_ALLOWED_NETWORK_TYPES_BITMAP = 223; + int RIL_REQUEST_GET_SLICING_CONFIG = 224; /* Responses begin */ int RIL_RESPONSE_ACKNOWLEDGEMENT = 800; diff --git a/test-mock/Android.bp b/test-mock/Android.bp index 460a26d63cd6..0bb61987a514 100644 --- a/test-mock/Android.bp +++ b/test-mock/Android.bp @@ -32,7 +32,9 @@ java_sdk_library { ":android-test-mock-sources", // Note: Below are NOT APIs of this library. We only take APIs under // the android.test.mock package. They however provide private APIs that - // android.test.mock APIs references to. + // android.test.mock APIs references to. We need to have the classes in + // source code form to have access to the @hide comment which disappears + // when the classes are compiled into a Jar library. ":framework-core-sources-for-test-mock", ":framework_native_aidl", ], @@ -46,6 +48,14 @@ java_sdk_library { api_packages: [ "android.test.mock", ], + // Only include android.test.mock.* classes. Jarjar rules below removes + // classes in other packages like android.content. In order to keep the + // list up-to-date, permitted_packages ensures that the library contains + // clases under android.test.mock after the jarjar rules are applied. + jarjar_rules: "jarjar-rules.txt", + permitted_packages: [ + "android.test.mock", + ], compile_dex: true, default_to_stubs: true, dist_group: "android", diff --git a/test-mock/jarjar-rules.txt b/test-mock/jarjar-rules.txt new file mode 100644 index 000000000000..4420a4413f5b --- /dev/null +++ b/test-mock/jarjar-rules.txt @@ -0,0 +1,7 @@ +zap android.accounts.** +zap android.app.** +zap android.content.** +zap android.database.** +zap android.os.** +zap android.util.** +zap android.view.** diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java index 0f84f6ebe522..c9a8947ab5ef 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java @@ -322,6 +322,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID); verify(mSafeModeTimeoutAlarm).cancel(); assertFalse(mGatewayConnection.isInSafeMode()); + verifySafeModeStateAndCallbackFired(1 /* invocationCount */, false /* isInSafeMode */); } @Test @@ -391,6 +392,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID); + verifySafeModeStateAndCallbackFired(2 /* invocationCount */, false /* isInSafeMode */); assertFalse(mGatewayConnection.isInSafeMode()); } @@ -400,7 +402,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection mTestLooper.dispatchAll(); triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID); - assertFalse(mGatewayConnection.isInSafeMode()); + verifySafeModeStateAndCallbackFired(1 /* invocationCount */, false /* isInSafeMode */); // Trigger a failed validation, and the subsequent safemode timeout. triggerValidation(NetworkAgent.VALIDATION_STATUS_NOT_VALID); @@ -416,7 +418,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection runnableCaptor.getValue().run(); mTestLooper.dispatchAll(); - assertTrue(mGatewayConnection.isInSafeMode()); + verifySafeModeStateAndCallbackFired(2 /* invocationCount */, true /* isInSafeMode */); } private Consumer<VcnNetworkAgent> setupNetworkAndGetUnwantedCallback() { diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java index a696b3ae28f7..64d0bca15ce9 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -23,7 +23,6 @@ import static com.android.server.vcn.VcnTestUtils.setupIpSecManager; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.CALLS_REAL_METHODS; @@ -301,6 +300,11 @@ public class VcnGatewayConnectionTestBase { expectCanceled); } + protected void verifySafeModeStateAndCallbackFired(int invocationCount, boolean isInSafeMode) { + verify(mGatewayStatusCallback, times(invocationCount)).onSafeModeStatusChanged(); + assertEquals(isInSafeMode, mGatewayConnection.isInSafeMode()); + } + protected void verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent( @NonNull State expectedState) { // Set a VcnNetworkAgent, and expect it to be unregistered and cleared @@ -314,9 +318,8 @@ public class VcnGatewayConnectionTestBase { delayedEvent.run(); mTestLooper.dispatchAll(); - verify(mGatewayStatusCallback).onSafeModeStatusChanged(); assertEquals(expectedState, mGatewayConnection.getCurrentState()); - assertTrue(mGatewayConnection.isInSafeMode()); + verifySafeModeStateAndCallbackFired(1, true); verify(mockNetworkAgent).unregister(); assertNull(mGatewayConnection.getNetworkAgent()); diff --git a/tools/bit/print.cpp b/tools/bit/print.cpp index 35feda11ec29..8bc6f167bd7f 100644 --- a/tools/bit/print.cpp +++ b/tools/bit/print.cpp @@ -17,6 +17,7 @@ #include "print.h" #include <sys/ioctl.h> +#include <stdarg.h> #include <stdio.h> #include <unistd.h> diff --git a/tools/bit/util.h b/tools/bit/util.h index 7ccdab103d9a..8c66911b3c48 100644 --- a/tools/bit/util.h +++ b/tools/bit/util.h @@ -17,6 +17,8 @@ #ifndef UTIL_H #define UTIL_H +#include <sys/types.h> + #include <map> #include <string> #include <vector> diff --git a/tools/streaming_proto/Errors.cpp b/tools/streaming_proto/Errors.cpp index 0cd9037dcb55..6890d99b104c 100644 --- a/tools/streaming_proto/Errors.cpp +++ b/tools/streaming_proto/Errors.cpp @@ -1,5 +1,6 @@ #include "Errors.h" +#include <stdarg.h> #include <stdlib.h> namespace android { |