diff options
300 files changed, 7696 insertions, 2077 deletions
diff --git a/Android.bp b/Android.bp index 6964dafced1b..7ce8b31e42c2 100644 --- a/Android.bp +++ b/Android.bp @@ -49,9 +49,8 @@ java_defaults { "rs/java/**/*.java", ":framework-javastream-protos", - // TODO: Resolve circular library dependency and remove media1-srcs and mediasession2-srcs + // TODO: Resolve circular library dependency and remove media1-srcs ":media1-srcs", - ":mediasession2-srcs", "core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl", "core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl", @@ -376,6 +375,7 @@ java_defaults { "core/java/android/view/IApplicationToken.aidl", "core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl", "core/java/android/view/IDockedStackListener.aidl", + "core/java/android/view/IDisplayFoldListener.aidl", "core/java/android/view/IGraphicsStats.aidl", "core/java/android/view/IGraphicsStatsCallback.aidl", "core/java/android/view/IInputFilter.aidl", @@ -828,19 +828,6 @@ java_library_host { } // A temporary build target that is conditionally included on the bootclasspath if -// org.apache.http.legacy library has been removed and which provides support for -// maintaining backwards compatibility for APKs that target pre-P and depend on -// org.apache.http.legacy classes. This is used iff REMOVE_OAHL_FROM_BCP=true is -// specified on the build command line. -java_library { - name: "framework-oahl-backward-compatibility", - installable: true, - srcs: [ - "core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java", - ], -} - -// A temporary build target that is conditionally included on the bootclasspath if // android.test.base library has been removed and which provides support for // maintaining backwards compatibility for APKs that target pre-P and depend on // android.test.base classes. This is used iff REMOVE_ATB_FROM_BCP=true is diff --git a/api/current.txt b/api/current.txt index 361ee74becdd..d431e3920661 100644 --- a/api/current.txt +++ b/api/current.txt @@ -3802,6 +3802,7 @@ package android.app { method @Deprecated public void onStateNotSaved(); method @CallSuper protected void onStop(); method protected void onTitleChanged(CharSequence, int); + method public void onTopResumedActivityChanged(boolean); method public boolean onTouchEvent(android.view.MotionEvent); method public boolean onTrackballEvent(android.view.MotionEvent); method public void onTrimMemory(int); @@ -5776,6 +5777,7 @@ package android.app { method public String addAutomaticZenRule(android.app.AutomaticZenRule); method public boolean areBubblesAllowed(); method public boolean areNotificationsEnabled(); + method public boolean areNotificationsPaused(); method public boolean canNotifyAsPackage(String); method public void cancel(int); method public void cancel(String, int); @@ -14167,6 +14169,7 @@ package android.graphics { ctor public ImageFormat(); method public static int getBitsPerPixel(int); field public static final int DEPTH16 = 1144402265; // 0x44363159 + field public static final int DEPTH_JPEG = 1768253795; // 0x69656963 field public static final int DEPTH_POINT_CLOUD = 257; // 0x101 field public static final int FLEX_RGBA_8888 = 42; // 0x2a field public static final int FLEX_RGB_888 = 41; // 0x29 @@ -16171,6 +16174,7 @@ package android.hardware { method public long getUsage(); method public int getWidth(); method public boolean isClosed(); + method public static boolean isSupported(int, int, int, int, long); method public void writeToParcel(android.os.Parcel, int); field public static final int BLOB = 33; // 0x21 field public static final android.os.Parcelable.Creator<android.hardware.HardwareBuffer> CREATOR; @@ -25153,6 +25157,8 @@ package android.media { field public static final int METADATA_KEY_DATE = 5; // 0x5 field public static final int METADATA_KEY_DISC_NUMBER = 14; // 0xe field public static final int METADATA_KEY_DURATION = 9; // 0x9 + field public static final int METADATA_KEY_EXIF_LENGTH = 34; // 0x22 + field public static final int METADATA_KEY_EXIF_OFFSET = 33; // 0x21 field public static final int METADATA_KEY_GENRE = 6; // 0x6 field public static final int METADATA_KEY_HAS_AUDIO = 16; // 0x10 field public static final int METADATA_KEY_HAS_IMAGE = 26; // 0x1a @@ -25956,6 +25962,7 @@ package android.media { method @NonNull public abstract android.media.MediaSession2 onGetPrimarySession(); method @Nullable public abstract android.media.MediaSession2Service.MediaNotification onUpdateNotification(@NonNull android.media.MediaSession2); method public final void removeSession(@NonNull android.media.MediaSession2); + field public static final String SERVICE_INTERFACE = "android.media.MediaSession2Service"; } public static class MediaSession2Service.MediaNotification { @@ -27348,6 +27355,7 @@ package android.media.session { method public int getRatingType(); method @Nullable public android.app.PendingIntent getSessionActivity(); method @NonNull public android.media.session.MediaSession.Token getSessionToken(); + method public String getTag(); method @NonNull public android.media.session.MediaController.TransportControls getTransportControls(); method public void registerCallback(@NonNull android.media.session.MediaController.Callback); method public void registerCallback(@NonNull android.media.session.MediaController.Callback, @Nullable android.os.Handler); @@ -27681,6 +27689,7 @@ package android.media.tv { field public static final String TYPE_DVB_T2 = "TYPE_DVB_T2"; field public static final String TYPE_ISDB_C = "TYPE_ISDB_C"; field public static final String TYPE_ISDB_S = "TYPE_ISDB_S"; + field public static final String TYPE_ISDB_S3 = "TYPE_ISDB_S3"; field public static final String TYPE_ISDB_T = "TYPE_ISDB_T"; field public static final String TYPE_ISDB_TB = "TYPE_ISDB_TB"; field public static final String TYPE_NTSC = "TYPE_NTSC"; @@ -30587,6 +30596,7 @@ package android.nfc { } public final class NfcAdapter { + method public boolean deviceSupportsNfcSecure(); method public void disableForegroundDispatch(android.app.Activity); method @Deprecated public void disableForegroundNdefPush(android.app.Activity); method public void disableReaderMode(android.app.Activity); @@ -30599,6 +30609,7 @@ package android.nfc { method @Deprecated public boolean invokeBeam(android.app.Activity); method public boolean isEnabled(); method @Deprecated public boolean isNdefPushEnabled(); + method public boolean isNfcSecureEnabled(); method @Deprecated public void setBeamPushUris(android.net.Uri[], android.app.Activity); method @Deprecated public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity); method @Deprecated public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...); @@ -38649,6 +38660,7 @@ package android.provider { field public static final String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS"; field public static final String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS"; field public static final String ACTION_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS"; + field public static final String ACTION_PROCESS_WIFI_EASY_CONNECT_QR_CODE = "android.settings.PROCESS_WIFI_EASY_CONNECT_QR_CODE"; field public static final String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS"; field public static final String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; field public static final String ACTION_REQUEST_SET_AUTOFILL_SERVICE = "android.settings.REQUEST_SET_AUTOFILL_SERVICE"; @@ -38682,6 +38694,7 @@ package android.provider { field public static final String EXTRA_DO_NOT_DISTURB_MODE_ENABLED = "android.settings.extra.do_not_disturb_mode_enabled"; field public static final String EXTRA_DO_NOT_DISTURB_MODE_MINUTES = "android.settings.extra.do_not_disturb_mode_minutes"; field public static final String EXTRA_INPUT_METHOD_ID = "input_method_id"; + field public static final String EXTRA_QR_CODE = "android.provider.extra.QR_CODE"; field public static final String EXTRA_SUB_ID = "android.provider.extra.SUB_ID"; field public static final String INTENT_CATEGORY_USAGE_ACCESS_CONFIG = "android.intent.category.USAGE_ACCESS_CONFIG"; field public static final String METADATA_USAGE_ACCESS_REASON = "android.settings.metadata.USAGE_ACCESS_REASON"; @@ -45013,6 +45026,7 @@ package android.telephony { method @Deprecated public void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean); method public boolean updateAvailableNetworks(java.util.List<android.telephony.AvailableNetworkInfo>); field public static final String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL"; + field public static final String ACTION_NETWORK_COUNTRY_CHANGED = "android.telephony.action.NETWORK_COUNTRY_CHANGED"; field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE"; field public static final String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE"; field public static final String ACTION_SECRET_CODE = "android.telephony.action.SECRET_CODE"; @@ -45051,6 +45065,7 @@ package android.telephony { field public static final String EXTRA_INCOMING_NUMBER = "incoming_number"; field public static final String EXTRA_IS_REFRESH = "android.telephony.extra.IS_REFRESH"; field public static final String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT"; + field public static final String EXTRA_NETWORK_COUNTRY = "android.telephony.extra.NETWORK_COUNTRY"; field public static final String EXTRA_NOTIFICATION_COUNT = "android.telephony.extra.NOTIFICATION_COUNT"; field public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telephony.extra.PHONE_ACCOUNT_HANDLE"; field public static final String EXTRA_PRECISE_CARRIER_ID = "android.telephony.extra.PRECISE_CARRIER_ID"; diff --git a/api/system-current.txt b/api/system-current.txt index 8b4de48addbe..90191b603718 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3622,6 +3622,10 @@ package android.media.session { method public void unregisterCallback(@NonNull android.media.session.ControllerCallbackLink); } + public static final class MediaController.PlaybackInfo implements android.os.Parcelable { + ctor public MediaController.PlaybackInfo(int, int, int, int, android.media.AudioAttributes); + } + public abstract static class MediaSession.Callback { method public void onSetMediaButtonEventDelegate(@NonNull android.media.session.MediaSessionEngine.MediaButtonEventDelegate); } @@ -4092,6 +4096,7 @@ package android.net { public final class IpPrefix implements android.os.Parcelable { ctor public IpPrefix(java.net.InetAddress, int); + ctor public IpPrefix(String); } public final class IpSecManager { @@ -4114,6 +4119,7 @@ package android.net { ctor public LinkAddress(java.net.InetAddress, int, int, int); ctor public LinkAddress(java.net.InetAddress, int); ctor public LinkAddress(String); + ctor public LinkAddress(String, int, int); method public boolean isGlobalPreferred(); method public boolean isIPv4(); method public boolean isIPv6(); @@ -4124,6 +4130,7 @@ package android.net { ctor public LinkProperties(); ctor public LinkProperties(android.net.LinkProperties); method public boolean addDnsServer(java.net.InetAddress); + method public boolean addLinkAddress(android.net.LinkAddress); method public boolean addRoute(android.net.RouteInfo); method public void clear(); method @Nullable public android.net.IpPrefix getNat64Prefix(); @@ -4138,6 +4145,7 @@ package android.net { method public boolean isProvisioned(); method public boolean isReachable(java.net.InetAddress); method public boolean removeDnsServer(java.net.InetAddress); + method public boolean removeLinkAddress(android.net.LinkAddress); method public boolean removeRoute(android.net.RouteInfo); method public void setDnsServers(java.util.Collection<java.net.InetAddress>); method public void setDomains(String); @@ -4154,6 +4162,7 @@ package android.net { } public class Network implements android.os.Parcelable { + ctor public Network(android.net.Network); method public android.net.Network getPrivateDnsBypassingCopy(); } @@ -4261,6 +4270,9 @@ package android.net { method public static void setThreadStatsTagApp(); method public static void setThreadStatsTagBackup(); method public static void setThreadStatsTagRestore(); + field public static final int TAG_SYSTEM_DHCP = -192; // 0xffffff40 + field public static final int TAG_SYSTEM_DHCP_SERVER = -186; // 0xffffff46 + field public static final int TAG_SYSTEM_PROBE = -190; // 0xffffff42 } public class VpnService extends android.app.Service { @@ -4286,6 +4298,8 @@ package android.net.apf { public class ApfCapabilities { ctor public ApfCapabilities(int, int, int); + method public static boolean getApfDrop8023Frames(android.content.Context); + method public static int[] getApfEthTypeBlackList(android.content.Context); method public boolean hasDataAccess(); field public final int apfPacketFormat; field public final int apfVersionSupported; @@ -4482,10 +4496,19 @@ package android.net.metrics { package android.net.util { public class SocketUtils { + method public static void addArpEntry(java.net.Inet4Address, android.net.MacAddress, String, java.io.FileDescriptor) throws java.io.IOException; + method public static void attachControlPacketFilter(java.io.FileDescriptor, int) throws java.net.SocketException; + method public static void attachDhcpFilter(java.io.FileDescriptor) throws java.net.SocketException; + method public static void attachRaFilter(java.io.FileDescriptor, int) throws java.net.SocketException; + method public static void bindSocket(java.io.FileDescriptor, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException; method public static void bindSocketToInterface(java.io.FileDescriptor, String) throws android.system.ErrnoException; + method public static void closeSocket(java.io.FileDescriptor) throws java.io.IOException; + method public static void connectSocket(java.io.FileDescriptor, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException; method public static java.net.SocketAddress makeNetlinkSocketAddress(int, int); method public static java.net.SocketAddress makePacketSocketAddress(short, int); method public static java.net.SocketAddress makePacketSocketAddress(int, byte[]); + method public static void sendTo(java.io.FileDescriptor, byte[], int, int, int, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException; + method public static void setSocketTimeValueOption(java.io.FileDescriptor, int, int, long) throws android.system.ErrnoException; } } @@ -5079,6 +5102,7 @@ package android.nfc { method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableNdefPush(); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler); method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int); + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setNfcSecure(boolean); field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1 } @@ -5744,6 +5768,12 @@ package android.provider { field public static final String NAMESPACE = "activity_manager"; } + public static interface DeviceConfig.AttentionManagerService { + field public static final String NAMESPACE = "attention_manager_service"; + field public static final String PROPERTY_COMPONENT_NAME = "component_name"; + field public static final String PROPERTY_SERVICE_ENABLED = "service_enabled"; + } + public static interface DeviceConfig.FsiBoot { field public static final String NAMESPACE = "fsi_boot"; field public static final String OOB_ENABLED = "oob_enabled"; @@ -5752,8 +5782,8 @@ package android.provider { public static interface DeviceConfig.IntelligenceAttention { field public static final String NAMESPACE = "intelligence_attention"; - field public static final String PROPERTY_ATTENTION_CHECK_ENABLED = "attention_check_enabled"; - field public static final String PROPERTY_ATTENTION_CHECK_SETTINGS = "attention_check_settings"; + field public static final String PROPERTY_ATTENTION_ENABLED = "attention_enabled"; + field public static final String PROPERTY_ATTENTION_SETTINGS = "attention_settings"; } public static interface DeviceConfig.OnPropertyChangedListener { @@ -7522,7 +7552,7 @@ package android.telephony { } public class PhoneStateListener { - method public void onCallAttributesChanged(android.telephony.CallAttributes); + method public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes); method public void onCallDisconnectCauseChanged(int, int); method public void onPreciseCallStateChanged(android.telephony.PreciseCallState); method public void onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState); @@ -7928,7 +7958,7 @@ package android.telephony { package android.telephony.data { public final class DataCallResponse implements android.os.Parcelable { - ctor public DataCallResponse(int, int, int, int, @Nullable String, @Nullable String, @Nullable java.util.List<android.net.LinkAddress>, @Nullable java.util.List<java.net.InetAddress>, @Nullable java.util.List<java.net.InetAddress>, @Nullable java.util.List<java.lang.String>, int); + ctor public DataCallResponse(int, int, int, int, int, @Nullable String, @Nullable java.util.List<android.net.LinkAddress>, @Nullable java.util.List<java.net.InetAddress>, @Nullable java.util.List<java.net.InetAddress>, @Nullable java.util.List<java.lang.String>, int); ctor public DataCallResponse(android.os.Parcel); method public int describeContents(); method public int getActive(); @@ -7939,9 +7969,9 @@ package android.telephony.data { method @NonNull public String getIfname(); method public int getMtu(); method @NonNull public java.util.List<java.lang.String> getPcscfs(); + method public int getProtocolType(); method public int getStatus(); method public int getSuggestedRetryTime(); - method @NonNull public String getType(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR; } @@ -7955,8 +7985,8 @@ package android.telephony.data { method public int getMtu(); method public String getPassword(); method public int getProfileId(); - method public String getProtocol(); - method public String getRoamingProtocol(); + method public int getProtocol(); + method public int getRoamingProtocol(); method public int getSupportedApnTypesBitmap(); method public int getType(); method public String getUserName(); diff --git a/api/test-current.txt b/api/test-current.txt index 049e0025a59b..9c27535664fe 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -516,6 +516,7 @@ package android.graphics { public final class Bitmap implements android.os.Parcelable { method public void eraseColor(@ColorLong long); + method public void setColorSpace(@NonNull android.graphics.ColorSpace); } public final class ImageDecoder implements java.lang.AutoCloseable { @@ -811,6 +812,7 @@ package android.net { public final class IpPrefix implements android.os.Parcelable { ctor public IpPrefix(java.net.InetAddress, int); + ctor public IpPrefix(String); } public final class IpSecManager { @@ -819,6 +821,9 @@ package android.net { public class LinkAddress implements android.os.Parcelable { ctor public LinkAddress(java.net.InetAddress, int, int, int); + ctor public LinkAddress(java.net.InetAddress, int); + ctor public LinkAddress(String); + ctor public LinkAddress(String, int, int); method public boolean isGlobalPreferred(); method public boolean isIPv4(); method public boolean isIPv6(); @@ -828,6 +833,7 @@ package android.net { public final class LinkProperties implements android.os.Parcelable { ctor public LinkProperties(android.net.LinkProperties); method public boolean addDnsServer(java.net.InetAddress); + method public boolean addLinkAddress(android.net.LinkAddress); method @Nullable public android.net.IpPrefix getNat64Prefix(); method public java.util.List<java.net.InetAddress> getPcscfServers(); method public String getTcpBufferSizes(); @@ -840,6 +846,7 @@ package android.net { method public boolean isProvisioned(); method public boolean isReachable(java.net.InetAddress); method public boolean removeDnsServer(java.net.InetAddress); + method public boolean removeLinkAddress(android.net.LinkAddress); method public boolean removeRoute(android.net.RouteInfo); method public void setNat64Prefix(android.net.IpPrefix); method public void setPcscfServers(java.util.Collection<java.net.InetAddress>); @@ -850,6 +857,7 @@ package android.net { } public class Network implements android.os.Parcelable { + ctor public Network(android.net.Network); method public android.net.Network getPrivateDnsBypassingCopy(); } @@ -890,6 +898,9 @@ package android.net { method public static long getLoopbackRxPackets(); method public static long getLoopbackTxBytes(); method public static long getLoopbackTxPackets(); + field public static final int TAG_SYSTEM_DHCP = -192; // 0xffffff40 + field public static final int TAG_SYSTEM_DHCP_SERVER = -186; // 0xffffff46 + field public static final int TAG_SYSTEM_PROBE = -190; // 0xffffff42 } } @@ -898,6 +909,8 @@ package android.net.apf { public class ApfCapabilities { ctor public ApfCapabilities(int, int, int); + method public static boolean getApfDrop8023Frames(android.content.Context); + method public static int[] getApfEthTypeBlackList(android.content.Context); method public boolean hasDataAccess(); field public final int apfPacketFormat; field public final int apfVersionSupported; @@ -1091,6 +1104,26 @@ package android.net.metrics { } +package android.net.util { + + public class SocketUtils { + method public static void addArpEntry(java.net.Inet4Address, android.net.MacAddress, String, java.io.FileDescriptor) throws java.io.IOException; + method public static void attachControlPacketFilter(java.io.FileDescriptor, int) throws java.net.SocketException; + method public static void attachDhcpFilter(java.io.FileDescriptor) throws java.net.SocketException; + method public static void attachRaFilter(java.io.FileDescriptor, int) throws java.net.SocketException; + method public static void bindSocket(java.io.FileDescriptor, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException; + method public static void bindSocketToInterface(java.io.FileDescriptor, String) throws android.system.ErrnoException; + method public static void closeSocket(java.io.FileDescriptor) throws java.io.IOException; + method public static void connectSocket(java.io.FileDescriptor, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException; + method public static java.net.SocketAddress makeNetlinkSocketAddress(int, int); + method public static java.net.SocketAddress makePacketSocketAddress(short, int); + method public static java.net.SocketAddress makePacketSocketAddress(int, byte[]); + method public static void sendTo(java.io.FileDescriptor, byte[], int, int, int, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException; + method public static void setSocketTimeValueOption(java.io.FileDescriptor, int, int, long) throws android.system.ErrnoException; + } + +} + package android.os { public class Build { diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 8e56bef6856e..812a2f2a76ae 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -24,15 +24,18 @@ import "frameworks/base/cmds/statsd/src/atom_field_options.proto"; import "frameworks/base/core/proto/android/app/enums.proto"; import "frameworks/base/core/proto/android/app/settings_enums.proto"; import "frameworks/base/core/proto/android/app/job/enums.proto"; +import "frameworks/base/core/proto/android/bluetooth/a2dp/enums.proto"; import "frameworks/base/core/proto/android/bluetooth/enums.proto"; import "frameworks/base/core/proto/android/bluetooth/hci/enums.proto"; import "frameworks/base/core/proto/android/bluetooth/hfp/enums.proto"; +import "frameworks/base/core/proto/android/bluetooth/smp/enums.proto"; import "frameworks/base/core/proto/android/debug/enums.proto"; import "frameworks/base/core/proto/android/hardware/biometrics/enums.proto"; import "frameworks/base/core/proto/android/net/networkcapabilities.proto"; import "frameworks/base/core/proto/android/os/enums.proto"; import "frameworks/base/core/proto/android/server/connectivity/data_stall_event.proto"; import "frameworks/base/core/proto/android/server/enums.proto"; +import "frameworks/base/core/proto/android/server/job/enums.proto"; import "frameworks/base/core/proto/android/server/location/enums.proto"; import "frameworks/base/core/proto/android/service/procstats_enum.proto"; import "frameworks/base/core/proto/android/service/usb.proto"; @@ -213,6 +216,24 @@ message Atom { WatchdogRollbackOccurred watchdog_rollback_occurred = 147; BiometricHalDeathReported biometric_hal_death_reported = 148; BubbleUIChanged bubble_ui_changed = 149; + ScheduledJobConstraintChanged scheduled_job_constraint_changed = 150; + BluetoothActiveDeviceChanged bluetooth_active_device_changed = 151; + BluetoothA2dpPlaybackStateChanged bluetooth_a2dp_playback_state_changed = 152; + BluetoothA2dpCodecConfigChanged bluetooth_a2dp_codec_config_changed = 153; + BluetoothA2dpCodecCapabilityChanged bluetooth_a2dp_codec_capability_changed = 154; + BluetoothA2dpAudioUnderrunReported bluetooth_a2dp_audio_underrun_reported = 155; + BluetoothA2dpAudioOverrunReported bluetooth_a2dp_audio_overrun_reported = 156; + BluetoothDeviceRssiReported bluetooth_device_rssi_reported = 157; + BluetoothDeviceFailedContactCounterReported bluetooth_device_failed_contact_counter_reported = 158; + BluetoothDeviceTxPowerLevelReported bluetooth_device_tx_power_level_reported = 159; + BluetoothHciTimeoutReported bluetooth_hci_timeout_reported = 160; + BluetoothQualityReportReported bluetooth_quality_report_reported = 161; + BluetoothManufacturerInfoReported bluetooth_device_info_reported = 162; + BluetoothRemoteVersionInfoReported bluetooth_remote_version_info_reported = 163; + BluetoothSdpAttributeReported bluetooth_sdp_attribute_reported = 164; + BluetoothBondStateChanged bluetooth_bond_state_changed = 165; + BluetoothClassicPairingEventReported bluetooth_classic_pairing_event_reported = 166; + BluetoothSmpPairingEventReported bluetooth_smp_pairing_event_reported = 167; } // Pulled events will start at field 10000. @@ -1407,6 +1428,27 @@ message BluetoothScoConnectionStateChanged { optional android.bluetooth.hfp.ScoCodec codec = 3; } +/** + * Logged when active device of a profile changes + * + * Logged from: + * packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpService.java + * packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetService.java + * packages/apps/Bluetooth/src/com/android/bluetooth/hearingaid/HearingAidService.java + */ +message BluetoothActiveDeviceChanged { + // The profile whose active device has changed. Eg. A2DP, HEADSET, HEARING_AID + // From android.bluetooth.BluetoothProfile + optional int32 bt_profile = 1; + // An identifier that can be used to match events for this new active device. + // Currently, this is a salted hash of the MAC address of this Bluetooth device. + // Salt: Randomly generated 256 bit value + // Hash algorithm: HMAC-SHA256 + // Size: 32 byte + // Default: null or empty if there is no active device for this profile + optional bytes obfuscated_id = 2 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + // Logs when there is an event affecting Bluetooth device's link layer connection. // - This event is triggered when there is a related HCI command or event // - Users of this metrics can deduce Bluetooth device's connection state from these events @@ -1509,6 +1551,516 @@ message WatchdogRollbackOccurred { optional int32 package_version_code = 3; } +/** + * Logs when there is a change in Bluetooth A2DP playback state + * + * Logged from: + * packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpService.java + */ +message BluetoothA2dpPlaybackStateChanged { + // An identifier that can be used to match events for this device. + // Currently, this is a salted hash of the MAC address of this Bluetooth device. + // Salt: Randomly generated 256 bit value + // Hash algorithm: HMAC-SHA256 + // Size: 32 byte + // Default: null or empty if the device identifier is not known + optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; + // Current playback state + // Default: PLAYBACK_STATE_UNKNOWN + optional android.bluetooth.a2dp.PlaybackStateEnum playback_state = 2; + // Current audio coding mode + // Default: AUDIO_CODING_MODE_UNKNOWN + optional android.bluetooth.a2dp.AudioCodingModeEnum audio_coding_mode = 3; +} + +/** + * Logs when there is a change in A2DP codec config for a particular remote device + * + * Logged from: + * frameworks/base/core/java/android/bluetooth/BluetoothCodecConfig.java + * packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpService.java + */ +message BluetoothA2dpCodecConfigChanged { + // An identifier that can be used to match events for this device. + // Currently, this is a salted hash of the MAC address of this Bluetooth device. + // Salt: Randomly generated 256 bit value + // Hash algorithm: HMAC-SHA256 + // Size: 32 byte + // Default: null or empty if the device identifier is not known + optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; + // Type of codec as defined by various SOURCE_CODEC_TYPE_* constants in BluetoothCodecConfig + // Default SOURCE_CODEC_TYPE_INVALID + optional int32 codec_type = 2; + // Codec priroity, the higher the more preferred, -1 for disabled + // Default: CODEC_PRIORITY_DEFAULT + optional int32 codec_priority = 3; + // Sample rate in Hz as defined by various SAMPLE_RATE_* constants in BluetoothCodecConfig + // Default: SAMPLE_RATE_NONE + optional int32 sample_rate = 4; + // Bits per sample as defined by various BITS_PER_SAMPLE_* constants in BluetoothCodecConfig + // Default: BITS_PER_SAMPLE_NONE + optional int32 bits_per_sample = 5; + // Channel mode as defined by various CHANNEL_MODE_* constants in BluetoothCodecConfig + // Default: CHANNEL_MODE_NONE + optional int32 channel_mode = 6; + // Codec specific values + // Default 0 + optional int64 codec_specific_1 = 7; + optional int64 codec_specific_2 = 8; + optional int64 codec_specific_3 = 9; + optional int64 codec_specific_4 = 10; +} + +/** + * Logs when there is a change in selectable A2DP codec capability for a paricular remote device + * Each codec's capability is logged separately due to statsd restriction + * + * Logged from: + * frameworks/base/core/java/android/bluetooth/BluetoothCodecConfig.java + * packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpService.java + */ +message BluetoothA2dpCodecCapabilityChanged { + // An identifier that can be used to match events for this device. + // Currently, this is a salted hash of the MAC address of this Bluetooth device. + // Salt: Randomly generated 256 bit value + // Hash algorithm: HMAC-SHA256 + // Size: 32 byte + // Default: null or empty if the device identifier is not known + optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; + // Type of codec as defined by various SOURCE_CODEC_TYPE_* constants in BluetoothCodecConfig + // Default SOURCE_CODEC_TYPE_INVALID + optional int32 codec_type = 2; + // Codec priroity, the higher the more preferred, -1 for disabled + // Default: CODEC_PRIORITY_DEFAULT + optional int32 codec_priority = 3; + // A bit field of supported sample rates as defined by various SAMPLE_RATE_* constants + // in BluetoothCodecConfig + // Default: empty and SAMPLE_RATE_NONE for individual item + optional int32 sample_rate = 4; + // A bit field of supported bits per sample as defined by various BITS_PER_SAMPLE_* constants + // in BluetoothCodecConfig + // Default: empty and BITS_PER_SAMPLE_NONE for individual item + optional int32 bits_per_sample = 5; + // A bit field of supported channel mode as defined by various CHANNEL_MODE_* constants in + // BluetoothCodecConfig + // Default: empty and CHANNEL_MODE_NONE for individual item + optional int32 channel_mode = 6; + // Codec specific values + // Default 0 + optional int64 codec_specific_1 = 7; + optional int64 codec_specific_2 = 8; + optional int64 codec_specific_3 = 9; + optional int64 codec_specific_4 = 10; +} + +/** + * Logs when A2DP failed to read from PCM source. + * This typically happens when audio HAL cannot supply A2DP with data fast enough for encoding. + * + * Logged from: + * system/bt + */ +message BluetoothA2dpAudioUnderrunReported { + // An identifier that can be used to match events for this device. + // Currently, this is a salted hash of the MAC address of this Bluetooth device. + // Salt: Randomly generated 256 bit value + // Hash algorithm: HMAC-SHA256 + // Size: 32 byte + // Default: null or empty if the device identifier is not known + optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; + // Encoding interval in nanoseconds + // Default: 0 + optional int64 encoding_interval_nanos = 2; + // Number of bytes of PCM data that could not be read from the source + // Default: 0 + optional int32 num_missing_pcm_bytes = 3; +} + +/** + * Logs when A2DP failed send encoded data to the remote device fast enough such that the transmit + * buffer queue is full and we have to drop data + * + * Logged from: + * system/bt + */ +message BluetoothA2dpAudioOverrunReported { + // An identifier that can be used to match events for this device. + // Currently, this is a salted hash of the MAC address of this Bluetooth device. + // Salt: Randomly generated 256 bit value + // Hash algorithm: HMAC-SHA256 + // Size: 32 byte + // Default: null or empty if the device identifier is not known + optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; + // Encoding interval in nanoseconds + // Default: 0 + optional int64 encoding_interval_nanos = 2; + // Number of buffers dropped in this event + // Each buffer is encoded in one encoding interval and consists of multiple encoded frames + // Default: 0 + optional int32 num_dropped_buffers = 3; + // Number of encoded buffers dropped in this event + // Default 0 + optional int32 num_dropped_encoded_frames = 4; + // Number of encoded bytes dropped in this event + // Default: 0 + optional int32 num_dropped_encoded_bytes = 5; +} + +/** + * Logs when we receive reports regarding a device's RSSI value + * + * Logged from: + * system/bt + */ +message BluetoothDeviceRssiReported { + // An identifier that can be used to match events for this device. + // Currently, this is a salted hash of the MAC address of this Bluetooth device. + // Salt: Randomly generated 256 bit value + // Hash algorithm: HMAC-SHA256 + // Size: 32 byte + // Default: null or empty if the device identifier is not known + optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; + // Connection handle of this connection if available + // Range: 0x0000 - 0x0EFF (12 bits) + // Default: 0xFFFF if the handle is unknown + optional int32 connection_handle = 2; + // HCI command status code if this is triggerred by hci_cmd + // Default: STATUS_UNKNOWN + optional android.bluetooth.hci.StatusEnum hci_status = 3; + // BR/EDR + // Range: -128 ≤ N ≤ 127 (signed integer) + // Units: dB + // LE: + // Range: -127 to 20, 127 (signed integer) + // Units: dBm + // Invalid when an out of range value is reported + optional int32 rssi = 4; +} + +/** + * Logs when we receive reports regarding how many consecutive failed contacts for a connection + * + * Logged from: + * system/bt + */ +message BluetoothDeviceFailedContactCounterReported { + // An identifier that can be used to match events for this device. + // Currently, this is a salted hash of the MAC address of this Bluetooth device. + // Salt: Randomly generated 256 bit value + // Hash algorithm: HMAC-SHA256 + // Size: 32 byte + // Default: null or empty if the device identifier is not known + optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; + // Connection handle of this connection if available + // Range: 0x0000 - 0x0EFF (12 bits) + // Default: 0xFFFF if the handle is unknown + optional int32 connection_handle = 2; + // HCI command status code if this is triggerred by hci_cmd + // Default: STATUS_UNKNOWN + optional android.bluetooth.hci.StatusEnum cmd_status = 3; + // Number of consecutive failed contacts for a connection corresponding to the Handle + // Range: uint16_t, 0-0xFFFF + // Default: 0xFFFFF + optional int32 failed_contact_counter = 4; +} + +/** + * Logs when we receive reports regarding the tranmit power level used for a specific connection + * + * Logged from: + * system/bt + */ +message BluetoothDeviceTxPowerLevelReported { + // An identifier that can be used to match events for this device. + // Currently, this is a salted hash of the MAC address of this Bluetooth device. + // Salt: Randomly generated 256 bit value + // Hash algorithm: HMAC-SHA256 + // Size: 32 byte + // Default: null or empty if the device identifier is not known + optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; + // Connection handle of this connection if available + // Range: 0x0000 - 0x0EFF (12 bits) + // Default: 0xFFFF if the handle is unknown + optional int32 connection_handle = 2; + // HCI command status code if this is triggered by hci_cmd + // Default: STATUS_UNKNOWN + optional android.bluetooth.hci.StatusEnum hci_status = 3; + // Range: -30 ≤ N ≤ 20 + // Units: dBm + // Invalid when an out of range value is reported + optional int32 transmit_power_level = 4; +} + +/** + * Logs when Bluetooth controller failed to reply with command status within a timeout period after + * receiving an HCI command from the host + * + * Logged from: system/bt + */ +message BluetoothHciTimeoutReported { + // HCI command associated with this event + // Default: CMD_UNKNOWN + optional android.bluetooth.hci.CommandEnum hci_command = 1; +} + +/** + * Logs when we receive Bluetooth Link Quality Report event from the controller + * See Android Bluetooth HCI specification for more details + * + * Note: all count and bytes field are counted since last event + * + * Logged from: system/bt + */ +message BluetoothQualityReportReported { + // Quality report ID + // Original type: uint8_t + // Default: BQR_ID_UNKNOWN + optional android.bluetooth.hci.BqrIdEnum quality_report_id = 1; + // Packet type of the connection + // Original type: uint8_t + // Default: BQR_PACKET_TYPE_UNKNOWN + optional android.bluetooth.hci.BqrPacketTypeEnum packet_types = 2; + // Connection handle of the connection + // Original type: uint16_t + optional int32 connection_handle = 3; + // Performing Role for the connection + // Original type: uint8_t + optional int32 connection_role = 4; + // Current Transmit Power Level for the connection. This value is the same as the controller's + // response to the HCI_Read_Transmit_Power_Level HCI command + // Original type: uint8_t + optional int32 tx_power_level = 5; + // Received Signal Strength Indication (RSSI) value for the connection. This value is an + // absolute receiver signal strength value + // Original type: int8_t + optional int32 rssi = 6; + // Signal-to-Noise Ratio (SNR) value for the connection. It is the average SNR of all the + // channels used by the link currently + // Original type: uint8_t + optional int32 snr = 7; + // Indicates the number of unused channels in AFH_channel_map + // Original type: uint8_t + optional int32 unused_afh_channel_count = 8; + // Indicates the number of the channels which are interfered and quality is bad but are still + // selected for AFH + // Original type: uint8_t + optional int32 afh_select_unideal_channel_count = 9; + // Current Link Supervision Timeout Setting + // Unit: N * 0.3125 ms (1 Bluetooth Clock) + // Original type: uint16_t + optional int32 lsto = 10; + // Piconet Clock for the specified Connection_Handle. This value is the same as the controller's + // response to HCI_Read_Clock HCI command with the parameter "Which_Clock" of + // 0x01 (Piconet Clock) + // Unit: N * 0.3125 ms (1 Bluetooth Clock) + // Original type: uint32_t + optional int64 connection_piconet_clock = 11; + // The count of retransmission + // Original type: uint32_t + optional int64 retransmission_count = 12; + // The count of no RX + // Original type: uint32_t + optional int64 no_rx_count = 13; + // The count of NAK (Negative Acknowledge) + // Original type: uint32_t + optional int64 nak_count = 14; + // Controller timestamp of last TX ACK + // Unit: N * 0.3125 ms (1 Bluetooth Clock) + // Original type: uint32_t + optional int64 last_tx_ack_timestamp = 15; + // The count of Flow-off (STOP) + // Original type: uint32_t + optional int64 flow_off_count = 16; + // Controller timestamp of last Flow-on (GO) + // Unit: N * 0.3125 ms (1 Bluetooth Clock) + // Original type: uint32_t + optional int64 last_flow_on_timestamp = 17; + // Buffer overflow count (how many bytes of TX data are dropped) since the last event + // Original type: uint32_t + optional int64 buffer_overflow_bytes = 18; + // Buffer underflow count (in byte) since last event + // Original type: uint32_t + optional int64 buffer_underflow_bytes = 19; +} + +/** + * Logs when a Bluetooth device's manufacturer information is learnt by the Bluetooth stack + * + * Notes: + * - Each event can be partially filled as we might learn different pieces of device + * information at different time + * - Multiple device info events can be combined to give more complete picture + * - When multiple device info events tries to describe the same information, the + * later one wins + * + * Logged from: + * packages/apps/Bluetooth + */ +message BluetoothManufacturerInfoReported { + // An identifier that can be used to match events for this device. + // Currently, this is a salted hash of the MAC address of this Bluetooth device. + // Salt: Randomly generated 256 bit value + // Hash algorithm: HMAC-SHA256 + // Size: 32 byte + // Default: null or empty if the device identifier is not known + optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; + // Where is this device info obtained from + optional android.bluetooth.DeviceInfoSrcEnum source_type = 2; + // Name of the data source + // For EXTERNAL: package name of the data source + // For INTERNAL: null for general case, component name otherwise + optional string source_name = 3; + // Name of the manufacturer of this device + optional string manufacturer = 4; + // Model of this device + optional string model = 5; + // Hardware version of this device + optional string hardware_version = 6; + // Software version of this device + optional string software_version = 7; +} + +/** + * Logs when we receive Bluetooth Read Remote Version Information Complete Event from the remote + * device, as documented by the Bluetooth Core HCI specification + * Reference: https://www.bluetooth.com/specifications/bluetooth-core-specification + * Vol 2, Part E, Page 1118 + * + * Logged from: + * system/bt + */ +message BluetoothRemoteVersionInfoReported { + // Connection handle of the connection + // Original type: uint16_t + optional int32 connection_handle = 1; + // HCI command status code + // Default: STATUS_UNKNOWN + optional android.bluetooth.hci.StatusEnum hci_status = 2; + // 1 byte Version of current LMP in the remote controller + optional int32 lmp_version = 3; + // 2 bytes LMP manufacturer code of the remote controller + // https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers + optional int32 lmp_manufacturer_code = 4; + // 4 bytes subversion of the LMP in the remote controller + optional int32 lmp_subversion = 5; +} + +/** + * Logs when certain Bluetooth SDP attributes are discovered + * Constant definitions are from: + * https://www.bluetooth.com/specifications/assigned-numbers/service-discovery + * + * Current logged attributes: + * - BluetoothProfileDescriptorList + * - Supported Features Bitmask + * + * Logged from: + * system/bt + */ +message BluetoothSdpAttributeReported { + // An identifier that can be used to match events for this device. + // Currently, this is a salted hash of the MAC address of this Bluetooth device. + // Salt: Randomly generated 256 bit value + // Hash algorithm: HMAC-SHA256 + optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; + // Short form UUIDs used to identify Bluetooth protocols, profiles, and service classes + // Original type: uint16_t + optional int32 protocol_uuid = 2; + // Short form UUIDs used to identify Bluetooth SDP attribute types + // Original type: uint16_t + optional int32 attribute_id = 3; + // Attribute value for the particular attribute + optional bytes attribute_value = 4 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Logs when bond state of a Bluetooth device changes + * + * Logged from: + * frameworks/base/core/java/android/bluetooth/BluetoothDevice.java + * packages/apps/Bluetooth/src/com/android/bluetooth/btservice/BondStateMachine.java + */ +message BluetoothBondStateChanged { + // An identifier that can be used to match events for this device. + // Currently, this is a salted hash of the MAC address of this Bluetooth device. + // Salt: Randomly generated 256 bit value + // Hash algorithm: HMAC-SHA256 + // Size: 32 byte + // Default: null or empty if the device identifier is not known + optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; + // Preferred transport type to remote dual mode device + // Default: TRANSPORT_AUTO means no preference + optional android.bluetooth.TransportTypeEnum transport = 2; + // The type of this Bluetooth device (Classic, LE, or Dual mode) + // Default: UNKNOWN + optional android.bluetooth.DeviceTypeEnum type = 3; + // Current bond state (NONE, BONDING, BONDED) + // Default: BOND_STATE_UNKNOWN + optional android.bluetooth.BondStateEnum bond_state = 4; + // Bonding sub state + // Default: BOND_SUB_STATE_UNKNOWN + optional android.bluetooth.BondSubStateEnum bonding_sub_state = 5; + // Unbond Reason + // Default: UNBOND_REASON_UNKNOWN + optional android.bluetooth.UnbondReasonEnum unbond_reason = 6; +} + +/** + * Logs there is an event related Bluetooth classic pairing + * + * Logged from: + * system/bt + */ +message BluetoothClassicPairingEventReported { + // An identifier that can be used to match events for this device. + // Currently, this is a salted hash of the MAC address of this Bluetooth device. + // Salt: Randomly generated 256 bit value + // Hash algorithm: HMAC-SHA256 + // Size: 32 byte + // Default: null or empty if the device identifier is not known + optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; + // Connection handle of this connection if available + // Range: 0x0000 - 0x0EFF (12 bits) + // Default: 0xFFFF if the handle is unknown + optional int32 connection_handle = 2; + // HCI command associated with this event + // Default: CMD_UNKNOWN + optional android.bluetooth.hci.CommandEnum hci_cmd = 3; + // HCI event associated with this event + // Default: EVT_UNKNOWN + optional android.bluetooth.hci.EventEnum hci_event = 4; + // HCI command status code if this is triggerred by hci_cmd + // Default: STATUS_UNKNOWN + optional android.bluetooth.hci.StatusEnum cmd_status = 5; + // HCI reason code associated with this event + // Default: STATUS_UNKNOWN + optional android.bluetooth.hci.StatusEnum reason_code = 6; +} + +/** + * Logs when there is an event related to Bluetooth Security Manager Protocol (SMP) + * + * Logged from: + * system/bt + */ +message BluetoothSmpPairingEventReported { + // An identifier that can be used to match events for this device. + // Currently, this is a salted hash of the MAC address of this Bluetooth device. + // Salt: Randomly generated 256 bit value + // Hash algorithm: HMAC-SHA256 + // Size: 32 byte + // Default: null or empty if the device identifier is not known + optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; + // SMP command sent or received over L2CAP + // Default: CMD_UNKNOWN + optional android.bluetooth.smp.CommandEnum smp_command = 2; + // Whether this command is sent or received + // Default: DIRECTION_UNKNOWN + optional android.bluetooth.DirectionEnum direction = 3; + // SMP failure reason code + // Default: PAIRING_FAIL_REASON_DEFAULT + optional android.bluetooth.smp.PairingFailReasonEnum smp_fail_reason = 4; +} /** * Logs when something is plugged into or removed from the USB-C connector. @@ -4699,3 +5251,24 @@ message BubbleUIChanged { optional float normalized_x_position = 7; optional float normalized_y_position = 8; } + +/** + * Logs that a constraint for a scheduled job has changed. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/job/controllers/JobStatus.java + */ +message ScheduledJobConstraintChanged { + repeated AttributionNode attribution_node = 1; + + // Name of the job. + optional string job_name = 2; + + optional com.android.server.job.ConstraintEnum constraint = 3; + + enum State { + UNSATISFIED = 0; + SATISFIED = 1; + } + optional State state = 4; +} diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 4122d8440959..5645461cc3ab 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -524,6 +524,14 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn multiIntervals.resize(mFieldMatchers.size()); } + // We only use anomaly detection under certain cases. + // N.B.: The anomaly detection cases were modified in order to fix an issue with value metrics + // containing multiple values. We tried to retain all previous behaviour, but we are unsure the + // previous behaviour was correct. At the time of the fix, anomaly detection had no owner. + // Whoever next works on it should look into the cases where it is triggered in this function. + // Discussion here: http://ag/6124370. + bool useAnomalyDetection = true; + for (int i = 0; i < (int)mFieldMatchers.size(); i++) { const Matcher& matcher = mFieldMatchers[i]; Interval& interval = multiIntervals[i]; @@ -546,7 +554,11 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn // no base. just update base and return. interval.base = value; interval.hasBase = true; - return; + // If we're missing a base, do not use anomaly detection on incomplete data + useAnomalyDetection = false; + // Continue (instead of return) here in order to set interval.base and + // interval.hasBase for other intervals + continue; } } Value diff; @@ -560,7 +572,9 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn VLOG("Unexpected decreasing value"); StatsdStats::getInstance().notePullDataError(mPullTagId); interval.base = value; - return; + // If we've got bad data, do not use anomaly detection + useAnomalyDetection = false; + continue; } break; case ValueMetric::DECREASING: @@ -572,7 +586,9 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn VLOG("Unexpected increasing value"); StatsdStats::getInstance().notePullDataError(mPullTagId); interval.base = value; - return; + // If we've got bad data, do not use anomaly detection + useAnomalyDetection = false; + continue; } break; case ValueMetric::ANY: @@ -608,14 +624,18 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn interval.sampleSize += 1; } - // TODO: propgate proper values down stream when anomaly support doubles - long wholeBucketVal = multiIntervals[0].value.long_value; - auto prev = mCurrentFullBucket.find(eventKey); - if (prev != mCurrentFullBucket.end()) { - wholeBucketVal += prev->second; - } - for (auto& tracker : mAnomalyTrackers) { - tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, wholeBucketVal); + // Only trigger the tracker if all intervals are correct + if (useAnomalyDetection) { + // TODO: propgate proper values down stream when anomaly support doubles + long wholeBucketVal = multiIntervals[0].value.long_value; + auto prev = mCurrentFullBucket.find(eventKey); + if (prev != mCurrentFullBucket.end()) { + wholeBucketVal += prev->second; + } + for (auto& tracker : mAnomalyTrackers) { + tracker->detectAndDeclareAnomaly( + eventTimeNs, mCurrentBucketNum, eventKey, wholeBucketVal); + } } } diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 69eb0afaf7c4..a8dfc5ba0e5d 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -212,6 +212,7 @@ private: FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket); FRIEND_TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime); FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput); + FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue); FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase); FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures); FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey); diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index 9cfe3436ada7..c0648ee70032 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -1508,6 +1508,113 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { EXPECT_EQ(5, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); } +TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { + ValueMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); + metric.mutable_value_field()->add_child()->set_field(3); + metric.set_aggregation_type(ValueMetric::MIN); + metric.set_use_diff(true); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + + ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, + eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, + pullerManager); + + shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); + event1->write(1); + event1->write(10); + event1->write(20); + event1->init(); + shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 15); + event2->write(1); + event2->write(15); + event2->write(22); + event2->init(); + valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1); + // has one slice + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + ValueMetricProducer::Interval curInterval0 = + valueProducer.mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::Interval curInterval1 = + valueProducer.mCurrentSlicedBucket.begin()->second[1]; + EXPECT_EQ(true, curInterval0.hasBase); + EXPECT_EQ(10, curInterval0.base.long_value); + EXPECT_EQ(false, curInterval0.hasValue); + EXPECT_EQ(true, curInterval1.hasBase); + EXPECT_EQ(20, curInterval1.base.long_value); + EXPECT_EQ(false, curInterval1.hasValue); + + valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2); + + // has one slice + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + curInterval0 = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + curInterval1 = valueProducer.mCurrentSlicedBucket.begin()->second[1]; + EXPECT_EQ(true, curInterval0.hasValue); + EXPECT_EQ(5, curInterval0.value.long_value); + EXPECT_EQ(true, curInterval1.hasValue); + EXPECT_EQ(2, curInterval1.value.long_value); + + // no change in first value field + shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10); + event3->write(1); + event3->write(15); + event3->write(25); + event3->init(); + valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3); + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + curInterval0 = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + curInterval1 = valueProducer.mCurrentSlicedBucket.begin()->second[1]; + EXPECT_EQ(true, curInterval0.hasBase); + EXPECT_EQ(15, curInterval0.base.long_value); + EXPECT_EQ(true, curInterval0.hasValue); + EXPECT_EQ(true, curInterval1.hasBase); + EXPECT_EQ(25, curInterval1.base.long_value); + EXPECT_EQ(true, curInterval1.hasValue); + + shared_ptr<LogEvent> event4 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 15); + event4->write(1); + event4->write(15); + event4->write(29); + event4->init(); + valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4); + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + curInterval0 = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + curInterval1 = valueProducer.mCurrentSlicedBucket.begin()->second[1]; + EXPECT_EQ(true, curInterval0.hasBase); + EXPECT_EQ(15, curInterval0.base.long_value); + EXPECT_EQ(true, curInterval0.hasValue); + EXPECT_EQ(true, curInterval1.hasBase); + EXPECT_EQ(29, curInterval1.base.long_value); + EXPECT_EQ(true, curInterval1.hasValue); + + valueProducer.flushIfNeededLocked(bucket3StartTimeNs); + + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size()); + EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second[0].values.size()); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second[1].values.size()); + + EXPECT_EQ(5, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].valueIndex[0]); + EXPECT_EQ(2, valueProducer.mPastBuckets.begin()->second[0].values[1].long_value); + EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[0].valueIndex[1]); + + EXPECT_EQ(3, valueProducer.mPastBuckets.begin()->second[1].values[0].long_value); + EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[1].valueIndex[0]); +} + /* * Tests zero default base. */ diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 92f47e767d2e..e77e212cb46e 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1808,6 +1808,29 @@ public class Activity extends ContextThemeWrapper mCalled = true; } + /** + * Called when activity gets or looses the top resumed position in the system. + * + * <p>Starting with {@link android.os.Build.VERSION_CODES#Q} multiple activities can be resumed + * at the same time in multi-window and multi-display modes. This callback should be used + * instead of {@link #onResume()} as an indication that the activity can try to open + * exclusive-access devices like camera.</p> + * + * <p>It will always be delivered after the activity was resumed and before it is paused. In + * some cases it might be skipped and activity can go straight from {@link #onResume()} to + * {@link #onPause()} without receiving the top resumed state.</p> + * + * @param isTopResumedActivity {@code true} if it's the topmost resumed activity in the system, + * {@code false} otherwise. A call with this as {@code true} will + * always be followed by another one with {@code false}. + * + * @see #onResume() + * @see #onPause() + * @see #onWindowFocusChanged(boolean) + */ + public void onTopResumedActivityChanged(boolean isTopResumedActivity) { + } + void setVoiceInteractor(IVoiceInteractor voiceInteractor) { if (mVoiceInteractor != null) { for (Request activeRequest: mVoiceInteractor.getActiveRequests()) { diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index ee3d27c7dac8..03a09ee04ea1 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -449,6 +449,14 @@ public final class ActivityThread extends ClientTransactionHandler { ViewRootImpl.ActivityConfigCallback configCallback; ActivityClientRecord nextIdle; + // Indicates whether this activity is currently the topmost resumed one in the system. + // This holds the last reported value from server. + boolean isTopResumedActivity; + // This holds the value last sent to the activity. This is needed, because an update from + // server may come at random time, but we always need to report changes between ON_RESUME + // and ON_PAUSE to the app. + boolean lastReportedTopResumedState; + ProfilerInfo profilerInfo; @UnsupportedAppUsage @@ -3295,16 +3303,14 @@ public final class ActivityThread extends ClientTransactionHandler { final boolean resumed = !r.paused; if (resumed) { r.activity.mTemporaryPause = true; - mInstrumentation.callActivityOnPause(r.activity); + performPauseActivityIfNeeded(r, "performNewIntents"); } checkAndBlockForNetworkAccess(); deliverNewIntents(r, intents); if (resumed) { - r.activity.performResume(false, "performNewIntents"); + performResumeActivity(token, false, "performNewIntents"); r.activity.mTemporaryPause = false; - } - - if (r.paused && andPause) { + } else if (andPause) { // In this case the activity was in the paused state when we delivered the intent, // to guarantee onResume gets called after onNewIntent we temporarily resume the // activity and pause again as the caller wanted. @@ -3957,6 +3963,8 @@ public final class ActivityThread extends ClientTransactionHandler { r.state = null; r.persistentState = null; r.setState(ON_RESUME); + + reportTopResumedActivityChanged(r, r.isTopResumedActivity); } catch (Exception e) { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException("Unable to resume activity " @@ -4111,6 +4119,45 @@ public final class ActivityThread extends ClientTransactionHandler { Looper.myQueue().addIdleHandler(new Idler()); } + + @Override + public void handleTopResumedActivityChanged(IBinder token, boolean onTop, String reason) { + ActivityClientRecord r = mActivities.get(token); + if (r == null || r.activity == null) { + Slog.w(TAG, "Not found target activity to report position change for token: " + token); + return; + } + + if (DEBUG_ORDER) { + Slog.d(TAG, "Received position change to top: " + onTop + " for activity: " + r); + } + + if (r.isTopResumedActivity == onTop) { + throw new IllegalStateException("Activity top position already set to onTop=" + onTop); + } + + r.isTopResumedActivity = onTop; + + if (r.getLifecycleState() == ON_RESUME) { + reportTopResumedActivityChanged(r, onTop); + } else { + if (DEBUG_ORDER) { + Slog.d(TAG, "Won't deliver top position change in state=" + r.getLifecycleState()); + } + } + } + + /** + * Call {@link Activity#onTopResumedActivityChanged(boolean)} if its top resumed state changed + * since the last report. + */ + private void reportTopResumedActivityChanged(ActivityClientRecord r, boolean onTop) { + if (r.lastReportedTopResumedState != onTop) { + r.lastReportedTopResumedState = onTop; + r.activity.onTopResumedActivityChanged(onTop); + } + } + @Override public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving, int configChanges, PendingTransactionActions pendingActions, String reason) { @@ -4202,6 +4249,10 @@ public final class ActivityThread extends ClientTransactionHandler { return; } + // Always reporting top resumed position loss when pausing an activity. If necessary, it + // will be restored in performResumeActivity(). + reportTopResumedActivityChanged(r, false /* onTop */); + try { r.activity.mCalled = false; mInstrumentation.callActivityOnPause(r.activity); diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 364d3c9c8e5d..d2630d531436 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -555,9 +555,11 @@ public class AppOpsManager { public static final int OP_WRITE_MEDIA_IMAGES = 86; /** @hide Has a legacy (non-isolated) view of storage. */ public static final int OP_LEGACY_STORAGE = 87; + /** @hide Accessing accessibility features */ + public static final int OP_ACCESS_ACCESSIBILITY = 88; /** @hide */ @UnsupportedAppUsage - public static final int _NUM_OP = 88; + public static final int _NUM_OP = 89; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -826,6 +828,8 @@ public class AppOpsManager { public static final String OPSTR_WRITE_MEDIA_IMAGES = "android:write_media_images"; /** @hide Has a legacy (non-isolated) view of storage. */ public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage"; + /** @hide Interact with accessibility. */ + public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility"; // Warning: If an permission is added here it also has to be added to // com.android.packageinstaller.permission.utils.EventLogger @@ -985,6 +989,7 @@ public class AppOpsManager { OP_READ_MEDIA_IMAGES, // READ_MEDIA_IMAGES OP_WRITE_MEDIA_IMAGES, // WRITE_MEDIA_IMAGES OP_LEGACY_STORAGE, // LEGACY_STORAGE + OP_ACCESS_ACCESSIBILITY, // ACCESS_ACCESSIBILITY }; /** @@ -1079,6 +1084,7 @@ public class AppOpsManager { OPSTR_READ_MEDIA_IMAGES, OPSTR_WRITE_MEDIA_IMAGES, OPSTR_LEGACY_STORAGE, + OPSTR_ACCESS_ACCESSIBILITY, }; /** @@ -1174,6 +1180,7 @@ public class AppOpsManager { "READ_MEDIA_IMAGES", "WRITE_MEDIA_IMAGES", "LEGACY_STORAGE", + "ACCESS_ACCESSIBILITY", }; /** @@ -1270,6 +1277,7 @@ public class AppOpsManager { Manifest.permission.READ_MEDIA_IMAGES, null, // no permission for OP_WRITE_MEDIA_IMAGES null, // no permission for OP_LEGACY_STORAGE + null, // no permission for OP_ACCESS_ACCESSIBILITY }; /** @@ -1366,6 +1374,7 @@ public class AppOpsManager { null, // READ_MEDIA_IMAGES null, // WRITE_MEDIA_IMAGES null, // LEGACY_STORAGE + null, // ACCESS_ACCESSIBILITY }; /** @@ -1461,6 +1470,7 @@ public class AppOpsManager { false, // READ_MEDIA_IMAGES false, // WRITE_MEDIA_IMAGES false, // LEGACY_STORAGE + false, // ACCESS_ACCESSIBILITY }; /** @@ -1555,6 +1565,7 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, // READ_MEDIA_IMAGES AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_IMAGES AppOpsManager.MODE_DEFAULT, // LEGACY_STORAGE + AppOpsManager.MODE_ALLOWED, // ACCESS_ACCESSIBILITY }; /** @@ -1653,6 +1664,7 @@ public class AppOpsManager { false, // READ_MEDIA_IMAGES false, // WRITE_MEDIA_IMAGES false, // LEGACY_STORAGE + false, // ACCESS_ACCESSIBILITY }; /** diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java index b5560331e7aa..da45054cbe51 100644 --- a/core/java/android/app/AppOpsManagerInternal.java +++ b/core/java/android/app/AppOpsManagerInternal.java @@ -78,7 +78,7 @@ public abstract class AppOpsManagerInternal { /** * Sets the app-ops mode for a certain app-op and uid. * - * <p>Similar as {@link AppOpsManager#setMode} but does not require the package manager to be + * <p>Similar as {@link AppOpsManager#setUidMode} but does not require the package manager to be * working. Hence this can be used very early during boot. * * <p>Only for internal callers. Does <u>not</u> verify that package name belongs to uid. @@ -88,4 +88,12 @@ public abstract class AppOpsManagerInternal { * @param mode The new mode to set. */ public abstract void setUidMode(int code, int uid, int mode); + + /** + * Set all {@link #setMode (package) modes} for this uid to the default value. + * + * @param code The app-op + * @param uid The uid + */ + public abstract void setAllPkgModesToDefault(int code, int uid); } diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java index 07dbb6bee9fd..70badfae4a20 100644 --- a/core/java/android/app/ClientTransactionHandler.java +++ b/core/java/android/app/ClientTransactionHandler.java @@ -105,6 +105,16 @@ public abstract class ClientTransactionHandler { boolean isForward, String reason); /** + * Notify the activity about top resumed state change. + * @param token Target activity token. + * @param isTopResumedActivity Current state of the activity, {@code true} if it's the + * topmost resumed activity in the system, {@code false} otherwise. + * @param reason Reason for performing this operation. + */ + public abstract void handleTopResumedActivityChanged(IBinder token, + boolean isTopResumedActivity, String reason); + + /** * Stop the activity. * @param token Target activity token. * @param show Flag indicating whether activity is still shown. diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java index 853b45e0a80d..acc70944abf0 100644 --- a/core/java/android/app/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -1067,7 +1067,12 @@ public class DownloadManager { * COLUMN_* constants. */ public Cursor query(Query query) { - Cursor underlyingCursor = query.runQuery(mResolver, UNDERLYING_COLUMNS, mBaseUri); + return query(query, UNDERLYING_COLUMNS); + } + + /** @hide */ + public Cursor query(Query query, String[] projection) { + Cursor underlyingCursor = query.runQuery(mResolver, projection, mBaseUri); if (underlyingCursor == null) { return null; } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index fb65da14a087..412d7f45986c 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -277,6 +277,7 @@ interface IActivityManager { void unstableProviderDied(in IBinder connection); boolean isIntentSenderAnActivity(in IIntentSender sender); boolean isIntentSenderAForegroundService(in IIntentSender sender); + boolean isIntentSenderABroadcast(in IIntentSender sender); int startActivityAsUser(in IApplicationThread caller, in String callingPackage, in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode, int flags, in ProfilerInfo profilerInfo, diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 199c1338c50c..8953940952a8 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -68,6 +68,7 @@ interface INotificationManager void setBubblesAllowed(String pkg, int uid, boolean allowed); boolean areBubblesAllowed(String pkg); boolean areBubblesAllowedForPackage(String pkg, int uid); + boolean hasUserApprovedBubblesForPackage(String pkg, int uid); void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList); void createNotificationChannels(String pkg, in ParceledListSlice channelsList); @@ -94,6 +95,7 @@ interface INotificationManager boolean areChannelsBypassingDnd(); int getAppsBypassingDndCount(int uid); ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int userId); + boolean isPackagePaused(String pkg); // TODO: Remove this when callers have been migrated to the equivalent // INotificationListener method. diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index c4b4b4070ce7..621f134dc68e 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1094,6 +1094,22 @@ public class NotificationManager { } /** + * Returns whether notifications from this package are temporarily hidden. This + * could be done because the package was marked as distracting to the user via + * {@code PackageManager#setDistractingPackageRestrictions(String[], int)} or because the + * package is {@code PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle, + * PersistableBundle, SuspendDialogInfo) suspended}. + */ + public boolean areNotificationsPaused() { + INotificationManager service = getService(); + try { + return service.isPackagePaused(mContext.getPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Checks the ability to modify notification do not disturb policy for the calling package. * * <p> diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 75d95b2c2508..55014ebf1fcb 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -1113,6 +1113,19 @@ public final class PendingIntent implements Parcelable { /** * @hide + * Check whether this PendingIntent will launch an Activity. + */ + public boolean isBroadcast() { + try { + return ActivityManager.getService() + .isIntentSenderABroadcast(mTarget); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide * Return the Intent of this PendingIntent. */ @UnsupportedAppUsage diff --git a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java new file mode 100644 index 000000000000..4064a02e54a8 --- /dev/null +++ b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java @@ -0,0 +1,112 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app.servertransaction; + +import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; + +import android.app.ClientTransactionHandler; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Trace; + +/** + * Top resumed activity changed callback. + * @hide + */ +public class TopResumedActivityChangeItem extends ClientTransactionItem { + + private boolean mOnTop; + + @Override + public void execute(ClientTransactionHandler client, IBinder token, + PendingTransactionActions pendingActions) { + Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "topResumedActivityChangeItem"); + client.handleTopResumedActivityChanged(token, mOnTop, "topResumedActivityChangeItem"); + Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + } + + + // ObjectPoolItem implementation + + private TopResumedActivityChangeItem() {} + + /** Obtain an instance initialized with provided params. */ + public static TopResumedActivityChangeItem obtain(boolean onTop) { + TopResumedActivityChangeItem instance = + ObjectPool.obtain(TopResumedActivityChangeItem.class); + if (instance == null) { + instance = new TopResumedActivityChangeItem(); + } + instance.mOnTop = onTop; + + return instance; + } + + @Override + public void recycle() { + mOnTop = false; + ObjectPool.recycle(this); + } + + + // Parcelable implementation + + /** Write to Parcel. */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeBoolean(mOnTop); + } + + /** Read from Parcel. */ + private TopResumedActivityChangeItem(Parcel in) { + mOnTop = in.readBoolean(); + } + + public static final Creator<TopResumedActivityChangeItem> CREATOR = + new Creator<TopResumedActivityChangeItem>() { + public TopResumedActivityChangeItem createFromParcel(Parcel in) { + return new TopResumedActivityChangeItem(in); + } + + public TopResumedActivityChangeItem[] newArray(int size) { + return new TopResumedActivityChangeItem[size]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final TopResumedActivityChangeItem other = (TopResumedActivityChangeItem) o; + return mOnTop == other.mOnTop; + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + (mOnTop ? 1 : 0); + return result; + } + + @Override + public String toString() { + return "TopResumedActivityChangeItem{onTop=" + mOnTop + "}"; + } +} diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java index 3d3c03ae3daa..43ce521fa9f3 100644 --- a/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -287,19 +287,14 @@ public abstract class UsageStatsManagerInternal { /** A class which is used to share the usage limit data for an app or a group of apps. */ public static class AppUsageLimitData { - private final boolean mGroupLimit; private final long mTotalUsageLimit; private final long mUsageRemaining; - public AppUsageLimitData(boolean groupLimit, long totalUsageLimit, long usageRemaining) { - this.mGroupLimit = groupLimit; + public AppUsageLimitData(long totalUsageLimit, long usageRemaining) { this.mTotalUsageLimit = totalUsageLimit; this.mUsageRemaining = usageRemaining; } - public boolean isGroupLimit() { - return mGroupLimit; - } public long getTotalUsageLimit() { return mTotalUsageLimit; } diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index b7366f1bbafc..e897b917fcc2 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -18,7 +18,6 @@ package android.content.pm; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; @@ -77,18 +76,6 @@ public class CrossProfileApps { } /** - * @deprecated use {@link #startActivity(ComponentName, UserHandle)} instead. - * - * @removed - * @hide - */ - @Deprecated - @UnsupportedAppUsage - public void startAnyActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) { - startActivity(component, targetUser); - } - - /** * Starts the specified activity of the caller package in the specified profile. Unlike * {@link #startMainActivity}, this can start any activity of the caller package, not just * the main activity. diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 89630e15972e..36bb5d49666d 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -1658,35 +1658,23 @@ public class LauncherApps { * A class that encapsulates information about the usage limit set for an app or * a group of apps. * - * <p>The launcher can query specifics about the usage limit such as if it is a group limit, - * how much usage time the limit has, and how much of the total usage time is remaining - * via the APIs available in this class. + * <p>The launcher can query specifics about the usage limit such as how much usage time + * the limit has and how much of the total usage time is remaining via the APIs available + * in this class. * * @see #getAppUsageLimit(String, UserHandle) */ public static final class AppUsageLimit implements Parcelable { - private final boolean mGroupLimit; private final long mTotalUsageLimit; private final long mUsageRemaining; /** @hide */ - public AppUsageLimit(boolean groupLimit, long totalUsageLimit, long usageRemaining) { - this.mGroupLimit = groupLimit; + public AppUsageLimit(long totalUsageLimit, long usageRemaining) { this.mTotalUsageLimit = totalUsageLimit; this.mUsageRemaining = usageRemaining; } /** - * Returns whether this limit refers to a group of apps. - * - * @return {@code TRUE} if the limit refers to a group of apps, {@code FALSE} otherwise. - * @hide - */ - public boolean isGroupLimit() { - return mGroupLimit; - } - - /** * Returns the total usage limit in milliseconds set for an app or a group of apps. * * @return the total usage limit in milliseconds @@ -1706,7 +1694,6 @@ public class LauncherApps { } private AppUsageLimit(Parcel source) { - mGroupLimit = source.readBoolean(); mTotalUsageLimit = source.readLong(); mUsageRemaining = source.readLong(); } @@ -1730,7 +1717,6 @@ public class LauncherApps { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeBoolean(mGroupLimit); dest.writeLong(mTotalUsageLimit); dest.writeLong(mUsageRemaining); } diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java index d2c0e7be5900..5d4928cd4ab3 100644 --- a/core/java/android/hardware/HardwareBuffer.java +++ b/core/java/android/hardware/HardwareBuffer.java @@ -182,6 +182,38 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { } /** + * Queries whether the given buffer description is supported by the system. If this returns + * true, then the allocation may succeed until resource exhaustion occurs. If this returns + * false then this combination will never succeed. + * + * @param width The width in pixels of the buffer + * @param height The height in pixels of the buffer + * @param format The @Format of each pixel + * @param layers The number of layers in the buffer + * @param usage The @Usage flags describing how the buffer will be used + * @return True if the combination is supported, false otherwise. + */ + public static boolean isSupported(int width, int height, @Format int format, int layers, + @Usage long usage) { + if (!HardwareBuffer.isSupportedFormat(format)) { + throw new IllegalArgumentException("Invalid pixel format " + format); + } + if (width <= 0) { + throw new IllegalArgumentException("Invalid width " + width); + } + if (height <= 0) { + throw new IllegalArgumentException("Invalid height " + height); + } + if (layers <= 0) { + throw new IllegalArgumentException("Invalid layer count " + layers); + } + if (format == BLOB && height != 1) { + throw new IllegalArgumentException("Height must be 1 when using the BLOB format"); + } + return nIsSupported(width, height, format, layers, usage); + } + + /** * Private use only. See {@link #create(int, int, int, int, long)}. May also be * called from JNI using an already allocated native <code>HardwareBuffer</code>. */ @@ -386,4 +418,6 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { private static native int nGetLayers(long nativeObject); @FastNative private static native long nGetUsage(long nativeObject); + private static native boolean nIsSupported(int width, int height, int format, int layers, + long usage); } diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java index 209afb88ccd3..125dabef779c 100644 --- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java @@ -295,12 +295,22 @@ public interface BiometricFaceConstants { public static final int FACE_ACQUIRED_FACE_OBSCURED = 19; /** + * This message represents the earliest message sent at the beginning of the authentication + * pipeline. It is expected to be used to measure latency. For example, in a camera-based + * authentication system it's expected to be sent prior to camera initialization. Note this + * should be sent whenever authentication is restarted (see IBiometricsFace#userActivity). + * The framework will measure latency based on the time between the last START message and the + * onAuthenticated callback. + */ + public static final int FACE_ACQUIRED_START = 20; + + /** * Hardware vendors may extend this list if there are conditions that do not fall under one of * the above categories. Vendors are responsible for providing error strings for these errors. * * @hide */ - public static final int FACE_ACQUIRED_VENDOR = 20; + public static final int FACE_ACQUIRED_VENDOR = 21; /** * @hide diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 97583089736e..425b9561dbc1 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -3721,6 +3721,59 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<android.hardware.camera2.params.RecommendedStreamConfiguration[]>("android.depth.availableRecommendedDepthStreamConfigurations", android.hardware.camera2.params.RecommendedStreamConfiguration[].class); /** + * <p>The available dynamic depth dataspace stream + * configurations that this camera device supports + * (i.e. format, width, height, output/input stream).</p> + * <p>These are output stream configurations for use with + * dataSpace DYNAMIC_DEPTH. The configurations are + * listed as <code>(format, width, height, input?)</code> tuples.</p> + * <p>Only devices that support depth output for at least + * the HAL_PIXEL_FORMAT_Y16 dense depth map along with + * HAL_PIXEL_FORMAT_BLOB with the same size or size with + * the same aspect ratio can have dynamic depth dataspace + * stream configuration. {@link CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE android.depth.depthIsExclusive} also + * needs to be set to FALSE.</p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * + * @see CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE + * @hide + */ + public static final Key<android.hardware.camera2.params.StreamConfiguration[]> DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS = + new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.depth.availableDynamicDepthStreamConfigurations", android.hardware.camera2.params.StreamConfiguration[].class); + + /** + * <p>This lists the minimum frame duration for each + * format/size combination for dynamic depth output streams.</p> + * <p>This should correspond to the frame duration when only that + * stream is active, with all processing (typically in android.*.mode) + * set to either OFF or FAST.</p> + * <p>When multiple streams are used in a request, the minimum frame + * duration will be max(individual stream min durations).</p> + * <p>The minimum frame duration of a stream (of a particular format, size) + * is the same regardless of whether the stream is input or output.</p> + * <p><b>Units</b>: (format, width, height, ns) x n</p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * @hide + */ + public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS = + new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDynamicDepthMinFrameDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class); + + /** + * <p>This lists the maximum stall duration for each + * output format/size combination for dynamic depth streams.</p> + * <p>A stall duration is how much extra time would get added + * to the normal minimum frame duration for a repeating request + * that has streams with non-zero stall.</p> + * <p>All dynamic depth output streams may have a nonzero stall + * duration.</p> + * <p><b>Units</b>: (format, width, height, ns) x n</p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * @hide + */ + public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS = + new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDynamicDepthStallDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class); + + /** * <p>String containing the ids of the underlying physical cameras.</p> * <p>For a logical camera, this is concatenation of all underlying physical camera IDs. * The null terminator for physical camera ID must be preserved so that the whole string diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index c527ab4c6730..7877a4d51313 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -1119,6 +1119,8 @@ public class CameraMetadataNative implements Parcelable { continue; } + // Dynamic depth streams involve alot of SW processing and currently cannot be + // recommended. StreamConfigurationMap map = null; switch (i) { case RecommendedStreamConfigurationMap.USECASE_PREVIEW: @@ -1127,28 +1129,44 @@ public class CameraMetadataNative implements Parcelable { map = new StreamConfigurationMap(scData.streamConfigurationArray, scData.minDurationArray, scData.stallDurationArray, /*depthconfiguration*/ null, /*depthminduration*/ null, - /*depthstallduration*/ null, /*highspeedvideoconfigurations*/ null, + /*depthstallduration*/ null, + /*dynamicDepthConfigurations*/ null, + /*dynamicDepthMinFrameDurations*/ null, + /*dynamicDepthStallDurations*/ null, + /*highspeedvideoconfigurations*/ null, /*inputoutputformatsmap*/ null, listHighResolution, supportsPrivate[i]); break; case RecommendedStreamConfigurationMap.USECASE_RECORD: map = new StreamConfigurationMap(scData.streamConfigurationArray, scData.minDurationArray, scData.stallDurationArray, /*depthconfiguration*/ null, /*depthminduration*/ null, - /*depthstallduration*/ null, highSpeedVideoConfigurations, + /*depthstallduration*/ null, + /*dynamicDepthConfigurations*/ null, + /*dynamicDepthMinFrameDurations*/ null, + /*dynamicDepthStallDurations*/ null, + highSpeedVideoConfigurations, /*inputoutputformatsmap*/ null, listHighResolution, supportsPrivate[i]); break; case RecommendedStreamConfigurationMap.USECASE_ZSL: map = new StreamConfigurationMap(scData.streamConfigurationArray, scData.minDurationArray, scData.stallDurationArray, depthScData.streamConfigurationArray, depthScData.minDurationArray, - depthScData.stallDurationArray, /*highSpeedVideoConfigurations*/ null, + depthScData.stallDurationArray, + /*dynamicDepthConfigurations*/ null, + /*dynamicDepthMinFrameDurations*/ null, + /*dynamicDepthStallDurations*/ null, + /*highSpeedVideoConfigurations*/ null, inputOutputFormatsMap, listHighResolution, supportsPrivate[i]); break; default: map = new StreamConfigurationMap(scData.streamConfigurationArray, scData.minDurationArray, scData.stallDurationArray, depthScData.streamConfigurationArray, depthScData.minDurationArray, - depthScData.stallDurationArray, /*highSpeedVideoConfigurations*/ null, + depthScData.stallDurationArray, + /*dynamicDepthConfigurations*/ null, + /*dynamicDepthMinFrameDurations*/ null, + /*dynamicDepthStallDurations*/ null, + /*highSpeedVideoConfigurations*/ null, /*inputOutputFormatsMap*/ null, listHighResolution, supportsPrivate[i]); } @@ -1206,6 +1224,12 @@ public class CameraMetadataNative implements Parcelable { CameraCharacteristics.DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS); StreamConfigurationDuration[] depthStallDurations = getBase( CameraCharacteristics.DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS); + StreamConfiguration[] dynamicDepthConfigurations = getBase( + CameraCharacteristics.DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS); + StreamConfigurationDuration[] dynamicDepthMinFrameDurations = getBase( + CameraCharacteristics.DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS); + StreamConfigurationDuration[] dynamicDepthStallDurations = getBase( + CameraCharacteristics.DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS); HighSpeedVideoConfiguration[] highSpeedVideoConfigurations = getBase( CameraCharacteristics.CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS); ReprocessFormatsMap inputOutputFormatsMap = getBase( @@ -1214,7 +1238,8 @@ public class CameraMetadataNative implements Parcelable { return new StreamConfigurationMap( configurations, minFrameDurations, stallDurations, depthConfigurations, depthMinFrameDurations, depthStallDurations, - highSpeedVideoConfigurations, inputOutputFormatsMap, + dynamicDepthConfigurations, dynamicDepthMinFrameDurations, + dynamicDepthStallDurations, highSpeedVideoConfigurations, inputOutputFormatsMap, listHighResolution); } diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java index dd052a8db1d9..a22e008a65fd 100644 --- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java +++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java @@ -95,13 +95,17 @@ public final class StreamConfigurationMap { StreamConfiguration[] depthConfigurations, StreamConfigurationDuration[] depthMinFrameDurations, StreamConfigurationDuration[] depthStallDurations, + StreamConfiguration[] dynamicDepthConfigurations, + StreamConfigurationDuration[] dynamicDepthMinFrameDurations, + StreamConfigurationDuration[] dynamicDepthStallDurations, HighSpeedVideoConfiguration[] highSpeedVideoConfigurations, ReprocessFormatsMap inputOutputFormatsMap, boolean listHighResolution) { this(configurations, minFrameDurations, stallDurations, depthConfigurations, depthMinFrameDurations, depthStallDurations, - highSpeedVideoConfigurations, inputOutputFormatsMap, listHighResolution, - /*enforceImplementationDefined*/ true); + dynamicDepthConfigurations, dynamicDepthMinFrameDurations, + dynamicDepthStallDurations, highSpeedVideoConfigurations, inputOutputFormatsMap, + listHighResolution, /*enforceImplementationDefined*/ true); } /** @@ -131,6 +135,9 @@ public final class StreamConfigurationMap { StreamConfiguration[] depthConfigurations, StreamConfigurationDuration[] depthMinFrameDurations, StreamConfigurationDuration[] depthStallDurations, + StreamConfiguration[] dynamicDepthConfigurations, + StreamConfigurationDuration[] dynamicDepthMinFrameDurations, + StreamConfigurationDuration[] dynamicDepthStallDurations, HighSpeedVideoConfiguration[] highSpeedVideoConfigurations, ReprocessFormatsMap inputOutputFormatsMap, boolean listHighResolution, @@ -163,6 +170,19 @@ public final class StreamConfigurationMap { "depthStallDurations"); } + if (dynamicDepthConfigurations == null) { + mDynamicDepthConfigurations = new StreamConfiguration[0]; + mDynamicDepthMinFrameDurations = new StreamConfigurationDuration[0]; + mDynamicDepthStallDurations = new StreamConfigurationDuration[0]; + } else { + mDynamicDepthConfigurations = checkArrayElementsNotNull(dynamicDepthConfigurations, + "dynamicDepthConfigurations"); + mDynamicDepthMinFrameDurations = checkArrayElementsNotNull( + dynamicDepthMinFrameDurations, "dynamicDepthMinFrameDurations"); + mDynamicDepthStallDurations = checkArrayElementsNotNull(dynamicDepthStallDurations, + "dynamicDepthStallDurations"); + } + if (highSpeedVideoConfigurations == null) { mHighSpeedVideoConfigurations = new HighSpeedVideoConfiguration[0]; } else { @@ -205,6 +225,15 @@ public final class StreamConfigurationMap { mDepthOutputFormats.put(config.getFormat(), mDepthOutputFormats.get(config.getFormat()) + 1); } + for (StreamConfiguration config : mDynamicDepthConfigurations) { + if (!config.isOutput()) { + // Ignoring input configs + continue; + } + + mDynamicDepthOutputFormats.put(config.getFormat(), + mDynamicDepthOutputFormats.get(config.getFormat()) + 1); + } if (configurations != null && enforceImplementationDefined && mOutputFormats.indexOfKey(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) < 0) { @@ -335,6 +364,8 @@ public final class StreamConfigurationMap { int dataspace = imageFormatToDataspace(format); if (dataspace == HAL_DATASPACE_DEPTH) { return mDepthOutputFormats.indexOfKey(internalFormat) >= 0; + } else if (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) { + return mDynamicDepthOutputFormats.indexOfKey(internalFormat) >= 0; } else { return getFormatsMap(/*output*/true).indexOfKey(internalFormat) >= 0; } @@ -446,7 +477,9 @@ public final class StreamConfigurationMap { boolean isFlexible = SurfaceUtils.isFlexibleConsumer(surface); StreamConfiguration[] configs = - surfaceDataspace != HAL_DATASPACE_DEPTH ? mConfigurations : mDepthConfigurations; + surfaceDataspace == HAL_DATASPACE_DEPTH ? mDepthConfigurations : + surfaceDataspace == HAL_DATASPACE_DYNAMIC_DEPTH ? mDynamicDepthConfigurations : + mConfigurations; for (StreamConfiguration config : configs) { if (config.getFormat() == surfaceFormat && config.isOutput()) { // Matching format, either need exact size match, or a flexible consumer @@ -479,7 +512,9 @@ public final class StreamConfigurationMap { int dataspace = imageFormatToDataspace(format); StreamConfiguration[] configs = - dataspace != HAL_DATASPACE_DEPTH ? mConfigurations : mDepthConfigurations; + dataspace == HAL_DATASPACE_DEPTH ? mDepthConfigurations : + dataspace == HAL_DATASPACE_DYNAMIC_DEPTH ? mDynamicDepthConfigurations : + mConfigurations; for (StreamConfiguration config : configs) { if ((config.getFormat() == internalFormat) && config.isOutput() && config.getSize().equals(size)) { @@ -992,6 +1027,12 @@ public final class StreamConfigurationMap { Arrays.equals(mMinFrameDurations, other.mMinFrameDurations) && Arrays.equals(mStallDurations, other.mStallDurations) && Arrays.equals(mDepthConfigurations, other.mDepthConfigurations) && + Arrays.equals(mDepthMinFrameDurations, other.mDepthMinFrameDurations) && + Arrays.equals(mDepthStallDurations, other.mDepthStallDurations) && + Arrays.equals(mDynamicDepthConfigurations, other.mDynamicDepthConfigurations) && + Arrays.equals(mDynamicDepthMinFrameDurations, + other.mDynamicDepthMinFrameDurations) && + Arrays.equals(mDynamicDepthStallDurations, other.mDynamicDepthStallDurations) && Arrays.equals(mHighSpeedVideoConfigurations, other.mHighSpeedVideoConfigurations); } @@ -1005,9 +1046,10 @@ public final class StreamConfigurationMap { public int hashCode() { // XX: do we care about order? return HashCodeHelpers.hashCodeGeneric( - mConfigurations, mMinFrameDurations, - mStallDurations, - mDepthConfigurations, mHighSpeedVideoConfigurations); + mConfigurations, mMinFrameDurations, mStallDurations, + mDepthConfigurations, mDepthMinFrameDurations, mDepthStallDurations, + mDynamicDepthConfigurations, mDynamicDepthMinFrameDurations, + mDynamicDepthStallDurations, mHighSpeedVideoConfigurations); } // Check that the argument is supported by #getOutputFormats or #getInputFormats @@ -1022,6 +1064,10 @@ public final class StreamConfigurationMap { if (mDepthOutputFormats.indexOfKey(internalFormat) >= 0) { return format; } + } else if (internalDataspace == HAL_DATASPACE_DYNAMIC_DEPTH) { + if (mDynamicDepthOutputFormats.indexOfKey(internalFormat) >= 0) { + return format; + } } else { if (mAllOutputFormats.indexOfKey(internalFormat) >= 0) { return format; @@ -1245,6 +1291,7 @@ public final class StreamConfigurationMap { switch (format) { case ImageFormat.JPEG: case ImageFormat.DEPTH_POINT_CLOUD: + case ImageFormat.DEPTH_JPEG: return HAL_PIXEL_FORMAT_BLOB; case ImageFormat.DEPTH16: return HAL_PIXEL_FORMAT_Y16; @@ -1264,6 +1311,7 @@ public final class StreamConfigurationMap { * <li>ImageFormat.JPEG => HAL_DATASPACE_V0_JFIF * <li>ImageFormat.DEPTH_POINT_CLOUD => HAL_DATASPACE_DEPTH * <li>ImageFormat.DEPTH16 => HAL_DATASPACE_DEPTH + * <li>ImageFormat.DEPTH_JPEG => HAL_DATASPACE_DYNAMIC_DEPTH * <li>others => HAL_DATASPACE_UNKNOWN * </ul> * </p> @@ -1293,6 +1341,8 @@ public final class StreamConfigurationMap { case ImageFormat.DEPTH16: case ImageFormat.RAW_DEPTH: return HAL_DATASPACE_DEPTH; + case ImageFormat.DEPTH_JPEG: + return HAL_DATASPACE_DYNAMIC_DEPTH; default: return HAL_DATASPACE_UNKNOWN; } @@ -1343,12 +1393,16 @@ public final class StreamConfigurationMap { SparseIntArray formatsMap = !output ? mInputFormats : dataspace == HAL_DATASPACE_DEPTH ? mDepthOutputFormats : + dataspace == HAL_DATASPACE_DYNAMIC_DEPTH ? mDynamicDepthOutputFormats : highRes ? mHighResOutputFormats : mOutputFormats; int sizesCount = formatsMap.get(format); - if ( ((!output || dataspace == HAL_DATASPACE_DEPTH) && sizesCount == 0) || - (output && dataspace != HAL_DATASPACE_DEPTH && mAllOutputFormats.get(format) == 0)) { + if ( ((!output || (dataspace == HAL_DATASPACE_DEPTH || + dataspace == HAL_DATASPACE_DYNAMIC_DEPTH)) && sizesCount == 0) || + (output && (dataspace != HAL_DATASPACE_DEPTH && + dataspace != HAL_DATASPACE_DYNAMIC_DEPTH) && + mAllOutputFormats.get(format) == 0)) { // Only throw if this is really not supported at all throw new IllegalArgumentException("format not available"); } @@ -1357,9 +1411,13 @@ public final class StreamConfigurationMap { int sizeIndex = 0; StreamConfiguration[] configurations = - (dataspace == HAL_DATASPACE_DEPTH) ? mDepthConfigurations : mConfigurations; + (dataspace == HAL_DATASPACE_DEPTH) ? mDepthConfigurations : + (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthConfigurations : + mConfigurations; StreamConfigurationDuration[] minFrameDurations = - (dataspace == HAL_DATASPACE_DEPTH) ? mDepthMinFrameDurations : mMinFrameDurations; + (dataspace == HAL_DATASPACE_DEPTH) ? mDepthMinFrameDurations : + (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthMinFrameDurations : + mMinFrameDurations; for (StreamConfiguration config : configurations) { int fmt = config.getFormat(); @@ -1386,7 +1444,21 @@ public final class StreamConfigurationMap { } } - if (sizeIndex != sizesCount) { + // Dynamic depth streams can have both fast and also high res modes. + if ((sizeIndex != sizesCount) && (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH)) { + + if (sizeIndex > sizesCount) { + throw new AssertionError( + "Too many dynamic depth sizes (expected " + sizesCount + ", actual " + + sizeIndex + ")"); + } + + if (sizeIndex <= 0) { + sizes = new Size[0]; + } else { + sizes = Arrays.copyOf(sizes, sizeIndex); + } + } else if (sizeIndex != sizesCount) { throw new AssertionError( "Too few sizes (expected " + sizesCount + ", actual " + sizeIndex + ")"); } @@ -1409,6 +1481,10 @@ public final class StreamConfigurationMap { for (int j = 0; j < mDepthOutputFormats.size(); j++) { formats[i++] = depthFormatToPublic(mDepthOutputFormats.keyAt(j)); } + if (mDynamicDepthOutputFormats.size() > 0) { + // Only one publicly dynamic depth format is available. + formats[i++] = ImageFormat.DEPTH_JPEG; + } } if (formats.length != i) { throw new AssertionError("Too few formats " + i + ", expected " + formats.length); @@ -1451,11 +1527,13 @@ public final class StreamConfigurationMap { private StreamConfigurationDuration[] getDurations(int duration, int dataspace) { switch (duration) { case DURATION_MIN_FRAME: - return (dataspace == HAL_DATASPACE_DEPTH) ? - mDepthMinFrameDurations : mMinFrameDurations; + return (dataspace == HAL_DATASPACE_DEPTH) ? mDepthMinFrameDurations : + (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? + mDynamicDepthMinFrameDurations : mMinFrameDurations; case DURATION_STALL: - return (dataspace == HAL_DATASPACE_DEPTH) ? - mDepthStallDurations : mStallDurations; + return (dataspace == HAL_DATASPACE_DEPTH) ? mDepthStallDurations : + (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthStallDurations : + mStallDurations; default: throw new IllegalArgumentException("duration was invalid"); } @@ -1467,6 +1545,7 @@ public final class StreamConfigurationMap { int size = formatsMap.size(); if (output) { size += mDepthOutputFormats.size(); + size += mDynamicDepthOutputFormats.size(); } return size; @@ -1486,10 +1565,11 @@ public final class StreamConfigurationMap { return false; } - private boolean isSupportedInternalConfiguration(int format, int dataspace, - Size size) { + private boolean isSupportedInternalConfiguration(int format, int dataspace, Size size) { StreamConfiguration[] configurations = - (dataspace == HAL_DATASPACE_DEPTH) ? mDepthConfigurations : mConfigurations; + (dataspace == HAL_DATASPACE_DEPTH) ? mDepthConfigurations : + (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthConfigurations : + mConfigurations; for (int i = 0; i < configurations.length; i++) { if (configurations[i].getFormat() == format && @@ -1681,6 +1761,8 @@ public final class StreamConfigurationMap { return "DEPTH16"; case ImageFormat.DEPTH_POINT_CLOUD: return "DEPTH_POINT_CLOUD"; + case ImageFormat.DEPTH_JPEG: + return "DEPTH_JPEG"; case ImageFormat.RAW_DEPTH: return "RAW_DEPTH"; case ImageFormat.PRIVATE: @@ -1712,6 +1794,7 @@ public final class StreamConfigurationMap { (1 << HAL_DATASPACE_RANGE_SHIFT); private static final int HAL_DATASPACE_DEPTH = 0x1000; + private static final int HAL_DATASPACE_DYNAMIC_DEPTH = 0x1002; private static final long DURATION_20FPS_NS = 50000000L; /** @@ -1728,6 +1811,10 @@ public final class StreamConfigurationMap { private final StreamConfigurationDuration[] mDepthMinFrameDurations; private final StreamConfigurationDuration[] mDepthStallDurations; + private final StreamConfiguration[] mDynamicDepthConfigurations; + private final StreamConfigurationDuration[] mDynamicDepthMinFrameDurations; + private final StreamConfigurationDuration[] mDynamicDepthStallDurations; + private final HighSpeedVideoConfiguration[] mHighSpeedVideoConfigurations; private final ReprocessFormatsMap mInputOutputFormatsMap; @@ -1745,6 +1832,8 @@ public final class StreamConfigurationMap { private final SparseIntArray mInputFormats = new SparseIntArray(); /** internal format -> num depth output sizes mapping, for HAL_DATASPACE_DEPTH */ private final SparseIntArray mDepthOutputFormats = new SparseIntArray(); + /** internal format -> num dynamic depth output sizes mapping, for HAL_DATASPACE_DYNAMIC_DEPTH */ + private final SparseIntArray mDynamicDepthOutputFormats = new SparseIntArray(); /** High speed video Size -> FPS range count mapping*/ private final HashMap</*HighSpeedVideoSize*/Size, /*Count*/Integer> mHighSpeedVideoSizeMap = new HashMap<Size, Integer>(); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 5b3ad77dbf43..333cfbd400dd 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -597,6 +597,7 @@ public class InputMethodService extends AbstractInputMethodService { Log.v(TAG, "Making IME window invisible"); } setImeWindowStatus(IME_ACTIVE | IME_INVISIBLE, mBackDisposition); + applyVisibilityInInsetsConsumer(false /* setVisible */); onPreRenderedWindowVisibilityChanged(false /* setVisible */); } else { mShowInputFlags = 0; @@ -625,10 +626,10 @@ public class InputMethodService extends AbstractInputMethodService { ? mDecorViewVisible && mWindowVisible : isInputViewShown(); if (dispatchOnShowInputRequested(flags, false)) { if (mIsPreRendered) { - // TODO: notify visibility to insets consumer. if (DEBUG) { Log.v(TAG, "Making IME window visible"); } + applyVisibilityInInsetsConsumer(true /* setVisible */); onPreRenderedWindowVisibilityChanged(true /* setVisible */); } else { showWindow(true); @@ -1887,10 +1888,23 @@ public class InputMethodService extends AbstractInputMethodService { if (DEBUG) Log.v(TAG, "showWindow: draw decorView!"); mWindow.show(); } + maybeNotifyPreRendered(); mDecorViewWasVisible = true; mInShowWindow = false; } + /** + * Notify {@link android.view.ImeInsetsSourceConsumer} if IME has been pre-rendered + * for current EditorInfo, when pre-rendering is enabled. + */ + private void maybeNotifyPreRendered() { + if (!mCanPreRender || !mIsPreRendered) { + return; + } + mPrivOps.reportPreRendered(getCurrentInputEditorInfo()); + } + + private boolean prepareWindow(boolean showInput) { boolean doShowInput = false; mDecorViewVisible = true; @@ -1942,6 +1956,18 @@ public class InputMethodService extends AbstractInputMethodService { } } + /** + * Apply the IME visibility in {@link android.view.ImeInsetsSourceConsumer} when + * pre-rendering is enabled. + * @param setVisible {@code true} to make it visible, false to hide it. + */ + private void applyVisibilityInInsetsConsumer(boolean setVisible) { + if (!mIsPreRendered) { + return; + } + mPrivOps.applyImeVisibility(setVisible); + } + private void finishViews(boolean finishingInput) { if (mInputViewStarted) { if (DEBUG) Log.v(TAG, "CALL: onFinishInputView"); @@ -2081,6 +2107,7 @@ public class InputMethodService extends AbstractInputMethodService { // When IME is not pre-rendered, this will actually show the IME. if (DEBUG) Log.v(TAG, "showWindow: draw decorView!"); mWindow.show(); + maybeNotifyPreRendered(); mDecorViewWasVisible = true; mInShowWindow = false; } else { diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 5bb24bab6e48..3bae12e93745 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -1014,20 +1014,54 @@ public class ConnectivityManager { * to remove an existing always-on VPN configuration. * @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or * {@code false} otherwise. + * @param lockdownWhitelist The list of packages that are allowed to access network directly + * when VPN is in lockdown mode but is not running. Non-existent packages are ignored so + * this method must be called when a package that should be whitelisted is installed or + * uninstalled. * @return {@code true} if the package is set as always-on VPN controller; * {@code false} otherwise. * @hide */ + @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN) public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage, - boolean lockdownEnabled) { + boolean lockdownEnabled, @Nullable List<String> lockdownWhitelist) { try { - return mService.setAlwaysOnVpnPackage(userId, vpnPackage, lockdownEnabled); + return mService.setAlwaysOnVpnPackage( + userId, vpnPackage, lockdownEnabled, lockdownWhitelist); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** + * Configures an always-on VPN connection through a specific application. + * This connection is automatically granted and persisted after a reboot. + * + * <p>The designated package should declare a {@link VpnService} in its + * manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE}, + * otherwise the call will fail. + * + * @param userId The identifier of the user to set an always-on VPN for. + * @param vpnPackage The package name for an installed VPN app on the device, or {@code null} + * to remove an existing always-on VPN configuration. + * @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or + * {@code false} otherwise. + * @return {@code true} if the package is set as always-on VPN controller; + * {@code false} otherwise. + * @hide + */ + @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN) + public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage, + boolean lockdownEnabled) { + try { + return mService.setAlwaysOnVpnPackage( + userId, vpnPackage, lockdownEnabled, /* whitelist */ null); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Returns the package name of the currently set always-on VPN application. * If there is no always-on VPN set, or the VPN is provided by the system instead * of by an app, {@code null} will be returned. @@ -1036,6 +1070,7 @@ public class ConnectivityManager { * or {@code null} if none is set. * @hide */ + @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN) public String getAlwaysOnVpnPackageForUser(int userId) { try { return mService.getAlwaysOnVpnPackage(userId); @@ -1045,6 +1080,36 @@ public class ConnectivityManager { } /** + * @return whether always-on VPN is in lockdown mode. + * + * @hide + **/ + @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN) + public boolean isVpnLockdownEnabled(int userId) { + try { + return mService.isVpnLockdownEnabled(userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + } + + /** + * @return the list of packages that are allowed to access network when always-on VPN is in + * lockdown mode but not connected. Returns {@code null} when VPN lockdown is not active. + * + * @hide + **/ + @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN) + public List<String> getVpnLockdownWhitelist(int userId) { + try { + return mService.getVpnLockdownWhitelist(userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Returns details about the currently active default data network * for a given uid. This is for internal use only to avoid spying * other apps. diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index e97060a0a599..fd7360fd4c17 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -125,8 +125,11 @@ interface IConnectivityManager boolean updateLockdownVpn(); boolean isAlwaysOnVpnPackageSupported(int userId, String packageName); - boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown); + boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown, + in List<String> lockdownWhitelist); String getAlwaysOnVpnPackage(int userId); + boolean isVpnLockdownEnabled(int userId); + List<String> getVpnLockdownWhitelist(int userId); int checkMobileProvisioning(int suggestedTimeOutMs); diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java index b996cdab5164..175263f0adaa 100644 --- a/core/java/android/net/IpPrefix.java +++ b/core/java/android/net/IpPrefix.java @@ -104,6 +104,8 @@ public final class IpPrefix implements Parcelable { * * @hide */ + @SystemApi + @TestApi public IpPrefix(String prefix) { // We don't reuse the (InetAddress, int) constructor because "error: call to this must be // first statement in constructor". We could factor out setting the member variables to an diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java index fbd602c7b2d0..8d779aaa2312 100644 --- a/core/java/android/net/LinkAddress.java +++ b/core/java/android/net/LinkAddress.java @@ -176,6 +176,7 @@ public class LinkAddress implements Parcelable { * @hide */ @SystemApi + @TestApi public LinkAddress(InetAddress address, int prefixLength) { this(address, prefixLength, 0, 0); this.scope = scopeForUnicastAddress(address); @@ -199,6 +200,7 @@ public class LinkAddress implements Parcelable { * @hide */ @SystemApi + @TestApi public LinkAddress(String address) { this(address, 0, 0); this.scope = scopeForUnicastAddress(this.address); @@ -212,6 +214,8 @@ public class LinkAddress implements Parcelable { * @param scope The address scope. * @hide */ + @SystemApi + @TestApi public LinkAddress(String address, int flags, int scope) { // This may throw an IllegalArgumentException; catching it is the caller's responsibility. // TODO: consider rejecting mapped IPv4 addresses such as "::ffff:192.0.2.5/24". diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 662870182eea..42db0fd7cb8c 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -287,7 +287,8 @@ public final class LinkProperties implements Parcelable { * @return true if {@code address} was added or updated, false otherwise. * @hide */ - @UnsupportedAppUsage + @SystemApi + @TestApi public boolean addLinkAddress(LinkAddress address) { if (address == null) { return false; @@ -315,6 +316,8 @@ public final class LinkProperties implements Parcelable { * @return true if the address was removed, false if it did not exist. * @hide */ + @SystemApi + @TestApi public boolean removeLinkAddress(LinkAddress toRemove) { int i = findLinkAddressIndex(toRemove); if (i >= 0) { diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java index 2c831de72051..e04b5fc5f9cf 100644 --- a/core/java/android/net/Network.java +++ b/core/java/android/net/Network.java @@ -123,6 +123,8 @@ public class Network implements Parcelable { /** * @hide */ + @SystemApi + @TestApi public Network(Network that) { this(that.netId, that.mPrivateDnsBypass); } diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index bbf8f97c8865..49c6f74b1a34 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -128,10 +128,14 @@ public class TrafficStats { public static final int TAG_SYSTEM_APP = 0xFFFFFF05; /** @hide */ + @SystemApi + @TestApi public static final int TAG_SYSTEM_DHCP = 0xFFFFFF40; /** @hide */ public static final int TAG_SYSTEM_NTP = 0xFFFFFF41; /** @hide */ + @SystemApi + @TestApi public static final int TAG_SYSTEM_PROBE = 0xFFFFFF42; /** @hide */ public static final int TAG_SYSTEM_NEIGHBOR = 0xFFFFFF43; @@ -140,6 +144,8 @@ public class TrafficStats { /** @hide */ public static final int TAG_SYSTEM_PAC = 0xFFFFFF45; /** @hide */ + @SystemApi + @TestApi public static final int TAG_SYSTEM_DHCP_SERVER = 0xFFFFFF46; private static INetworkStatsService sStatsService; diff --git a/core/java/android/net/apf/ApfCapabilities.java b/core/java/android/net/apf/ApfCapabilities.java index 73cf94b785a7..e09fa8fd9e77 100644 --- a/core/java/android/net/apf/ApfCapabilities.java +++ b/core/java/android/net/apf/ApfCapabilities.java @@ -18,6 +18,9 @@ package android.net.apf; import android.annotation.SystemApi; import android.annotation.TestApi; +import android.content.Context; + +import com.android.internal.R; /** * APF program support capabilities. @@ -74,4 +77,18 @@ public class ApfCapabilities { public boolean hasDataAccess() { return apfVersionSupported >= 4; } + + /** + * @return Whether the APF Filter in the device should filter out IEEE 802.3 Frames. + */ + public static boolean getApfDrop8023Frames(Context context) { + return context.getResources().getBoolean(R.bool.config_apfDrop802_3Frames); + } + + /** + * @return An array of blacklisted EtherType, packets with EtherTypes within it will be dropped. + */ + public static int[] getApfEthTypeBlackList(Context context) { + return context.getResources().getIntArray(R.array.config_apfEthTypeBlackList); + } } diff --git a/packages/NetworkStack/src/android/net/util/FdEventsReader.java b/core/java/android/net/shared/FdEventsReader.java index 8bbf449f6374..5ccc560a3091 100644 --- a/packages/NetworkStack/src/android/net/util/FdEventsReader.java +++ b/core/java/android/net/shared/FdEventsReader.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package android.net.util; +package android.net.shared; -import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT; import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR; +import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT; import android.annotation.NonNull; import android.annotation.Nullable; @@ -63,6 +63,7 @@ import java.io.FileDescriptor; * All public methods MUST only be called from the same thread with which * the Handler constructor argument is associated. * + * @param <BufferType> the type of the buffer used to read data. * @hide */ public abstract class FdEventsReader<BufferType> { @@ -89,6 +90,7 @@ public abstract class FdEventsReader<BufferType> { mBuffer = buffer; } + /** Start this FdEventsReader. */ public void start() { if (onCorrectThread()) { createAndRegisterFd(); @@ -100,6 +102,7 @@ public abstract class FdEventsReader<BufferType> { } } + /** Stop this FdEventsReader and destroy the file descriptor. */ public void stop() { if (onCorrectThread()) { unregisterAndDestroyFd(); @@ -112,18 +115,25 @@ public abstract class FdEventsReader<BufferType> { } @NonNull - public Handler getHandler() { return mHandler; } + public Handler getHandler() { + return mHandler; + } protected abstract int recvBufSize(@NonNull BufferType buffer); - public int recvBufSize() { return recvBufSize(mBuffer); } + /** Returns the size of the receive buffer. */ + public int recvBufSize() { + return recvBufSize(mBuffer); + } /** * Get the number of successful calls to {@link #readPacket(FileDescriptor, Object)}. * * <p>A call was successful if {@link #readPacket(FileDescriptor, Object)} returned a value > 0. */ - public final long numPacketsReceived() { return mPacketsReceived; } + public final long numPacketsReceived() { + return mPacketsReceived; + } /** * Subclasses MUST create the listening socket here, including setting @@ -199,7 +209,9 @@ public abstract class FdEventsReader<BufferType> { onStart(); } - private boolean isRunning() { return (mFd != null) && mFd.valid(); } + private boolean isRunning() { + return (mFd != null) && mFd.valid(); + } // Keep trying to read until we get EAGAIN/EWOULDBLOCK or some fatal error. private boolean handleInput() { diff --git a/core/java/android/net/util/SocketUtils.java b/core/java/android/net/util/SocketUtils.java index de67cf5952f4..fbb15ed693d9 100644 --- a/core/java/android/net/util/SocketUtils.java +++ b/core/java/android/net/util/SocketUtils.java @@ -20,20 +20,29 @@ import static android.system.OsConstants.SOL_SOCKET; import static android.system.OsConstants.SO_BINDTODEVICE; import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.net.MacAddress; import android.net.NetworkUtils; import android.system.ErrnoException; import android.system.NetlinkSocketAddress; import android.system.Os; import android.system.PacketSocketAddress; +import android.system.StructTimeval; + +import libcore.io.IoBridge; import java.io.FileDescriptor; +import java.io.IOException; +import java.net.Inet4Address; import java.net.SocketAddress; +import java.net.SocketException; /** * Collection of utilities to interact with raw sockets. * @hide */ @SystemApi +@TestApi public class SocketUtils { /** * Create a raw datagram socket that is bound to an interface. @@ -57,18 +66,94 @@ public class SocketUtils { } /** - * Make a socket address to bind to packet sockets. + * Make socket address that packet sockets can bind to. */ public static SocketAddress makePacketSocketAddress(short protocol, int ifIndex) { return new PacketSocketAddress(protocol, ifIndex); } /** - * Make a socket address to send raw packets. + * Make a socket address that packet socket can send packets to. */ public static SocketAddress makePacketSocketAddress(int ifIndex, byte[] hwAddr) { return new PacketSocketAddress(ifIndex, hwAddr); } + /** + * Set an option on a socket that takes a time value argument. + */ + public static void setSocketTimeValueOption( + FileDescriptor fd, int level, int option, long millis) throws ErrnoException { + Os.setsockoptTimeval(fd, level, option, StructTimeval.fromMillis(millis)); + } + + /** + * Bind a socket to the specified address. + */ + public static void bindSocket(FileDescriptor fd, SocketAddress addr) + throws ErrnoException, SocketException { + Os.bind(fd, addr); + } + + /** + * Connect a socket to the specified address. + */ + public static void connectSocket(FileDescriptor fd, SocketAddress addr) + throws ErrnoException, SocketException { + Os.connect(fd, addr); + } + + /** + * Send a message on a socket, using the specified SocketAddress. + */ + public static void sendTo(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, + int flags, SocketAddress addr) throws ErrnoException, SocketException { + Os.sendto(fd, bytes, byteOffset, byteCount, flags, addr); + } + + /** + * @see IoBridge#closeAndSignalBlockedThreads(FileDescriptor) + */ + public static void closeSocket(FileDescriptor fd) throws IOException { + IoBridge.closeAndSignalBlockedThreads(fd); + } + + /** + * Attaches a socket filter that accepts DHCP packets to the given socket. + */ + public static void attachDhcpFilter(FileDescriptor fd) throws SocketException { + NetworkUtils.attachDhcpFilter(fd); + } + + /** + * Attaches a socket filter that accepts ICMPv6 router advertisements to the given socket. + * @param fd the socket's {@link FileDescriptor}. + * @param packetType the hardware address type, one of ARPHRD_*. + */ + public static void attachRaFilter(FileDescriptor fd, int packetType) throws SocketException { + NetworkUtils.attachRaFilter(fd, packetType); + } + + /** + * Attaches a socket filter that accepts L2-L4 signaling traffic required for IP connectivity. + * + * This includes: all ARP, ICMPv6 RS/RA/NS/NA messages, and DHCPv4 exchanges. + * + * @param fd the socket's {@link FileDescriptor}. + * @param packetType the hardware address type, one of ARPHRD_*. + */ + public static void attachControlPacketFilter(FileDescriptor fd, int packetType) + throws SocketException { + NetworkUtils.attachControlPacketFilter(fd, packetType); + } + + /** + * Add an entry into the ARP cache. + */ + public static void addArpEntry(Inet4Address ipv4Addr, MacAddress ethAddr, String ifname, + FileDescriptor fd) throws IOException { + NetworkUtils.addArpEntry(ipv4Addr, ethAddr, ifname, fd); + } + private SocketUtils() {} } diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl index 6801618e6a68..0b2cfdd9ece3 100644 --- a/core/java/android/nfc/INfcAdapter.aidl +++ b/core/java/android/nfc/INfcAdapter.aidl @@ -68,4 +68,8 @@ interface INfcAdapter void removeNfcUnlockHandler(INfcUnlockHandler unlockHandler); void verifyNfcPermission(); + boolean isNfcSecureEnabled(); + boolean deviceSupportsNfcSecure(); + boolean setNfcSecure(boolean enable); + } diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index e55e0364f5d4..a7d2ee98b45c 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -1702,6 +1702,63 @@ public final class NfcAdapter { } /** + * Sets Secure NFC feature. + * <p>This API is for the Settings application. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + public boolean setNfcSecure(boolean enable) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + try { + return sService.setNfcSecure(enable); + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + return false; + } + } + + /** + * Checks if the device supports Secure NFC functionality. + * + * @return True if device supports Secure NFC, false otherwise + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + */ + public boolean deviceSupportsNfcSecure() { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + try { + return sService.deviceSupportsNfcSecure(); + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + return false; + } + } + + /** + * Checks Secure NFC feature is enabled. + * + * @return True if device supports Secure NFC is enabled, false otherwise + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if device doesn't support + * Secure NFC functionality. {@link #deviceSupportsNfcSecure} + */ + public boolean isNfcSecureEnabled() { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + try { + return sService.isNfcSecureEnabled(); + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + return false; + } + } + + /** * Enable NDEF Push feature. * <p>This API is for the Settings application. * @hide diff --git a/core/java/android/os/DumpstateOptions.java b/core/java/android/os/DumpstateOptions.java deleted file mode 100644 index 53037b2499cd..000000000000 --- a/core/java/android/os/DumpstateOptions.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.os; - -/** - * Options passed to dumpstate service. - * - * @hide - */ -public final class DumpstateOptions implements Parcelable { - // If true the caller can get callbacks with per-section - // progress details. - private final boolean mGetSectionDetails; - // Name of the caller. - private final String mName; - - public DumpstateOptions(Parcel in) { - mGetSectionDetails = in.readBoolean(); - mName = in.readString(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeBoolean(mGetSectionDetails); - out.writeString(mName); - } - - public static final Parcelable.Creator<DumpstateOptions> CREATOR = - new Parcelable.Creator<DumpstateOptions>() { - public DumpstateOptions createFromParcel(Parcel in) { - return new DumpstateOptions(in); - } - - public DumpstateOptions[] newArray(int size) { - return new DumpstateOptions[size]; - } - }; -} diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index a236300a56cf..3feccf507ff5 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -50,6 +50,7 @@ public class Environment { /** {@hide} */ public static final String DIR_ANDROID = "Android"; + private static final String DIR_SANDBOX = "sandbox"; private static final String DIR_DATA = "data"; private static final String DIR_MEDIA = "media"; private static final String DIR_OBB = "obb"; @@ -117,6 +118,10 @@ public class Environment { return buildPaths(getExternalDirs(), type); } + public File[] buildExternalStorageAndroidSandboxDirs() { + return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_SANDBOX); + } + public File[] buildExternalStorageAndroidDataDirs() { return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA); } @@ -824,6 +829,15 @@ public class Environment { * Returns the path for android-specific data on the SD card. * @hide */ + public static File[] buildExternalStorageAndroidSandboxDirs() { + throwIfUserRequired(); + return sCurrentUser.buildExternalStorageAndroidSandboxDirs(); + } + + /** + * Returns the path for android-specific data on the SD card. + * @hide + */ public static File[] buildExternalStorageAndroidDataDirs() { throwIfUserRequired(); return sCurrentUser.buildExternalStorageAndroidDataDirs(); diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index efcad3ece97d..93360a5fa377 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -63,7 +63,7 @@ public class GraphicsEnvironment { private static final boolean DEBUG = false; private static final String TAG = "GraphicsEnvironment"; private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0"; - private static final String GUP_WHITELIST_FILENAME = "whitelist.txt"; + private static final String GAME_DRIVER_WHITELIST_FILENAME = "whitelist.txt"; private static final String ANGLE_RULES_FILE = "a4a_rules.json"; private static final String ANGLE_TEMP_RULES = "debug.angle.rules"; private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID"; @@ -529,44 +529,45 @@ public class GraphicsEnvironment { return; } - // GUP_DEV_ALL_APPS + // GAME_DRIVER_ALL_APPS // 0: Default (Invalid values fallback to default as well) // 1: All apps use Game Driver // 2: All apps use system graphics driver - int gupDevAllApps = coreSettings.getInt(Settings.Global.GUP_DEV_ALL_APPS, 0); - if (gupDevAllApps == 2) { + int gameDriverAllApps = coreSettings.getInt(Settings.Global.GAME_DRIVER_ALL_APPS, 0); + if (gameDriverAllApps == 2) { if (DEBUG) { - Log.w(TAG, "GUP is turned off on this device"); + Log.w(TAG, "Game Driver is turned off on this device"); } return; } - if (gupDevAllApps != 1) { - // GUP_DEV_OPT_OUT_APPS has higher priority than GUP_DEV_OPT_IN_APPS - if (getGlobalSettingsString(coreSettings, Settings.Global.GUP_DEV_OPT_OUT_APPS) + if (gameDriverAllApps != 1) { + // GAME_DRIVER_OPT_OUT_APPS has higher priority than GAME_DRIVER_OPT_IN_APPS + if (getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_OPT_OUT_APPS) .contains(ai.packageName)) { if (DEBUG) { - Log.w(TAG, ai.packageName + " opts out from GUP."); + Log.w(TAG, ai.packageName + " opts out from Game Driver."); } return; } - boolean isDevOptIn = getGlobalSettingsString(coreSettings, - Settings.Global.GUP_DEV_OPT_IN_APPS) - .contains(ai.packageName); + boolean isOptIn = + getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_OPT_IN_APPS) + .contains(ai.packageName); - if (!isDevOptIn && !onWhitelist(context, driverPackageName, ai.packageName)) { + if (!isOptIn && !onWhitelist(context, driverPackageName, ai.packageName)) { if (DEBUG) { Log.w(TAG, ai.packageName + " is not on the whitelist."); } return; } - if (!isDevOptIn) { + if (!isOptIn) { // At this point, the application is on the whitelist only, check whether it's // on the blacklist, terminate early when it's on the blacklist. try { // TODO(b/121350991) Switch to DeviceConfig with property listener. - String base64String = coreSettings.getString(Settings.Global.GUP_BLACKLIST); + String base64String = + coreSettings.getString(Settings.Global.GAME_DRIVER_BLACKLIST); if (base64String != null && !base64String.isEmpty()) { Blacklists blacklistsProto = Blacklists.parseFrom( Base64.decode(base64String, BASE64_FLAGS)); @@ -652,7 +653,7 @@ public class GraphicsEnvironment { Context driverContext = context.createPackageContext(driverPackageName, Context.CONTEXT_RESTRICTED); AssetManager assets = driverContext.getAssets(); - InputStream stream = assets.open(GUP_WHITELIST_FILENAME); + InputStream stream = assets.open(GAME_DRIVER_WHITELIST_FILENAME); BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); for (String packageName; (packageName = reader.readLine()) != null; ) { if (packageName.equals(applicationPackageName)) { diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index 9e47179e9152..e94ad2b8989e 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -74,6 +74,19 @@ public class ZygoteProcess { public static final String ZYGOTE_SECONDARY_SOCKET_NAME = "zygote_secondary"; /** + * @hide for internal use only. + */ + public static final int ZYGOTE_CONNECT_TIMEOUT_MS = 20000; + + /** + * @hide for internal use only. + * + * Use a relatively short delay, because for app zygote, this is in the critical path of + * service launch. + */ + public static final int ZYGOTE_CONNECT_RETRY_DELAY_MS = 50; + + /** * @hide for internal use only */ public static final String BLASTULA_POOL_SOCKET_NAME = "blastula_pool"; @@ -933,7 +946,8 @@ public class ZygoteProcess { * @param address The name of the socket to connect to. */ public static void waitForConnectionToZygote(LocalSocketAddress zygoteSocketAddress) { - for (int n = 20; n >= 0; n--) { + int numRetries = ZYGOTE_CONNECT_TIMEOUT_MS / ZYGOTE_CONNECT_RETRY_DELAY_MS; + for (int n = numRetries; n >= 0; n--) { try { final ZygoteState zs = ZygoteState.connect(zygoteSocketAddress, null); @@ -945,7 +959,7 @@ public class ZygoteProcess { } try { - Thread.sleep(1000); + Thread.sleep(ZYGOTE_CONNECT_RETRY_DELAY_MS); } catch (InterruptedException ie) { } } diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 148dd910ef69..d58e00af5054 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -112,14 +112,12 @@ public final class DeviceConfig { @SystemApi public interface IntelligenceAttention { String NAMESPACE = "intelligence_attention"; - /** - * If {@code true}, enables the attention check. - */ - String PROPERTY_ATTENTION_CHECK_ENABLED = "attention_check_enabled"; - /** - * Settings for performing the attention check. - */ - String PROPERTY_ATTENTION_CHECK_SETTINGS = "attention_check_settings"; + + /** If {@code true}, enables the attention features. */ + String PROPERTY_ATTENTION_ENABLED = "attention_enabled"; + + /** Settings for the attention features. */ + String PROPERTY_ATTENTION_SETTINGS = "attention_settings"; } /** @@ -182,6 +180,22 @@ public final class DeviceConfig { } /** + * Namespace for {@link AttentionManagerService} related features. + * + * @hide + */ + @SystemApi + public interface AttentionManagerService { + String NAMESPACE = "attention_manager_service"; + + /** If {@code true}, enables {@link AttentionManagerService} features. */ + String PROPERTY_SERVICE_ENABLED = "service_enabled"; + + /** Allows a CTS to inject a fake implementation. */ + String PROPERTY_COMPONENT_NAME = "component_name"; + } + + /** * Namespace for storage-related features. * * @hide diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java index b348da45e2d6..63bbb9c0bc12 100644 --- a/core/java/android/provider/Downloads.java +++ b/core/java/android/provider/Downloads.java @@ -837,6 +837,14 @@ public final class Downloads { } } + /** @hide */ + public static final String MEDIASTORE_DOWNLOADS_DELETED_CALL = "mediastore_downloads_deleted"; + + /** @hide */ + public static final String EXTRA_IDS = "ids"; + /** @hide */ + public static final String EXTRA_MIME_TYPES = "mime_types"; + /** * Query where clause for general querying. */ diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 887175a80421..124c50a3393e 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -158,6 +158,8 @@ public final class MediaStore { public static final String PARAM_PROGRESS = "progress"; /** {@hide} */ public static final String PARAM_REQUIRE_ORIGINAL = "requireOriginal"; + /** {@hide} */ + public static final String PARAM_LIMIT = "limit"; /** * Activity Action: Launch a music player. @@ -513,7 +515,12 @@ public final class MediaStore { * @see MediaStore#createPending(Context, PendingParams) */ public static @NonNull Uri setIncludePending(@NonNull Uri uri) { - return uri.buildUpon().appendQueryParameter(PARAM_INCLUDE_PENDING, "1").build(); + return setIncludePending(uri.buildUpon()).build(); + } + + /** @hide */ + public static @NonNull Uri.Builder setIncludePending(@NonNull Uri.Builder uriBuilder) { + return uriBuilder.appendQueryParameter(PARAM_INCLUDE_PENDING, "1"); } /** @@ -982,6 +989,11 @@ public final class MediaStore { * work with multiple media file types in a single query. */ public static final class Files { + /** @hide */ + public static final String TABLE = "files"; + + /** @hide */ + public static final Uri EXTERNAL_CONTENT_URI = getContentUri(VOLUME_EXTERNAL); /** * Get the content:// style URI for the files table on the diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 195e72c71e55..a7af5d191c17 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -486,6 +486,37 @@ public final class Settings { "android.settings.WIFI_IP_SETTINGS"; /** + * Activity Action: Show setting page to process an Easy Connect (Wi-Fi DPP) QR code and start + * configuration. This intent should be used when you want to use this device to take on the + * configurator role for an IoT/other device. When provided with a valid DPP URI string Settings + * will open a wifi selection screen for the user to indicate which network they would like + * to configure the device specified in the DPP URI string for and carry them through the rest + * of the flow for provisioning the device. + * <p> + * In some cases, a matching Activity may not exist, so ensure you safeguard + * against this by checking WifiManager.isEasyConnectSupported(); + * <p> + * Input: + * The following keys in the bundle with their associated value. + * <ul> + * <li>"qrCode": Standard Easy Connect (Wi-Fi DPP) URI bootstrapping information as a + * string.</li> + * </ul> + * <p> + * Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_PROCESS_WIFI_EASY_CONNECT_QR_CODE = + "android.settings.PROCESS_WIFI_EASY_CONNECT_QR_CODE"; + + /** + * An extra to put in the bundle for {@link #ACTION_PROCESS_WIFI_EASY_CONNECT_QR_CODE} intents. + * It must contain properly formatted Easy Connect (Wi-Fi DPP) URI bootstrapping information for + * the process to proceed. + */ + public static final String EXTRA_QR_CODE = "android.provider.extra.QR_CODE"; + + /** * Activity Action: Show settings to allow configuration of data and view data usage. * <p> * In some cases, a matching Activity may not exist, so ensure you @@ -5800,6 +5831,16 @@ public final class Settings { public static final String ALWAYS_ON_VPN_LOCKDOWN = "always_on_vpn_lockdown"; /** + * Comma separated list of packages that are allowed to access the network when VPN is in + * lockdown mode but not running. + * @see #ALWAYS_ON_VPN_LOCKDOWN + * + * @hide + */ + public static final String ALWAYS_ON_VPN_LOCKDOWN_WHITELIST = + "always_on_vpn_lockdown_whitelist"; + + /** * Whether applications can be installed for this user via the system's * {@link Intent#ACTION_INSTALL_PACKAGE} mechanism. * @@ -11768,6 +11809,44 @@ public final class Settings { public static final String SYNC_MANAGER_CONSTANTS = "sync_manager_constants"; /** + * Broadcast dispatch tuning parameters specific to foreground broadcasts. + * + * This is encoded as a key=value list, separated by commas. Ex: "foo=1,bar=true" + * + * The following keys are supported: + * <pre> + * bcast_timeout (long) + * bcast_slow_time (long) + * bcast_deferral (long) + * bcast_deferral_decay_factor (float) + * bcast_deferral_floor (long) + * </pre> + * + * @hide + */ + public static final String BROADCAST_FG_CONSTANTS = "bcast_fg_constants"; + + /** + * Broadcast dispatch tuning parameters specific to background broadcasts. + * + * This is encoded as a key=value list, separated by commas. Ex: "foo=1,bar=true". + * See {@link #BROADCAST_FG_CONSTANTS} for the list of supported keys. + * + * @hide + */ + public static final String BROADCAST_BG_CONSTANTS = "bcast_bg_constants"; + + /** + * Broadcast dispatch tuning parameters specific to specific "offline" broadcasts. + * + * This is encoded as a key=value list, separated by commas. Ex: "foo=1,bar=true". + * See {@link #BROADCAST_FG_CONSTANTS} for the list of supported keys. + * + * @hide + */ + public static final String BROADCAST_OFFLOAD_CONSTANTS = "bcast_offload_constants"; + + /** * Whether or not App Standby feature is enabled by system. This controls throttling of apps * based on usage patterns and predictions. Platform will turn on this feature if both this * flag and {@link #ADAPTIVE_BATTERY_MANAGEMENT_ENABLED} is on. @@ -12162,33 +12241,33 @@ public final class Settings { "angle_whitelist"; /** - * Game Update Package global preference for all Apps. + * Game Driver global preference for all Apps. * 0 = Default - * 1 = All Apps use Game Update Package + * 1 = All Apps use Game Driver * 2 = All Apps use system graphics driver * @hide */ - public static final String GUP_DEV_ALL_APPS = "gup_dev_all_apps"; + public static final String GAME_DRIVER_ALL_APPS = "game_driver_all_apps"; /** - * List of Apps selected to use Game Update Package. + * List of Apps selected to use Game Driver. * i.e. <pkg1>,<pkg2>,...,<pkgN> * @hide */ - public static final String GUP_DEV_OPT_IN_APPS = "gup_dev_opt_in_apps"; + public static final String GAME_DRIVER_OPT_IN_APPS = "game_driver_opt_in_apps"; /** - * List of Apps selected not to use Game Update Package. + * List of Apps selected not to use Game Driver. * i.e. <pkg1>,<pkg2>,...,<pkgN> * @hide */ - public static final String GUP_DEV_OPT_OUT_APPS = "gup_dev_opt_out_apps"; + public static final String GAME_DRIVER_OPT_OUT_APPS = "game_driver_opt_out_apps"; /** - * Apps on the blacklist that are forbidden to use Game Update Package. + * Apps on the blacklist that are forbidden to use Game Driver. * @hide */ - public static final String GUP_BLACKLIST = "gup_blacklist"; + public static final String GAME_DRIVER_BLACKLIST = "game_driver_blacklist"; /** * Apps on the whitelist that are allowed to use Game Driver. diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 3a644d4cd12a..551bb8ada044 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -28,6 +28,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; +import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; /** @@ -410,7 +411,9 @@ public class StatusBarNotification implements Parcelable { .clearSubtype() .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID, getGroupLogTag()) .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_SUMMARY, - getNotification().isGroupSummary() ? 1 : 0); + getNotification().isGroupSummary() ? 1 : 0) + .addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CATEGORY, + getNotification().category); } private String getGroupLogTag() { diff --git a/core/java/android/view/IDisplayFoldListener.aidl b/core/java/android/view/IDisplayFoldListener.aidl new file mode 100644 index 000000000000..2c91149dfc1b --- /dev/null +++ b/core/java/android/view/IDisplayFoldListener.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +/** + * {@hide} + */ +oneway interface IDisplayFoldListener +{ + /** Called when the foldedness of a display changes */ + void onDisplayFoldChanged(int displayId, boolean folded); +} diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 42ac8801629f..8ae4757f5de6 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -34,6 +34,7 @@ import android.os.ParcelFileDescriptor; import android.view.IApplicationToken; import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IDockedStackListener; +import android.view.IDisplayFoldListener; import android.view.IOnKeyguardExitResult; import android.view.IPinnedStackListener; import android.view.RemoteAnimationAdapter; @@ -403,6 +404,16 @@ interface IWindowManager Region getCurrentImeTouchRegion(); /** + * Registers an IDisplayFoldListener. + */ + void registerDisplayFoldListener(IDisplayFoldListener listener); + + /** + * Unregisters an IDisplayFoldListener. + */ + void unregisterDisplayFoldListener(IDisplayFoldListener listener); + + /** * Starts a window trace. */ void startWindowTrace(); diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java new file mode 100644 index 000000000000..7026d2b1389c --- /dev/null +++ b/core/java/android/view/ImeInsetsSourceConsumer.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.view; + +import static android.view.InsetsState.TYPE_IME; + +import android.os.Parcel; +import android.text.TextUtils; +import android.view.SurfaceControl.Transaction; +import android.view.WindowInsets.Type; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.Arrays; +import java.util.function.Supplier; + +/** + * Controls the visibility and animations of IME window insets source. + * @hide + */ +public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { + private EditorInfo mFocusedEditor; + private EditorInfo mPreRenderedEditor; + /** + * Determines if IME would be shown next time IME is pre-rendered for currently focused + * editor {@link #mFocusedEditor} if {@link #isServedEditorRendered} is {@code true}. + */ + private boolean mShowOnNextImeRender; + private boolean mHasWindowFocus; + + public ImeInsetsSourceConsumer( + InsetsState state, Supplier<Transaction> transactionSupplier, + InsetsController controller) { + super(TYPE_IME, state, transactionSupplier, controller); + } + + public void onPreRendered(EditorInfo info) { + mPreRenderedEditor = info; + if (mShowOnNextImeRender) { + mShowOnNextImeRender = false; + if (isServedEditorRendered()) { + applyImeVisibility(true /* setVisible */); + } + } + } + + public void onServedEditorChanged(EditorInfo info) { + if (isDummyOrEmptyEditor(info)) { + mShowOnNextImeRender = false; + } + mFocusedEditor = info; + } + + public void applyImeVisibility(boolean setVisible) { + if (!mHasWindowFocus) { + // App window doesn't have focus, any visibility changes would be no-op. + return; + } + + if (setVisible) { + mController.show(Type.IME); + } else { + mController.hide(Type.IME); + } + } + + @Override + public void onWindowFocusGained() { + mHasWindowFocus = true; + getImm().registerImeConsumer(this); + } + + @Override + public void onWindowFocusLost() { + mHasWindowFocus = false; + } + + private boolean isDummyOrEmptyEditor(EditorInfo info) { + // TODO(b/123044812): Handle dummy input gracefully in IME Insets API + return info == null || (info.fieldId <= 0 && info.inputType <= 0); + } + + private boolean isServedEditorRendered() { + if (mFocusedEditor == null || mPreRenderedEditor == null + || isDummyOrEmptyEditor(mFocusedEditor) + || isDummyOrEmptyEditor(mPreRenderedEditor)) { + // No view is focused or ready. + return false; + } + return areEditorsSimilar(mFocusedEditor, mPreRenderedEditor); + } + + @VisibleForTesting + public static boolean areEditorsSimilar(EditorInfo info1, EditorInfo info2) { + // We don't need to compare EditorInfo.fieldId (View#id) since that shouldn't change + // IME views. + boolean areOptionsSimilar = + info1.imeOptions == info2.imeOptions + && info1.inputType == info2.inputType + && TextUtils.equals(info1.packageName, info2.packageName); + areOptionsSimilar &= info1.privateImeOptions != null + ? info1.privateImeOptions.equals(info2.privateImeOptions) : true; + + if (!areOptionsSimilar) { + return false; + } + + // compare bundle extras. + if ((info1.extras == null && info2.extras == null) || info1.extras == info2.extras) { + return true; + } + if ((info1.extras == null && info2.extras != null) + || (info1.extras == null && info2.extras != null)) { + return false; + } + if (info1.extras.hashCode() == info2.extras.hashCode() + || info1.extras.equals(info1)) { + return true; + } + if (info1.extras.size() != info2.extras.size()) { + return false; + } + if (info1.extras.toString().equals(info2.extras.toString())) { + return true; + } + + // Compare bytes + Parcel parcel1 = Parcel.obtain(); + info1.extras.writeToParcel(parcel1, 0); + parcel1.setDataPosition(0); + Parcel parcel2 = Parcel.obtain(); + info2.extras.writeToParcel(parcel2, 0); + parcel2.setDataPosition(0); + + return Arrays.equals(parcel1.createByteArray(), parcel2.createByteArray()); + } + + private InputMethodManager getImm() { + return mController.getViewRoot().mDisplayContext.getSystemService(InputMethodManager.class); + } +} diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 2142c36f8803..4f9ecd575326 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -16,6 +16,8 @@ package android.view; +import static android.view.InsetsState.TYPE_IME; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -262,7 +264,7 @@ public class InsetsController implements WindowInsetsController { if (controller != null) { return controller; } - controller = new InsetsSourceConsumer(type, mState, Transaction::new, this); + controller = createConsumerOfType(type); mSourceConsumers.put(type, controller); return controller; } @@ -274,6 +276,32 @@ public class InsetsController implements WindowInsetsController { } /** + * Called when current window gains focus. + */ + public void onWindowFocusGained() { + getSourceConsumer(TYPE_IME).onWindowFocusGained(); + } + + /** + * Called when current window loses focus. + */ + public void onWindowFocusLost() { + getSourceConsumer(TYPE_IME).onWindowFocusLost(); + } + + ViewRootImpl getViewRoot() { + return mViewRoot; + } + + private InsetsSourceConsumer createConsumerOfType(int type) { + if (type == TYPE_IME) { + return new ImeInsetsSourceConsumer(mState, Transaction::new, this); + } else { + return new InsetsSourceConsumer(type, mState, Transaction::new, this); + } + } + + /** * Sends the local visibility state back to window manager. */ private void sendStateToWindowManager() { diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index 7937cb69b80e..f48318cd7d0a 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -30,12 +30,12 @@ import java.util.function.Supplier; */ public class InsetsSourceConsumer { + protected final InsetsController mController; + protected boolean mVisible; private final Supplier<Transaction> mTransactionSupplier; private final @InternalInsetType int mType; private final InsetsState mState; - private final InsetsController mController; private @Nullable InsetsSourceControl mSourceControl; - private boolean mVisible; public InsetsSourceConsumer(@InternalInsetType int type, InsetsState state, Supplier<Transaction> transactionSupplier, InsetsController controller) { @@ -43,7 +43,7 @@ public class InsetsSourceConsumer { mState = state; mTransactionSupplier = transactionSupplier; mController = controller; - mVisible = InsetsState.getDefaultVisibly(type); + mVisible = InsetsState.getDefaultVisibility(type); } public void setControl(@Nullable InsetsSourceControl control) { @@ -76,6 +76,16 @@ public class InsetsSourceConsumer { setVisible(false); } + /** + * Called when current window gains focus + */ + public void onWindowFocusGained() {} + + /** + * Called when current window loses focus. + */ + public void onWindowFocusLost() {} + boolean applyLocalVisibilityOverride() { // If we don't have control, we are not able to change the visibility. diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index a6af1a296faf..4f809fe6d54a 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -278,7 +278,7 @@ public class InsetsState implements Parcelable { } } - public static boolean getDefaultVisibly(@InsetType int type) { + public static boolean getDefaultVisibility(@InsetType int type) { switch (type) { case TYPE_TOP_BAR: case TYPE_SIDE_BAR_1: diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 45e6c50d9ada..ecbec652fcf0 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -492,7 +492,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb if (mBackgroundControl == null) { return; } - if ((mSurfaceFlags & PixelFormat.OPAQUE) == 0) { + if ((mSurfaceFlags & PixelFormat.OPAQUE) != 0) { mBackgroundControl.show(); mBackgroundControl.setLayer(Integer.MIN_VALUE); } else { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index a031b704627f..f47eb10efd29 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2802,6 +2802,11 @@ public final class ViewRootImpl implements ViewParent, hasWindowFocus = mUpcomingWindowFocus; inTouchMode = mUpcomingInTouchMode; } + if (hasWindowFocus) { + mInsetsController.onWindowFocusGained(); + } else { + mInsetsController.onWindowFocusLost(); + } if (mAdded) { profileRendering(hasWindowFocus); diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index b9017b365f90..d7f1b9f2c3e1 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -156,8 +156,7 @@ public final class ContentCaptureManager { // Wait for system server to return the component name. final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); mHandler.sendMessage(obtainMessage( - ContentCaptureManager::handleReceiverServiceComponentName, - this, mContext.getUserId(), resultReceiver)); + ContentCaptureManager::handleGetComponentName, this, resultReceiver)); try { return resultReceiver.getParcelableResult(); @@ -198,7 +197,7 @@ public final class ContentCaptureManager { Preconditions.checkNotNull(request); try { - mService.removeUserData(mContext.getUserId(), request); + mService.removeUserData(request); } catch (RemoteException e) { e.rethrowFromSystemServer(); } @@ -227,9 +226,9 @@ public final class ContentCaptureManager { /** Retrieves the component name of the target content capture service through system_server. */ - private void handleReceiverServiceComponentName(int userId, IResultReceiver resultReceiver) { + private void handleGetComponentName(@NonNull IResultReceiver resultReceiver) { try { - mService.getReceiverServiceComponentName(userId, resultReceiver); + mService.getServiceComponentName(resultReceiver); } catch (RemoteException e) { Log.w(TAG, "Unable to retrieve service component name: " + e); } diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl index 51aea162f1db..56ed8bfab9ad 100644 --- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl +++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl @@ -34,21 +34,21 @@ import java.util.List; */ oneway interface IContentCaptureManager { /** - * Starts a new session for the provided {@code userId} running as part of the + * Starts a new session for the calling user running as part of the * app's activity identified by {@code activityToken}/{@code componentName}. * * @param sessionId Unique session id as provided by the app. * @param flags Meta flags that enable or disable content capture (see * {@link IContentCaptureContext#flags}). */ - void startSession(int userId, IBinder activityToken, in ComponentName componentName, + void startSession(IBinder activityToken, in ComponentName componentName, String sessionId, int flags, in IResultReceiver result); /** - * Marks the end of a session for the provided {@code userId} identified by + * Marks the end of a session for the calling user identified by * the corresponding {@code startSession}'s {@code sessionId}. */ - void finishSession(int userId, String sessionId); + void finishSession(String sessionId); /** * Returns the content capture service's component name (if enabled and @@ -56,10 +56,10 @@ oneway interface IContentCaptureManager { * @param Receiver of the content capture service's @{code ComponentName} * provided {@code Bundle} with key "{@code EXTRA}". */ - void getReceiverServiceComponentName(int userId, in IResultReceiver result); + void getServiceComponentName(in IResultReceiver result); /** - * Requests the removal of user data for the provided {@code userId}. + * Requests the removal of user data for the calling user. */ - void removeUserData(int userId, in UserDataRemovalRequest request); + void removeUserData(in UserDataRemovalRequest request); } diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index 9e99c88da3ca..83dbf2d5bb58 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -211,8 +211,8 @@ public final class MainContentCaptureSession extends ContentCaptureSession { try { if (mSystemServerInterface == null) return; - mSystemServerInterface.startSession(mContext.getUserId(), mApplicationToken, - componentName, mId, flags, new IResultReceiver.Stub() { + mSystemServerInterface.startSession(mApplicationToken, componentName, mId, flags, + new IResultReceiver.Stub() { @Override public void send(int resultCode, Bundle resultData) { IBinder binder = null; @@ -473,7 +473,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { try { if (mSystemServerInterface == null) return; - mSystemServerInterface.finishSession(mContext.getUserId(), mId); + mSystemServerInterface.finishSession(mId); } catch (RemoteException e) { Log.e(TAG, "Error destroying system-service session " + mId + " for " + getDebugState() + ": " + e); diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 0cb1800996c9..7fee3ef29a09 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -55,6 +55,7 @@ import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.SparseArray; import android.view.Display; +import android.view.ImeInsetsSourceConsumer; import android.view.InputChannel; import android.view.InputEvent; import android.view.InputEventSender; @@ -441,6 +442,13 @@ public final class InputMethodManager { */ private int mRequestUpdateCursorAnchorInfoMonitorMode = REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE; + /** + * When {@link ViewRootImpl#sNewInsetsMode} is set to + * >= {@link ViewRootImpl#NEW_INSETS_MODE_IME}, {@link ImeInsetsSourceConsumer} applies the + * IME visibility and listens for other state changes. + */ + private ImeInsetsSourceConsumer mImeInsetsConsumer; + final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20); final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20); @@ -454,6 +462,8 @@ public final class InputMethodManager { static final int MSG_TIMEOUT_INPUT_EVENT = 6; static final int MSG_FLUSH_INPUT_EVENT = 7; static final int MSG_REPORT_FULLSCREEN_MODE = 10; + static final int MSG_REPORT_PRE_RENDERED = 15; + static final int MSG_APPLY_IME_VISIBILITY = 20; private static boolean isAutofillUIShowing(View servedView) { AutofillManager afm = servedView.getContext().getSystemService(AutofillManager.class); @@ -650,6 +660,23 @@ public final class InputMethodManager { } return; } + case MSG_REPORT_PRE_RENDERED: { + synchronized (mH) { + if (mImeInsetsConsumer != null) { + mImeInsetsConsumer.onPreRendered((EditorInfo) msg.obj); + } + } + return; + + } + case MSG_APPLY_IME_VISIBILITY: { + synchronized (mH) { + if (mImeInsetsConsumer != null) { + mImeInsetsConsumer.applyImeVisibility(msg.arg1 != 0); + } + } + return; + } } } } @@ -729,6 +756,18 @@ public final class InputMethodManager { .sendToTarget(); } + @Override + public void reportPreRendered(EditorInfo info) { + mH.obtainMessage(MSG_REPORT_PRE_RENDERED, 0, 0, info) + .sendToTarget(); + } + + @Override + public void applyImeVisibility(boolean setVisible) { + mH.obtainMessage(MSG_APPLY_IME_VISIBILITY, setVisible ? 1 : 0, 0) + .sendToTarget(); + } + }; final InputConnection mDummyInputConnection = new BaseInputConnection(this, false); @@ -1515,6 +1554,7 @@ public final class InputMethodManager { // Hook 'em up and let 'er rip. mCurrentTextBoxAttribute = tba; + maybeCallServedViewChangedLocked(tba); mServedConnecting = false; if (mServedInputConnectionWrapper != null) { mServedInputConnectionWrapper.deactivate(); @@ -1730,6 +1770,10 @@ public final class InputMethodManager { mCurrentTextBoxAttribute = null; mCompletions = null; mServedConnecting = true; + // servedView has changed and it's not editable. + if (!mServedView.onCheckIsTextEditor()) { + maybeCallServedViewChangedLocked(null); + } } if (ic != null) { @@ -1828,6 +1872,21 @@ public final class InputMethodManager { } /** + * Register for IME state callbacks and applying visibility in + * {@link android.view.ImeInsetsSourceConsumer}. + * @hide + */ + public void registerImeConsumer(@NonNull ImeInsetsSourceConsumer imeInsetsConsumer) { + if (imeInsetsConsumer == null) { + throw new IllegalStateException("ImeInsetsSourceConsumer cannot be null."); + } + + synchronized (mH) { + mImeInsetsConsumer = imeInsetsConsumer; + } + } + + /** * Report the current selection range. * * <p><strong>Editor authors</strong>, you need to call this method whenever @@ -2705,6 +2764,12 @@ public final class InputMethodManager { } } + private void maybeCallServedViewChangedLocked(EditorInfo tba) { + if (mImeInsetsConsumer != null) { + mImeInsetsConsumer.onServedEditorChanged(tba); + } + } + void doDump(FileDescriptor fd, PrintWriter fout, String[] args) { final Printer p = new PrintWriterPrinter(fout); p.println("Input method client state for " + this + ":"); diff --git a/core/java/android/view/inputmethod/InputMethodSystemProperty.java b/core/java/android/view/inputmethod/InputMethodSystemProperty.java index 57ed7f923e88..7c79d44e7d31 100644 --- a/core/java/android/view/inputmethod/InputMethodSystemProperty.java +++ b/core/java/android/view/inputmethod/InputMethodSystemProperty.java @@ -93,8 +93,14 @@ public class InputMethodSystemProperty { * {@code true} when per-profile IME is enabled. * @hide */ - public static final boolean PER_PROFILE_IME_ENABLED = MULTI_CLIENT_IME_ENABLED - || Build.IS_DEBUGGABLE && SystemProperties.getBoolean( - PROP_DEBUG_PER_PROFILE_IME, false); - + public static final boolean PER_PROFILE_IME_ENABLED; + static { + if (MULTI_CLIENT_IME_ENABLED) { + PER_PROFILE_IME_ENABLED = true; + } else if (Build.IS_DEBUGGABLE) { + PER_PROFILE_IME_ENABLED = SystemProperties.getBoolean(PROP_DEBUG_PER_PROFILE_IME, true); + } else { + PER_PROFILE_IME_ENABLED = true; + } + } } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 803462d59fad..299801a1b8fc 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -22,6 +22,8 @@ import android.app.prediction.AppPredictionContext; import android.app.prediction.AppPredictionManager; import android.app.prediction.AppPredictor; import android.app.prediction.AppTarget; +import android.app.prediction.AppTargetEvent; +import android.app.prediction.AppTargetId; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -111,6 +113,7 @@ public class ChooserActivity extends ResolverActivity { private static final boolean USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS = false; // TODO(b/123088566) Share these in a better way. private static final String APP_PREDICTION_SHARE_UI_SURFACE = "share"; + public static final String LAUNCH_LOCATON_DIRECT_SHARE = "direct_share"; private static final int APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20; public static final String APP_PREDICTION_INTENT_FILTER_KEY = "intent_filter"; private AppPredictor mAppPredictor; @@ -787,9 +790,11 @@ public class ChooserActivity extends ResolverActivity { // Do nothing. We'll send the voice stuff ourselves. } - // TODO(b/123377860) Send clicked ShortcutInfo to mAppPredictor void updateModelAndChooserCounts(TargetInfo info) { if (info != null) { + if (USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) { + sendClickToAppPredictor(info); + } final ResolveInfo ri = info.getResolveInfo(); Intent targetIntent = getTargetIntent(); if (ri != null && ri.activityInfo != null && targetIntent != null) { @@ -802,13 +807,39 @@ public class ChooserActivity extends ResolverActivity { Log.d(TAG, "ResolveInfo Package is " + ri.activityInfo.packageName); Log.d(TAG, "Action to be updated is " + targetIntent.getAction()); } - } else if(DEBUG) { + } else if (DEBUG) { Log.d(TAG, "Can not log Chooser Counts of null ResovleInfo"); } } mIsSuccessfullySelected = true; } + private void sendClickToAppPredictor(TargetInfo targetInfo) { + if (!(targetInfo instanceof ChooserTargetInfo)) { + return; + } + ChooserTarget chooserTarget = ((ChooserTargetInfo) targetInfo).getChooserTarget(); + ComponentName componentName = chooserTarget.getComponentName(); + Bundle extras = chooserTarget.getIntentExtras(); + if (extras == null) { + return; + } + String shortcutId = extras.getString(Intent.EXTRA_SHORTCUT_ID); + if (shortcutId == null) { + return; + } + mAppPredictor.notifyAppTargetEvent( + new AppTargetEvent.Builder( + new AppTarget( + new AppTargetId(shortcutId), + componentName.getPackageName(), + componentName.getClassName(), + getUser()), + AppTargetEvent.ACTION_LAUNCH + ).setLaunchLocation(LAUNCH_LOCATON_DIRECT_SHARE) + .build()); + } + void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) { if (mRefinementResultReceiver != null) { mRefinementResultReceiver.destroy(); @@ -1128,6 +1159,10 @@ public class ChooserActivity extends ResolverActivity { return mBadgeContentDescription; } + public ChooserTarget getChooserTarget() { + return mChooserTarget; + } + @Override public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { return new ChooserTargetInfo(this, fillInIntent, flags); diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl index d0272e08b53a..e27ff0076054 100644 --- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl +++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package com.android.internal.inputmethod; import android.net.Uri; +import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodSubtype; import com.android.internal.inputmethod.IInputContentUriToken; @@ -39,4 +40,6 @@ interface IInputMethodPrivilegedOperations { boolean switchToNextInputMethod(boolean onlyCurrentIme); boolean shouldOfferSwitchingToNextInputMethod(); void notifyUserAction(); + void reportPreRendered(in EditorInfo info); + void applyImeVisibility(boolean setVisible); } diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java index 8978496073e5..d42c607b98bf 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java +++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java @@ -23,6 +23,7 @@ import android.net.Uri; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; +import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodSubtype; import com.android.internal.annotations.GuardedBy; @@ -347,4 +348,40 @@ public final class InputMethodPrivilegedOperations { throw e.rethrowFromSystemServer(); } } + + /** + * Calls {@link IInputMethodPrivilegedOperations#reportPreRendered(info)}. + * + * @param info {@link EditorInfo} of the currently rendered {@link TextView}. + */ + @AnyThread + public void reportPreRendered(EditorInfo info) { + final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); + if (ops == null) { + return; + } + try { + ops.reportPreRendered(info); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibility(boolean)}. + * + * @param setVisible {@code true} to set IME visible, else hidden. + */ + @AnyThread + public void applyImeVisibility(boolean setVisible) { + final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); + if (ops == null) { + return; + } + try { + ops.applyImeVisibility(setVisible); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl index 17b2bc46de36..2cfdaaa5ada6 100644 --- a/core/java/com/android/internal/view/IInputMethodClient.aidl +++ b/core/java/com/android/internal/view/IInputMethodClient.aidl @@ -16,6 +16,8 @@ package com.android.internal.view; +import android.view.inputmethod.EditorInfo; + import com.android.internal.view.InputBindResult; /** @@ -27,4 +29,6 @@ oneway interface IInputMethodClient { void onUnbindMethod(int sequence, int unbindReason); void setActive(boolean active, boolean fullscreen); void reportFullscreenMode(boolean fullscreen); + void reportPreRendered(in EditorInfo info); + void applyImeVisibility(boolean setVisible); } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 0466aaaee9fd..9afd5d0bff03 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -166,6 +166,7 @@ cc_library_shared { "android_media_AudioRecord.cpp", "android_media_AudioSystem.cpp", "android_media_AudioTrack.cpp", + "android_media_AudioAttributes.cpp", "android_media_DeviceCallback.cpp", "android_media_JetPlayer.cpp", "android_media_MediaMetricsJNI.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index f458299468b1..c90071a0de44 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -107,6 +107,7 @@ extern int register_android_media_AudioEffectDescriptor(JNIEnv *env); extern int register_android_media_AudioRecord(JNIEnv *env); extern int register_android_media_AudioSystem(JNIEnv *env); extern int register_android_media_AudioTrack(JNIEnv *env); +extern int register_android_media_AudioAttributes(JNIEnv *env); extern int register_android_media_MicrophoneInfo(JNIEnv *env); extern int register_android_media_JetPlayer(JNIEnv *env); extern int register_android_media_ToneGenerator(JNIEnv *env); @@ -1470,6 +1471,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_media_AudioSystem), REG_JNI(register_android_media_AudioRecord), REG_JNI(register_android_media_AudioTrack), + REG_JNI(register_android_media_AudioAttributes), REG_JNI(register_android_media_JetPlayer), REG_JNI(register_android_media_MicrophoneInfo), REG_JNI(register_android_media_RemoteDisplay), diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 5de088397690..c74797b60e69 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -93,6 +93,11 @@ public: mBitmap->setAlphaType(alphaType); } + void setColorSpace(sk_sp<SkColorSpace> colorSpace) { + assertValid(); + mBitmap->setColorSpace(colorSpace); + } + const SkImageInfo& info() { if (mBitmap) { return mBitmap->info(); @@ -959,6 +964,12 @@ static jboolean Bitmap_getColorSpace(JNIEnv* env, jobject, jlong bitmapHandle, return JNI_TRUE; } +static void Bitmap_setColorSpace(JNIEnv* env, jobject, jlong bitmapHandle, jlong colorSpacePtr) { + LocalScopedBitmap bitmapHolder(bitmapHandle); + sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpacePtr); + bitmapHolder->setColorSpace(cs); +} + /////////////////////////////////////////////////////////////////////////////// static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle, @@ -1239,6 +1250,7 @@ static const JNINativeMethod gBitmapMethods[] = { { "nativeCreateGraphicBufferHandle", "(J)Landroid/graphics/GraphicBuffer;", (void*) Bitmap_createGraphicBufferHandle }, { "nativeGetColorSpace", "(J[F[F)Z", (void*)Bitmap_getColorSpace }, + { "nativeSetColorSpace", "(JJ)V", (void*)Bitmap_setColorSpace }, { "nativeIsSRGB", "(J)Z", (void*)Bitmap_isSRGB }, { "nativeIsSRGBLinear", "(J)Z", (void*)Bitmap_isSRGBLinear}, { "nativeCopyColorSpace", "(JJ)V", diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 7d0d3d8442a1..3f9ec452749f 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -549,7 +549,7 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fi // if we only close the file descriptor and not the file object it is used to // create. If we don't explicitly clean up the file (which in turn closes the // descriptor) the buffers allocated internally by fseek will be leaked. - int dupDescriptor = dup(descriptor); + int dupDescriptor = fcntl(descriptor, F_DUPFD_CLOEXEC, 0); FILE* file = fdopen(dupDescriptor, "r"); if (file == NULL) { diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp index 5f126653600e..72e14e79f4f5 100644 --- a/core/jni/android/graphics/ImageDecoder.cpp +++ b/core/jni/android/graphics/ImageDecoder.cpp @@ -132,7 +132,7 @@ static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, "broken file descriptor; fstat returned -1", nullptr, source); } - int dupDescriptor = dup(descriptor); + int dupDescriptor = fcntl(descriptor, F_DUPFD_CLOEXEC, 0); FILE* file = fdopen(dupDescriptor, "r"); if (file == NULL) { close(dupDescriptor); diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp index 7738d849be39..f40b461a6dfd 100644 --- a/core/jni/android_content_res_ApkAssets.cpp +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -72,7 +72,7 @@ static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descri return 0; } - unique_fd dup_fd(::dup(fd)); + unique_fd dup_fd(::fcntl(fd, F_DUPFD_CLOEXEC, 0)); if (dup_fd < 0) { jniThrowIOException(env, errno); return 0; diff --git a/core/jni/android_ddm_DdmHandleNativeHeap.cpp b/core/jni/android_ddm_DdmHandleNativeHeap.cpp index 2f25d8f2d3a4..e22f581e14e2 100644 --- a/core/jni/android_ddm_DdmHandleNativeHeap.cpp +++ b/core/jni/android_ddm_DdmHandleNativeHeap.cpp @@ -54,7 +54,7 @@ struct Header { namespace android { static void ReadFile(const char* path, String8& s) { - int fd = open(path, O_RDONLY); + int fd = open(path, O_RDONLY | O_CLOEXEC); if (fd != -1) { char bytes[1024]; ssize_t byteCount; diff --git a/core/jni/android_hardware_HardwareBuffer.cpp b/core/jni/android_hardware_HardwareBuffer.cpp index 8174a4184c83..2d1fec819e97 100644 --- a/core/jni/android_hardware_HardwareBuffer.cpp +++ b/core/jni/android_hardware_HardwareBuffer.cpp @@ -104,6 +104,21 @@ static jlong android_hardware_HardwareBuffer_getNativeFinalizer(JNIEnv* env, job return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyWrapper)); } +static jboolean android_hardware_HardwareBuffer_isSupported(JNIEnv* env, jobject clazz, + jint width, jint height, jint format, jint layers, jlong usage) { + + AHardwareBuffer_Desc desc; + desc.width = width; + desc.height = height; + desc.format = format; + desc.layers = layers; + desc.usage = usage; + desc.stride = 0; + desc.rfu0 = 0; + desc.rfu1 = 0; + return AHardwareBuffer_isSupported(&desc); +} + //---------------------------------------------------------------------------- // Accessors // ---------------------------------------------------------------------------- @@ -234,6 +249,8 @@ static const JNINativeMethod gMethods[] = { (void*) android_hardware_HardwareBuffer_write }, { "nReadHardwareBufferFromParcel", "(Landroid/os/Parcel;)J", (void*) android_hardware_HardwareBuffer_read }, + { "nIsSupported", "(IIIIJ)Z", + (void*) android_hardware_HardwareBuffer_isSupported }, // --------------- @FastNative ---------------------- { "nGetWidth", "(J)I", (void*) android_hardware_HardwareBuffer_getWidth }, diff --git a/core/jni/android_hardware_SerialPort.cpp b/core/jni/android_hardware_SerialPort.cpp index 190ddced7312..3ff24468e3aa 100644 --- a/core/jni/android_hardware_SerialPort.cpp +++ b/core/jni/android_hardware_SerialPort.cpp @@ -134,7 +134,7 @@ android_hardware_SerialPort_open(JNIEnv *env, jobject thiz, jobject fileDescript int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); // duplicate the file descriptor, since ParcelFileDescriptor will eventually close its copy - fd = dup(fd); + fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); if (fd < 0) { jniThrowException(env, "java/io/IOException", "Could not open serial port"); return; diff --git a/core/jni/android_hardware_UsbDeviceConnection.cpp b/core/jni/android_hardware_UsbDeviceConnection.cpp index d953aee6b753..b885c285e380 100644 --- a/core/jni/android_hardware_UsbDeviceConnection.cpp +++ b/core/jni/android_hardware_UsbDeviceConnection.cpp @@ -49,7 +49,7 @@ android_hardware_UsbDeviceConnection_open(JNIEnv *env, jobject thiz, jstring dev { int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); // duplicate the file descriptor, since ParcelFileDescriptor will eventually close its copy - fd = dup(fd); + fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); if (fd < 0) return JNI_FALSE; diff --git a/core/jni/android_media_AudioAttributes.cpp b/core/jni/android_media_AudioAttributes.cpp new file mode 100644 index 000000000000..bfe61626c1a2 --- /dev/null +++ b/core/jni/android_media_AudioAttributes.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 + +#define LOG_TAG "AudioAttributes-JNI" + +#include <inttypes.h> +#include <jni.h> +#include <nativehelper/JNIHelp.h> +#include "core_jni_helpers.h" + +#include <utils/Log.h> +#include <vector> + +#include <nativehelper/ScopedLocalRef.h> +#include <nativehelper/ScopedUtfChars.h> + +#include "android_media_AudioAttributes.h" +#include "android_media_AudioErrors.h" + +// ---------------------------------------------------------------------------- + +using namespace android; + +// ---------------------------------------------------------------------------- +static const char* const kClassPathName = "android/media/AudioAttributes"; + +static jclass gAudioAttributesClass; +static struct { + jfieldID mUsage; // AudioAttributes.mUsage + jfieldID mSource; // AudioAttributes.mSource + jfieldID mContentType; // AudioAttributes.mContentType + jfieldID mFlags; // AudioAttributes.mFlags + jfieldID mFormattedTags; // AudioAttributes.mFormattedTags +} gAudioAttributesFields; + +static jclass gAudioAttributesBuilderClass; +static jmethodID gAudioAttributesBuilderCstor; +static struct { + jmethodID build; + jmethodID setUsage; + jmethodID setInternalCapturePreset; + jmethodID setContentType; + jmethodID setFlags; + jmethodID addTag; +} gAudioAttributesBuilderMethods; + + +static jint nativeAudioAttributesFromJavaAudioAttributes( + JNIEnv* env, jobject jAudioAttributes, audio_attributes_t *aa) +{ + if (env == nullptr) { + return AUDIO_JAVA_DEAD_OBJECT; + } + if (jAudioAttributes == nullptr) { + ALOGE("Invalid AudioAttributes java object"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + if (!env->IsInstanceOf(jAudioAttributes, gAudioAttributesClass)) { + return (jint)AUDIO_JAVA_BAD_VALUE; + } + const jstring jtags = + (jstring) env->GetObjectField(jAudioAttributes, gAudioAttributesFields.mFormattedTags); + if (jtags == nullptr) { + return AUDIO_JAVA_NO_INIT; + } + const char* tags = env->GetStringUTFChars(jtags, NULL); + // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it + strncpy(aa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1); + env->ReleaseStringUTFChars(jtags, tags); + + // Record ? + aa->source = (audio_source_t) env->GetIntField(jAudioAttributes, + gAudioAttributesFields.mSource); + // Track ? + aa->usage = (audio_usage_t) env->GetIntField(jAudioAttributes, gAudioAttributesFields.mUsage); + + aa->content_type = + (audio_content_type_t) env->GetIntField(jAudioAttributes, + gAudioAttributesFields.mContentType); + + aa->flags = (audio_flags_mask_t)env->GetIntField(jAudioAttributes, + gAudioAttributesFields.mFlags); + + ALOGV("AudioAttributes for usage=%d content=%d source=%d tags=%s flags=%08x tags=%s", + aa->usage, aa->content_type, aa->source, aa->tags, aa->flags, aa->tags); + return (jint)AUDIO_JAVA_SUCCESS; +} + +static jint nativeAudioAttributesToJavaAudioAttributes( + JNIEnv* env, jobject *jAudioAttributes, const audio_attributes_t &attributes) +{ + ScopedLocalRef<jobject> jAttributeBuilder(env, env->NewObject(gAudioAttributesBuilderClass, + gAudioAttributesBuilderCstor)); + if (jAttributeBuilder.get() == nullptr) { + return (jint)AUDIO_JAVA_ERROR; + } + env->CallObjectMethod(jAttributeBuilder.get(), + gAudioAttributesBuilderMethods.setUsage, + attributes.usage); + env->CallObjectMethod(jAttributeBuilder.get(), + gAudioAttributesBuilderMethods.setInternalCapturePreset, + attributes.source); + env->CallObjectMethod(jAttributeBuilder.get(), + gAudioAttributesBuilderMethods.setContentType, + attributes.content_type); + env->CallObjectMethod(jAttributeBuilder.get(), + gAudioAttributesBuilderMethods.setFlags, + attributes.flags); + env->CallObjectMethod(jAttributeBuilder.get(), + gAudioAttributesBuilderMethods.addTag, + env->NewStringUTF(attributes.tags)); + + *jAudioAttributes = env->CallObjectMethod(jAttributeBuilder.get(), + gAudioAttributesBuilderMethods.build); + return (jint)AUDIO_JAVA_SUCCESS; +} + +// ---------------------------------------------------------------------------- +JNIAudioAttributeHelper::UniqueAaPtr JNIAudioAttributeHelper::makeUnique() +{ + audio_attributes_t *aa = new (calloc(1, sizeof(audio_attributes_t))) + audio_attributes_t{AUDIO_ATTRIBUTES_INITIALIZER}; + return UniqueAaPtr{aa, free}; +} + +jint JNIAudioAttributeHelper::nativeFromJava(JNIEnv* env, jobject jAudioAttributes, + audio_attributes_t *paa) +{ + return nativeAudioAttributesFromJavaAudioAttributes(env, jAudioAttributes, paa); +} + +jint JNIAudioAttributeHelper::nativeToJava( + JNIEnv* env, jobject *jAudioAttributes, const audio_attributes_t &attributes) +{ + return nativeAudioAttributesToJavaAudioAttributes(env, jAudioAttributes, attributes); +} + +jint JNIAudioAttributeHelper::getJavaArray( + JNIEnv* env, jobjectArray *jAudioAttributeArray, jint numAudioAttributes) +{ + *jAudioAttributeArray = env->NewObjectArray(numAudioAttributes, gAudioAttributesClass, NULL); + return jAudioAttributeArray == NULL? (jint)AUDIO_JAVA_ERROR : (jint)AUDIO_JAVA_SUCCESS; +} + +/* + * JNI registration. + */ +static const JNINativeMethod gMethods[] = { + // n/a +}; + +int register_android_media_AudioAttributes(JNIEnv *env) +{ + jclass audioAttributesClass = FindClassOrDie(env, kClassPathName); + gAudioAttributesClass = MakeGlobalRefOrDie(env, audioAttributesClass); + gAudioAttributesFields.mUsage = GetFieldIDOrDie(env, audioAttributesClass, "mUsage", "I"); + gAudioAttributesFields.mSource = GetFieldIDOrDie(env, audioAttributesClass, "mSource", "I"); + gAudioAttributesFields.mContentType = + GetFieldIDOrDie(env, audioAttributesClass, "mContentType", "I"); + gAudioAttributesFields.mFlags = GetFieldIDOrDie(env, audioAttributesClass, "mFlags", "I"); + gAudioAttributesFields.mFormattedTags = + GetFieldIDOrDie(env, audioAttributesClass, "mFormattedTags", "Ljava/lang/String;"); + + jclass audioAttributesBuilderClass = FindClassOrDie( + env, "android/media/AudioAttributes$Builder"); + gAudioAttributesBuilderClass = MakeGlobalRefOrDie(env, audioAttributesBuilderClass); + gAudioAttributesBuilderCstor = GetMethodIDOrDie( + env, audioAttributesBuilderClass, "<init>", "()V"); + gAudioAttributesBuilderMethods.build = GetMethodIDOrDie( + env, audioAttributesBuilderClass, "build", "()Landroid/media/AudioAttributes;"); + gAudioAttributesBuilderMethods.setUsage = GetMethodIDOrDie( + env, audioAttributesBuilderClass, "setUsage", + "(I)Landroid/media/AudioAttributes$Builder;"); + gAudioAttributesBuilderMethods.setInternalCapturePreset = GetMethodIDOrDie( + env, audioAttributesBuilderClass, "setInternalCapturePreset", + "(I)Landroid/media/AudioAttributes$Builder;"); + gAudioAttributesBuilderMethods.setContentType = GetMethodIDOrDie( + env, audioAttributesBuilderClass, "setContentType", + "(I)Landroid/media/AudioAttributes$Builder;"); + gAudioAttributesBuilderMethods.setFlags = GetMethodIDOrDie( + env, audioAttributesBuilderClass, "setFlags", + "(I)Landroid/media/AudioAttributes$Builder;"); + gAudioAttributesBuilderMethods.addTag = GetMethodIDOrDie( + env, audioAttributesBuilderClass, "addTag", + "(Ljava/lang/String;)Landroid/media/AudioAttributes$Builder;"); + + env->DeleteLocalRef(audioAttributesClass); + + return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); +} diff --git a/core/jni/android_media_AudioAttributes.h b/core/jni/android_media_AudioAttributes.h new file mode 100644 index 000000000000..c55835222086 --- /dev/null +++ b/core/jni/android_media_AudioAttributes.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "jni.h" + +#include <memory.h> + +#include <system/audio.h> + +namespace android { + +class JNIAudioAttributeHelper +{ +public: + using UniqueAaPtr = std::unique_ptr<audio_attributes_t, decltype(free)*>; + + /** + * @brief makeUnique helper to prevent leak + * @return a unique ptr of 0-initialized native audio attributes structure + */ + static UniqueAaPtr makeUnique(); + + /** + * @brief nativeFromJava Gets the underlying AudioAttributes from an AudioAttributes Java + * object. + * @param env + * @param jAudioAttributes JAVA AudioAttribute object + * @param paa native AudioAttribute pointer + * @return AUDIO_JAVA_SUCCESS on success, error code otherwise + */ + static jint nativeFromJava( + JNIEnv* env, jobject jAudioAttributes, audio_attributes_t *attributes); + + /** + * @brief nativeToJava AudioAttributes Java object from a native AudioAttributes. + * @param env + * @param jAudioAttributes JAVA AudioAttribute object + * @param attributes native AudioAttribute + * @return AUDIO_JAVA_SUCCESS on success, error code otherwise + */ + static jint nativeToJava( + JNIEnv* env, jobject *jAudioAttributes, const audio_attributes_t &attributes); + + /** + * @brief getJavaArray: creates an array of JAVA AudioAttributes objects + * @param env + * @param jAudioAttributeArray + * @param numAudioAttributes + * @return Array of AudioAttributes objects + */ + static jint getJavaArray( + JNIEnv* env, jobjectArray *jAudioAttributeArray, jint numAudioAttributes); +}; + +}; // namespace android diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp index 67c306413bb2..02dffdc2ca25 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -35,6 +35,7 @@ #include "android_media_DeviceCallback.h" #include "android_media_MediaMetricsJNI.h" #include "android_media_MicrophoneInfo.h" +#include "android_media_AudioAttributes.h" // ---------------------------------------------------------------------------- @@ -42,7 +43,6 @@ using namespace android; // ---------------------------------------------------------------------------- static const char* const kClassPathName = "android/media/AudioRecord"; -static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes"; static jclass gArrayListClass; static struct { @@ -56,12 +56,6 @@ struct audio_record_fields_t { jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data jfieldID nativeDeviceCallback; // provides access to the JNIDeviceCallback instance }; -struct audio_attributes_fields_t { - jfieldID fieldRecSource; // AudioAttributes.mSource - jfieldID fieldFlags; // AudioAttributes.mFlags - jfieldID fieldFormattedTags;// AudioAttributes.mFormattedTags -}; -static audio_attributes_fields_t javaAudioAttrFields; static audio_record_fields_t javaAudioRecordFields; static struct { jfieldID fieldFramePosition; // AudioTimestamp.framePosition @@ -213,7 +207,6 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); nSession = NULL; - audio_attributes_t *paa = NULL; sp<AudioRecord> lpRecorder = 0; audiorecord_callback_cookie *lpCallbackData = NULL; @@ -275,15 +268,11 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str())); // read the AudioAttributes values - paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t)); - const jstring jtags = - (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags); - const char* tags = env->GetStringUTFChars(jtags, NULL); - // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it - strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1); - env->ReleaseStringUTFChars(jtags, tags); - paa->source = (audio_source_t) env->GetIntField(jaa, javaAudioAttrFields.fieldRecSource); - paa->flags = (audio_flags_mask_t)env->GetIntField(jaa, javaAudioAttrFields.fieldFlags); + auto paa = JNIAudioAttributeHelper::makeUnique(); + jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); + if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + return jStatus; + } ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags); audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE; @@ -311,7 +300,7 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, AudioRecord::TRANSFER_DEFAULT, flags, -1, -1, // default uid, pid - paa); + paa.get()); if (status != NO_ERROR) { ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.", @@ -368,19 +357,10 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize() env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData); - if (paa != NULL) { - // audio attributes were copied in AudioRecord creation - free(paa); - paa = NULL; - } - return (jint) AUDIO_JAVA_SUCCESS; // failure: native_init_failure: - if (paa != NULL) { - free(paa); - } env->DeleteGlobalRef(lpCallbackData->audioRecord_class); env->DeleteGlobalRef(lpCallbackData->audioRecord_ref); delete lpCallbackData; @@ -972,13 +952,6 @@ int register_android_media_AudioRecord(JNIEnv *env) javaAudioRecordFields.nativeDeviceCallback = GetFieldIDOrDie(env, audioRecordClass, JAVA_NATIVEDEVICECALLBACK_FIELD_NAME, "J"); - // Get the AudioAttributes class and fields - jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName); - javaAudioAttrFields.fieldRecSource = GetFieldIDOrDie(env, audioAttrClass, "mSource", "I"); - javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I"); - javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env, - audioAttrClass, "mFormattedTags", "Ljava/lang/String;"); - // Get the RecordTimestamp class and fields jclass audioTimestampClass = FindClassOrDie(env, "android/media/AudioTimestamp"); javaAudioTimestampFields.fieldFramePosition = diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 0d8ede794b73..34abcd889644 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -36,6 +36,7 @@ #include "android_media_AudioFormat.h" #include "android_media_AudioErrors.h" #include "android_media_MicrophoneInfo.h" +#include "android_media_AudioAttributes.h" // ---------------------------------------------------------------------------- @@ -153,15 +154,6 @@ static struct { jfieldID mRule; } gAudioMixMatchCriterionFields; -static jclass gAudioAttributesClass; -static struct { - jfieldID mUsage; - jfieldID mSource; - jfieldID mContentType; - jfieldID mFlags; - jfieldID mFormattedTags; -} gAudioAttributesFields; - static const char* const kEventHandlerClassPathName = "android/media/AudioPortEventHandler"; static struct { @@ -705,27 +697,6 @@ static void convertAudioGainConfigToNative(JNIEnv *env, env->DeleteLocalRef(jValues); } -static jint convertAudioAttributesToNative(JNIEnv *env, - audio_attributes_t *nAudioAttributes, - const jobject jAudioAttributes) -{ - nAudioAttributes->usage = (audio_usage_t)env->GetIntField(jAudioAttributes, - gAudioAttributesFields.mUsage); - nAudioAttributes->source = (audio_source_t)env->GetIntField(jAudioAttributes, - gAudioAttributesFields.mSource); - nAudioAttributes->content_type = (audio_content_type_t)env->GetIntField(jAudioAttributes, - gAudioAttributesFields.mContentType); - nAudioAttributes->flags = env->GetIntField(jAudioAttributes, - gAudioAttributesFields.mFlags); - const jstring jtags = (jstring)env->GetObjectField(jAudioAttributes, - gAudioAttributesFields.mFormattedTags); - const char *tags = env->GetStringUTFChars(jtags, NULL); - strncpy(nAudioAttributes->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1); - env->ReleaseStringUTFChars(jtags, tags); - return (jint)AUDIO_JAVA_SUCCESS; -} - - static jint convertAudioPortConfigToNative(JNIEnv *env, struct audio_port_config *nAudioPortConfig, const jobject jAudioPortConfig, @@ -1705,22 +1676,19 @@ android_media_AudioSystem_startAudioSource(JNIEnv *env, jobject clazz, if (!env->IsInstanceOf(jAudioPortConfig, gAudioPortConfigClass)) { return AUDIO_JAVA_BAD_VALUE; } - if (!env->IsInstanceOf(jAudioAttributes, gAudioAttributesClass)) { - return AUDIO_JAVA_BAD_VALUE; - } struct audio_port_config nAudioPortConfig = {}; jint jStatus = convertAudioPortConfigToNativeWithDevicePort(env, &nAudioPortConfig, jAudioPortConfig, false); if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } - audio_attributes_t nAudioAttributes = {}; - jStatus = convertAudioAttributesToNative(env, &nAudioAttributes, jAudioAttributes); - if (jStatus != AUDIO_JAVA_SUCCESS) { + auto paa = JNIAudioAttributeHelper::makeUnique(); + jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jAudioAttributes, paa.get()); + if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { return jStatus; } audio_port_handle_t handle; - status_t status = AudioSystem::startAudioSource(&nAudioPortConfig, &nAudioAttributes, &handle); + status_t status = AudioSystem::startAudioSource(&nAudioPortConfig, paa.get(), &handle); ALOGV("AudioSystem::startAudioSource() returned %d handle %d", status, handle); return handle > 0 ? handle : nativeToJavaStatus(status); } @@ -1833,12 +1801,16 @@ static jint convertAudioMixToNative(JNIEnv *env, case RULE_MATCH_ATTRIBUTE_USAGE: case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: { jobject jAttributes = env->GetObjectField(jCriterion, gAudioMixMatchCriterionFields.mAttr); + + auto paa = JNIAudioAttributeHelper::makeUnique(); + jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jAttributes, paa.get()); + if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + return jStatus; + } if (match_rule == RULE_MATCH_ATTRIBUTE_USAGE) { - nCriterion.mValue.mUsage = (audio_usage_t)env->GetIntField(jAttributes, - gAudioAttributesFields.mUsage); + nCriterion.mValue.mUsage = paa->usage; } else { - nCriterion.mValue.mSource = (audio_source_t)env->GetIntField(jAttributes, - gAudioAttributesFields.mSource); + nCriterion.mValue.mSource = paa->source; } env->DeleteLocalRef(jAttributes); } @@ -2395,17 +2367,6 @@ int register_android_media_AudioSystem(JNIEnv *env) "I"); gAudioMixMatchCriterionFields.mRule = GetFieldIDOrDie(env, audioMixMatchCriterionClass, "mRule", "I"); - - jclass audioAttributesClass = FindClassOrDie(env, "android/media/AudioAttributes"); - gAudioAttributesClass = MakeGlobalRefOrDie(env, audioAttributesClass); - gAudioAttributesFields.mUsage = GetFieldIDOrDie(env, audioAttributesClass, "mUsage", "I"); - gAudioAttributesFields.mSource = GetFieldIDOrDie(env, audioAttributesClass, "mSource", "I"); - gAudioAttributesFields.mContentType = GetFieldIDOrDie(env, - audioAttributesClass, "mContentType", "I"); - gAudioAttributesFields.mFlags = GetFieldIDOrDie(env, audioAttributesClass, "mFlags", "I"); - gAudioAttributesFields.mFormattedTags = GetFieldIDOrDie(env, - audioAttributesClass, "mFormattedTags", "Ljava/lang/String;"); - // AudioTrackRoutingProxy methods gClsAudioTrackRoutingProxy = android::FindClassOrDie(env, "android/media/AudioTrackRoutingProxy"); diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index a0d99ec95b55..f9f28dabc694 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -37,6 +37,7 @@ #include "android_media_PlaybackParams.h" #include "android_media_DeviceCallback.h" #include "android_media_VolumeShaper.h" +#include "android_media_AudioAttributes.h" #include <cinttypes> @@ -48,7 +49,6 @@ using ::android::media::VolumeShaper; // ---------------------------------------------------------------------------- static const char* const kClassPathName = "android/media/AudioTrack"; -static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes"; struct audio_track_fields_t { // these fields provide access from C++ to the... @@ -57,14 +57,7 @@ struct audio_track_fields_t { jfieldID jniData; // stores in Java additional resources used by the native AudioTrack jfieldID fieldStreamType; // ... mStreamType field in the AudioTrack Java object }; -struct audio_attributes_fields_t { - jfieldID fieldUsage; // AudioAttributes.mUsage - jfieldID fieldContentType; // AudioAttributes.mContentType - jfieldID fieldFlags; // AudioAttributes.mFlags - jfieldID fieldFormattedTags;// AudioAttributes.mFormattedTags -}; static audio_track_fields_t javaAudioTrackFields; -static audio_attributes_fields_t javaAudioAttrFields; static PlaybackParams::fields_t gPlaybackParamsFields; static VolumeShaperHelper::fields_t gVolumeShaperFields; @@ -249,8 +242,6 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job AudioTrackJniStorage* lpJniStorage = NULL; - audio_attributes_t *paa = NULL; - jclass clazz = env->GetObjectClass(thiz); if (clazz == NULL) { ALOGE("Can't find %s when setting up callback.", kClassPathName); @@ -304,18 +295,11 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job lpTrack = new AudioTrack(); // read the AudioAttributes values - paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t)); - const jstring jtags = - (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags); - const char* tags = env->GetStringUTFChars(jtags, NULL); - // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it - strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1); - env->ReleaseStringUTFChars(jtags, tags); - paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage); - paa->content_type = - (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType); - paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags); - + auto paa = JNIAudioAttributeHelper::makeUnique(); + jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); + if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + return jStatus; + } ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s", paa->usage, paa->content_type, paa->flags, paa->tags); @@ -357,7 +341,7 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK : AudioTrack::TRANSFER_SYNC, offload ? &offloadInfo : NULL, -1, -1, // default uid, pid values - paa); + paa.get()); break; @@ -384,7 +368,7 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job AudioTrack::TRANSFER_SHARED, NULL, // default offloadInfo -1, -1, // default uid, pid values - paa); + paa.get()); break; default: @@ -452,20 +436,11 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job // since we had audio attributes, the stream type was derived from them during the // creation of the native AudioTrack: push the same value to the Java object env->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType()); - if (paa != NULL) { - // audio attributes were copied in AudioTrack creation - free(paa); - paa = NULL; - } - return (jint) AUDIO_JAVA_SUCCESS; // failures: native_init_failure: - if (paa != NULL) { - free(paa); - } if (nSession != NULL) { env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); } @@ -1470,17 +1445,6 @@ int register_android_media_AudioTrack(JNIEnv *env) env->DeleteLocalRef(audioTrackClass); - // Get the AudioAttributes class and fields - jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName); - javaAudioAttrFields.fieldUsage = GetFieldIDOrDie(env, audioAttrClass, "mUsage", "I"); - javaAudioAttrFields.fieldContentType = GetFieldIDOrDie(env, - audioAttrClass, "mContentType", "I"); - javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I"); - javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env, - audioAttrClass, "mFormattedTags", "Ljava/lang/String;"); - - env->DeleteLocalRef(audioAttrClass); - // initialize PlaybackParams field info gPlaybackParamsFields.init(env); diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 783724853b8a..e9035ed102ae 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -646,7 +646,7 @@ static bool openFile(JNIEnv* env, jobject fileDescriptor, UniqueFile& fp) } /* dup() the descriptor so we don't close the original with fclose() */ - int fd = dup(origFd); + int fd = fcntl(origFd, F_DUPFD_CLOEXEC, 0); if (fd < 0) { ALOGW("dup(%d) failed: %s\n", origFd, strerror(errno)); jniThrowRuntimeException(env, "dup() failed"); diff --git a/core/jni/android_text_Hyphenator.cpp b/core/jni/android_text_Hyphenator.cpp index 6f9cc22fb3ab..de307737493e 100644 --- a/core/jni/android_text_Hyphenator.cpp +++ b/core/jni/android_text_Hyphenator.cpp @@ -37,7 +37,7 @@ static std::string buildFileName(const std::string& locale) { static const uint8_t* mmapPatternFile(const std::string& locale) { const std::string hyFilePath = buildFileName(locale); - const int fd = open(hyFilePath.c_str(), O_RDONLY); + const int fd = open(hyFilePath.c_str(), O_RDONLY | O_CLOEXEC); if (fd == -1) { return nullptr; // Open failed. } diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index f201ceb40ff4..df98cdc9f1f9 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -1184,7 +1184,7 @@ static int getprocname(pid_t pid, char *buf, size_t len) { FILE *f; snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid); - f = fopen(filename, "r"); + f = fopen(filename, "re"); if (!f) { *buf = '\0'; return 1; diff --git a/core/jni/android_util_FileObserver.cpp b/core/jni/android_util_FileObserver.cpp index d25192a57228..c64c21244fe0 100644 --- a/core/jni/android_util_FileObserver.cpp +++ b/core/jni/android_util_FileObserver.cpp @@ -42,7 +42,7 @@ static jmethodID method_onEvent; static jint android_os_fileobserver_init(JNIEnv* env, jobject object) { #if defined(__linux__) - return (jint)inotify_init(); + return (jint)inotify_init1(IN_CLOEXEC); #else return -1; #endif diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index b8139a712fd7..f9407f43af9f 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -131,6 +131,7 @@ int android_view_Surface_mapPublicFormatToHalFormat(PublicFormat f) { switch(f) { case PublicFormat::JPEG: case PublicFormat::DEPTH_POINT_CLOUD: + case PublicFormat::DEPTH_JPEG: return HAL_PIXEL_FORMAT_BLOB; case PublicFormat::DEPTH16: return HAL_PIXEL_FORMAT_Y16; @@ -161,6 +162,8 @@ android_dataspace android_view_Surface_mapPublicFormatToHalDataspace( case PublicFormat::NV21: case PublicFormat::YV12: return HAL_DATASPACE_V0_JFIF; + case PublicFormat::DEPTH_JPEG: + return static_cast<android_dataspace> (HAL_DATASPACE_DYNAMIC_DEPTH); default: // Most formats map to UNKNOWN return HAL_DATASPACE_UNKNOWN; @@ -223,8 +226,12 @@ PublicFormat android_view_Surface_mapHalFormatDataspaceToPublicFormat( case HAL_DATASPACE_V0_JFIF: return PublicFormat::JPEG; default: - // Assume otherwise-marked blobs are also JPEG - return PublicFormat::JPEG; + if (dataSpace == static_cast<android_dataspace>(HAL_DATASPACE_DYNAMIC_DEPTH)) { + return PublicFormat::DEPTH_JPEG; + } else { + // Assume otherwise-marked blobs are also JPEG + return PublicFormat::JPEG; + } } break; case HAL_PIXEL_FORMAT_BGRA_8888: diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp index 5eefc8196d30..dc536b2d2dee 100644 --- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp +++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp @@ -125,7 +125,7 @@ isFileDifferent(const char* filePath, uint32_t fileSize, time_t modifiedTime, return true; } - int fd = TEMP_FAILURE_RETRY(open(filePath, O_RDONLY)); + int fd = TEMP_FAILURE_RETRY(open(filePath, O_RDONLY | O_CLOEXEC)); if (fd < 0) { ALOGV("Couldn't open file %s: %s", filePath, strerror(errno)); return true; @@ -565,7 +565,7 @@ com_android_internal_content_NativeLibraryHelper_openApkFd(JNIEnv *env, jclass, return 0; } - int dupedFd = dup(fd); + int dupedFd = fcntl(fd, F_DUPFD_CLOEXEC, 0); if (dupedFd == -1) { jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Failed to dup FileDescriptor: %s", strerror(errno)); diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp index 24bafca9c386..8259ffcd419d 100644 --- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp +++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp @@ -95,7 +95,7 @@ static jlongArray get_long_array(JNIEnv* env, jobject obj, jfieldID field, int s static int legacyReadNetworkStatsDetail(std::vector<stats_line>* lines, const std::vector<std::string>& limitIfaces, int limitTag, int limitUid, const char* path) { - FILE* fp = fopen(path, "r"); + FILE* fp = fopen(path, "re"); if (fp == NULL) { return -1; } diff --git a/core/jni/com_android_internal_os_AtomicDirectory.cpp b/core/jni/com_android_internal_os_AtomicDirectory.cpp index 50b2288a614a..76b0fc167264 100644 --- a/core/jni/com_android_internal_os_AtomicDirectory.cpp +++ b/core/jni/com_android_internal_os_AtomicDirectory.cpp @@ -29,7 +29,7 @@ static jint com_android_internal_os_AtomicDirectory_getDirectoryFd(JNIEnv* env, return -1; } int fd; - if ((fd = TEMP_FAILURE_RETRY(open(path8.c_str(), O_DIRECTORY | O_RDONLY))) == -1) { + if ((fd = TEMP_FAILURE_RETRY(open(path8.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC))) == -1) { ALOGE("Cannot open directory %s, error: %s\n", path8.c_str(), strerror(errno)); return -1; } diff --git a/core/jni/include/android_runtime/android_view_Surface.h b/core/jni/include/android_runtime/android_view_Surface.h index 36487b3b40b3..984e942207c1 100644 --- a/core/jni/include/android_runtime/android_view_Surface.h +++ b/core/jni/include/android_runtime/android_view_Surface.h @@ -57,7 +57,8 @@ enum class PublicFormat { YV12 = 0x32315659, Y8 = 0x20203859, // @hide Y16 = 0x20363159, // @hide - DEPTH16 = 0x44363159 + DEPTH16 = 0x44363159, + DEPTH_JPEG = 0x69656963, }; /* Gets the underlying ANativeWindow for a Surface. */ diff --git a/core/proto/Android.bp b/core/proto/Android.bp index 80cc2d4cab94..3b891d6b4947 100644 --- a/core/proto/Android.bp +++ b/core/proto/Android.bp @@ -21,7 +21,10 @@ cc_library_static { type: "lite", }, srcs: [ + "android/bluetooth/a2dp/enums.proto", "android/bluetooth/enums.proto", "android/bluetooth/hci/enums.proto", + "android/bluetooth/hfp/enums.proto", + "android/bluetooth/smp/enums.proto", ], } diff --git a/core/proto/android/app/job/enums.proto b/core/proto/android/app/job/enums.proto index bba832880669..f702b3e37bda 100644 --- a/core/proto/android/app/job/enums.proto +++ b/core/proto/android/app/job/enums.proto @@ -18,6 +18,9 @@ syntax = "proto2"; package android.app.job; +// This file is for JobScheduler enums inside the app directory. If you're +// adding enums for system-server-side code, use the file in +// frameworks/base/core/proto/android/server/job. option java_outer_classname = "JobProtoEnums"; option java_multiple_files = true; diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index eb716ac280e2..94a6734b69c8 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -2157,10 +2157,10 @@ enum PageId { // OS: Q ZEN_CUSTOM_SETTINGS_DIALOG = 1612; - // OPEN: Settings > Developer Options > Game Update Packages + // OPEN: Settings > Developer Options > Game Driver Preferences // CATEGORY: SETTINGS // OS: Q - SETTINGS_GUP_DASHBOARD = 1613; + SETTINGS_GAME_DRIVER_DASHBOARD = 1613; // OPEN: Settings > Accessibility > Vibration > Ring vibration // CATEGORY: SETTINGS diff --git a/core/proto/android/bluetooth/a2dp/enums.proto b/core/proto/android/bluetooth/a2dp/enums.proto new file mode 100644 index 000000000000..5a025bdd6c10 --- /dev/null +++ b/core/proto/android/bluetooth/a2dp/enums.proto @@ -0,0 +1,35 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +package android.bluetooth.a2dp; + +option java_outer_classname = "BluetoothA2dpProtoEnums"; +option java_multiple_files = true; + +// A2dp playback state enum, defined from: +// frameworks/base/core/java/android/bluetooth/BluetoothA2dp.java +enum PlaybackStateEnum { + PLAYBACK_STATE_UNKNOWN = 0; + PLAYBACK_STATE_PLAYING = 10; + PLAYBACK_STATE_NOT_PLAYING = 11; +} + +enum AudioCodingModeEnum { + AUDIO_CODING_MODE_UNKNOWN = 0; + AUDIO_CODING_MODE_HARDWARE = 1; + AUDIO_CODING_MODE_SOFTWARE = 2; +} diff --git a/core/proto/android/bluetooth/enums.proto b/core/proto/android/bluetooth/enums.proto index 76c240ecff4d..a88a06cf091c 100644 --- a/core/proto/android/bluetooth/enums.proto +++ b/core/proto/android/bluetooth/enums.proto @@ -56,3 +56,57 @@ enum LinkTypeEnum { LINK_TYPE_ACL = 0x01; LINK_TYPE_ESCO = 0x02; } + +enum DeviceInfoSrcEnum { + DEVICE_INFO_SRC_UNKNOWN = 0; + // Within Android Bluetooth stack + DEVICE_INFO_INTERNAL = 1; + // Outside Android Bluetooth stack + DEVICE_INFO_EXTERNAL = 2; +} + +enum DeviceTypeEnum { + DEVICE_TYPE_UNKNOWN = 0; + DEVICE_TYPE_CLASSIC = 1; + DEVICE_TYPE_LE = 2; + DEVICE_TYPE_DUAL = 3; +} + +// Defined in frameworks/base/core/java/android/bluetooth/BluetoothDevice.java +enum TransportTypeEnum { + TRANSPORT_TYPE_AUTO = 0; + TRANSPORT_TYPE_BREDR = 1; + TRANSPORT_TYPE_LE = 2; +} + +// Bond state enum +// Defined in frameworks/base/core/java/android/bluetooth/BluetoothDevice.java +enum BondStateEnum { + BOND_STATE_UNKNOWN = 0; + BOND_STATE_NONE = 10; + BOND_STATE_BONDING = 11; + BOND_STATE_BONDED = 12; +} + +// Sub states within the bonding general state +enum BondSubStateEnum { + BOND_SUB_STATE_UNKNOWN = 0; + BOND_SUB_STATE_LOCAL_OOB_DATA_PROVIDED = 1; + BOND_SUB_STATE_LOCAL_PIN_REQUESTED = 2; + BOND_SUB_STATE_LOCAL_PIN_REPLIED = 3; + BOND_SUB_STATE_LOCAL_SSP_REQUESTED = 4; + BOND_SUB_STATE_LOCAL_SSP_REPLIED = 5; +} + +enum UnbondReasonEnum { + UNBOND_REASON_UNKNOWN = 0; + UNBOND_REASON_AUTH_FAILED = 1; + UNBOND_REASON_AUTH_REJECTED = 2; + UNBOND_REASON_AUTH_CANCELED = 3; + UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; + UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; + UNBOND_REASON_AUTH_TIMEOUT = 6; + UNBOND_REASON_REPEATED_ATTEMPTS = 7; + UNBOND_REASON_REMOTE_AUTH_CANCELED = 8; + UNBOND_REASON_REMOVED = 9; +} diff --git a/core/proto/android/bluetooth/hci/enums.proto b/core/proto/android/bluetooth/hci/enums.proto index e1d96bbce68a..ef894e548351 100644 --- a/core/proto/android/bluetooth/hci/enums.proto +++ b/core/proto/android/bluetooth/hci/enums.proto @@ -351,7 +351,7 @@ enum EventEnum { EVT_COMMAND_COMPLETE = 0x0E; EVT_COMMAND_STATUS = 0x0F; EVT_HARDWARE_ERROR = 0x10; - EVT_FLUSH_OCCURED = 0x11; + EVT_FLUSH_OCCURRED = 0x11; EVT_ROLE_CHANGE = 0x12; EVT_NUM_COMPL_DATA_PKTS = 0x13; EVT_MODE_CHANGE = 0x14; @@ -517,3 +517,43 @@ enum StatusEnum { STATUS_CLB_DATA_TOO_BIG = 0x43; STATUS_OPERATION_CANCELED_BY_HOST = 0x44; // Not currently used in system/bt } + +enum BqrIdEnum { + BQR_ID_UNKNOWN = 0x00; + BQR_ID_MONITOR_MODE = 0x01; + BQR_ID_APPROACH_LSTO = 0x02; + BQR_ID_A2DP_AUDIO_CHOPPY = 0x03; + BQR_ID_SCO_VOICE_CHOPPY = 0x04; +} + +enum BqrPacketTypeEnum { + BQR_PACKET_TYPE_UNKNOWN = 0x00; + BQR_PACKET_TYPE_ID = 0x01; + BQR_PACKET_TYPE_NULL = 0x02; + BQR_PACKET_TYPE_POLL = 0x03; + BQR_PACKET_TYPE_FHS = 0x04; + BQR_PACKET_TYPE_HV1 = 0x05; + BQR_PACKET_TYPE_HV2 = 0x06; + BQR_PACKET_TYPE_HV3 = 0x07; + BQR_PACKET_TYPE_DV = 0x08; + BQR_PACKET_TYPE_EV3 = 0x09; + BQR_PACKET_TYPE_EV4 = 0x0A; + BQR_PACKET_TYPE_EV5 = 0x0B; + BQR_PACKET_TYPE_2EV3 = 0x0C; + BQR_PACKET_TYPE_2EV5 = 0x0D; + BQR_PACKET_TYPE_3EV3 = 0x0E; + BQR_PACKET_TYPE_3EV5 = 0x0F; + BQR_PACKET_TYPE_DM1 = 0x10; + BQR_PACKET_TYPE_DH1 = 0x11; + BQR_PACKET_TYPE_DM3 = 0x12; + BQR_PACKET_TYPE_DH3 = 0x13; + BQR_PACKET_TYPE_DM5 = 0x14; + BQR_PACKET_TYPE_DH5 = 0x15; + BQR_PACKET_TYPE_AUX1 = 0x16; + BQR_PACKET_TYPE_2DH1 = 0x17; + BQR_PACKET_TYPE_2DH3 = 0x18; + BQR_PACKET_TYPE_2DH5 = 0x19; + BQR_PACKET_TYPE_3DH1 = 0x1A; + BQR_PACKET_TYPE_3DH3 = 0x1B; + BQR_PACKET_TYPE_3DH5 = 0x1C; +} diff --git a/core/proto/android/bluetooth/smp/enums.proto b/core/proto/android/bluetooth/smp/enums.proto new file mode 100644 index 000000000000..c6747b78dc29 --- /dev/null +++ b/core/proto/android/bluetooth/smp/enums.proto @@ -0,0 +1,58 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +package android.bluetooth.smp; + +option java_outer_classname = "BluetoothSmpProtoEnums"; +option java_multiple_files = true; + +// SMP Pairing command codes +enum CommandEnum { + CMD_UNKNOWN = 0x00; + CMD_PAIRING_REQUEST = 0x01; + CMD_PAIRING_RESPONSE = 0x02; + CMD_PAIRING_CONFIRM = 0x03; + CMD_PAIRING_RANDOM = 0x04; + CMD_PAIRING_FAILED = 0x05; + CMD_ENCRYPTION_INFON = 0x06; + CMD_MASTER_IDENTIFICATION = 0x07; + CMD_IDENTITY_INFO = 0x08; + CMD_IDENTITY_ADDR_INFO = 0x09; + CMD_SIGNING_INFO = 0x0A; + CMD_SECURITY_REQUEST = 0x0B; + CMD_PAIRING_PUBLIC_KEY = 0x0C; + CMD_PAIRING_DHKEY_CHECK = 0x0D; + CMD_PAIRING_KEYPRESS_INFO = 0x0E; +} + +enum PairingFailReasonEnum { + PAIRING_FAIL_REASON_RESERVED = 0x00; + PAIRING_FAIL_REASON_PASSKEY_ENTRY = 0x01; + PAIRING_FAIL_REASON_OOB = 0x02; + PAIRING_FAIL_REASON_AUTH_REQ = 0x03; + PAIRING_FAIL_REASON_CONFIRM_VALUE = 0x04; + PAIRING_FAIL_REASON_PAIR_NOT_SUPPORT = 0x05; + PAIRING_FAIL_REASON_ENC_KEY_SIZE = 0x06; + PAIRING_FAIL_REASON_INVALID_CMD = 0x07; + PAIRING_FAIL_REASON_UNSPECIFIED = 0x08; + PAIRING_FAIL_REASON_REPEATED_ATTEMPTS = 0x09; + PAIRING_FAIL_REASON_INVALID_PARAMETERS = 0x0A; + PAIRING_FAIL_REASON_DHKEY_CHK = 0x0B; + PAIRING_FAIL_REASON_NUMERIC_COMPARISON = 0x0C; + PAIRING_FAIL_REASON_CLASSIC_PAIRING_IN_PROGR = 0x0D; + PAIRING_FAIL_REASON_XTRANS_DERIVE_NOT_ALLOW = 0x0E; +}
\ No newline at end of file diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index 7e7942e6ddf1..66cd1094825a 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -436,20 +436,20 @@ message GlobalSettingsProto { // Ordered GPU debug layer list for GLES // i.e. <layer1>:<layer2>:...:<layerN> optional SettingProto debug_layers_gles = 7; - // GUP - Game Update Package global preference for all Apps + // Game Driver - global preference for all Apps // 0 = Default - // 1 = All Apps use Game Update Package + // 1 = All Apps use Game Driver // 2 = All Apps use system graphics driver - optional SettingProto gup_dev_all_apps = 8; - // GUP - List of Apps selected to use Game Update Package + optional SettingProto game_driver_all_apps = 8; + // Game Driver - List of Apps selected to use Game Driver // i.e. <pkg1>,<pkg2>,...,<pkgN> - optional SettingProto gup_dev_opt_in_apps = 9; - // GUP - List of Apps selected not to use Game Update Package + optional SettingProto game_driver_opt_in_apps = 9; + // Game Driver - List of Apps selected not to use Game Driver // i.e. <pkg1>,<pkg2>,...,<pkgN> - optional SettingProto gup_dev_opt_out_apps = 10; - // GUP - List of Apps that are forbidden to use Game Update Package - optional SettingProto gup_blacklist = 11; - // List of Apps that are allowed to use Game Driver package. + optional SettingProto game_driver_opt_out_apps = 10; + // Game Driver - List of Apps that are forbidden to use Game Driver + optional SettingProto game_driver_blacklist = 11; + // Game Driver - List of Apps that are allowed to use Game Driver optional SettingProto game_driver_whitelist = 12; // ANGLE - List of Apps that can check ANGLE rules optional SettingProto angle_whitelist = 13; diff --git a/core/proto/android/server/job/enums.proto b/core/proto/android/server/job/enums.proto new file mode 100644 index 000000000000..50fc0310ad99 --- /dev/null +++ b/core/proto/android/server/job/enums.proto @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package com.android.server.job; + +// This file is for JobScheduler enums inside the server directory. If you're +// adding enums for app-side code, use the file in +// frameworks/base/core/proto/android/app/job. +option java_outer_classname = "JobServerProtoEnums"; +option java_multiple_files = true; + +// Set of constraints that a job potentially needs satisfied before it can run. +// Defined in +// frameworks/base/services/core/java/com/android/server/job/controllers/JobStatus.java +enum ConstraintEnum { + CONSTRAINT_UNKNOWN = 0; + CONSTRAINT_CHARGING = 1; + CONSTRAINT_BATTERY_NOT_LOW = 2; + CONSTRAINT_STORAGE_NOT_LOW = 3; + CONSTRAINT_TIMING_DELAY = 4; + CONSTRAINT_DEADLINE = 5; + CONSTRAINT_IDLE = 6; + CONSTRAINT_CONNECTIVITY = 7; + CONSTRAINT_CONTENT_TRIGGER = 8; + CONSTRAINT_DEVICE_NOT_DOZING = 9; + CONSTRAINT_WITHIN_QUOTA = 10; + CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 11; +} diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto index 188769d930d1..6c9d13a572ee 100644 --- a/core/proto/android/server/jobscheduler.proto +++ b/core/proto/android/server/jobscheduler.proto @@ -29,6 +29,7 @@ import "frameworks/base/core/proto/android/net/networkrequest.proto"; import "frameworks/base/core/proto/android/os/bundle.proto"; import "frameworks/base/core/proto/android/os/persistablebundle.proto"; import "frameworks/base/core/proto/android/server/forceappstandbytracker.proto"; +import "frameworks/base/core/proto/android/server/job/enums.proto"; import "frameworks/base/libs/incident/proto/android/privacy.proto"; // Next tag: 21 @@ -757,21 +758,9 @@ message JobStatusDumpProto { } optional JobInfo job_info = 6; - enum Constraint { - CONSTRAINT_CHARGING = 1; - CONSTRAINT_BATTERY_NOT_LOW = 2; - CONSTRAINT_STORAGE_NOT_LOW = 3; - CONSTRAINT_TIMING_DELAY = 4; - CONSTRAINT_DEADLINE = 5; - CONSTRAINT_IDLE = 6; - CONSTRAINT_CONNECTIVITY = 7; - CONSTRAINT_CONTENT_TRIGGER = 8; - CONSTRAINT_DEVICE_NOT_DOZING = 9; - CONSTRAINT_WITHIN_QUOTA = 10; - } - repeated Constraint required_constraints = 7; - repeated Constraint satisfied_constraints = 8; - repeated Constraint unsatisfied_constraints = 9; + repeated ConstraintEnum required_constraints = 7; + repeated ConstraintEnum satisfied_constraints = 8; + repeated ConstraintEnum unsatisfied_constraints = 9; optional bool is_doze_whitelisted = 10; optional bool is_uid_active = 26; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 25baa921e8c9..7184c7a43da7 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3528,6 +3528,12 @@ android:protectionLevel="signature|privileged" /> <uses-permission android:name="android.permission.CONTROL_VPN" /> + <!-- Allows an application to access and modify always-on VPN configuration. + <p>Not for use by third-party or privileged applications. + @hide --> + <permission android:name="android.permission.CONTROL_ALWAYS_ON_VPN" + android:protectionLevel="signature" /> + <!-- Allows an application to capture audio output. <p>Not for use by third-party applications.</p> --> <permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" diff --git a/packages/SettingsLib/res/drawable/btn_borderless_rect.xml b/core/res/res/drawable/btn_borderless_rect.xml index 9eaba8364f8e..9eaba8364f8e 100644 --- a/packages/SettingsLib/res/drawable/btn_borderless_rect.xml +++ b/core/res/res/drawable/btn_borderless_rect.xml diff --git a/packages/SettingsLib/res/drawable/ic_bt_cellphone.xml b/core/res/res/drawable/ic_bt_cellphone.xml index be9b094134c8..be9b094134c8 100644 --- a/packages/SettingsLib/res/drawable/ic_bt_cellphone.xml +++ b/core/res/res/drawable/ic_bt_cellphone.xml diff --git a/packages/SettingsLib/res/drawable/ic_bt_headphones_a2dp.xml b/core/res/res/drawable/ic_bt_headphones_a2dp.xml index 32f39a39754f..32f39a39754f 100644 --- a/packages/SettingsLib/res/drawable/ic_bt_headphones_a2dp.xml +++ b/core/res/res/drawable/ic_bt_headphones_a2dp.xml diff --git a/packages/SettingsLib/res/drawable/ic_bt_headset_hfp.xml b/core/res/res/drawable/ic_bt_headset_hfp.xml index e43fe39409af..e43fe39409af 100644 --- a/packages/SettingsLib/res/drawable/ic_bt_headset_hfp.xml +++ b/core/res/res/drawable/ic_bt_headset_hfp.xml diff --git a/packages/SettingsLib/res/drawable/ic_bt_hearing_aid.xml b/core/res/res/drawable/ic_bt_hearing_aid.xml index e14c99b61e2f..e14c99b61e2f 100644 --- a/packages/SettingsLib/res/drawable/ic_bt_hearing_aid.xml +++ b/core/res/res/drawable/ic_bt_hearing_aid.xml diff --git a/packages/SettingsLib/res/drawable/ic_bt_laptop.xml b/core/res/res/drawable/ic_bt_laptop.xml index 029e4d913434..029e4d913434 100644 --- a/packages/SettingsLib/res/drawable/ic_bt_laptop.xml +++ b/core/res/res/drawable/ic_bt_laptop.xml diff --git a/packages/SettingsLib/res/drawable/ic_bt_misc_hid.xml b/core/res/res/drawable/ic_bt_misc_hid.xml index ac460ab675e9..ac460ab675e9 100644 --- a/packages/SettingsLib/res/drawable/ic_bt_misc_hid.xml +++ b/core/res/res/drawable/ic_bt_misc_hid.xml diff --git a/packages/SettingsLib/res/drawable/ic_bt_network_pan.xml b/core/res/res/drawable/ic_bt_network_pan.xml index 6e4361b95eab..6e4361b95eab 100644 --- a/packages/SettingsLib/res/drawable/ic_bt_network_pan.xml +++ b/core/res/res/drawable/ic_bt_network_pan.xml diff --git a/packages/SettingsLib/res/drawable/ic_bt_pointing_hid.xml b/core/res/res/drawable/ic_bt_pointing_hid.xml index de97e249789f..de97e249789f 100644 --- a/packages/SettingsLib/res/drawable/ic_bt_pointing_hid.xml +++ b/core/res/res/drawable/ic_bt_pointing_hid.xml diff --git a/packages/SettingsLib/res/drawable/ic_expand_more.xml b/core/res/res/drawable/ic_expand_more.xml index a8ff5397c839..a8ff5397c839 100644 --- a/packages/SettingsLib/res/drawable/ic_expand_more.xml +++ b/core/res/res/drawable/ic_expand_more.xml diff --git a/packages/SettingsLib/res/drawable/ic_lockscreen_ime.xml b/core/res/res/drawable/ic_lockscreen_ime.xml index 4b81a3c9c460..4b81a3c9c460 100644 --- a/packages/SettingsLib/res/drawable/ic_lockscreen_ime.xml +++ b/core/res/res/drawable/ic_lockscreen_ime.xml diff --git a/packages/SettingsLib/res/drawable/ic_menu.xml b/core/res/res/drawable/ic_menu.xml index b77db08b336d..b77db08b336d 100644 --- a/packages/SettingsLib/res/drawable/ic_menu.xml +++ b/core/res/res/drawable/ic_menu.xml diff --git a/packages/SettingsLib/res/drawable/ic_minus.xml b/core/res/res/drawable/ic_minus.xml index 9a929a44c665..9a929a44c665 100644 --- a/packages/SettingsLib/res/drawable/ic_minus.xml +++ b/core/res/res/drawable/ic_minus.xml diff --git a/packages/SettingsLib/res/drawable/ic_mode_edit.xml b/core/res/res/drawable/ic_mode_edit.xml index 319e7eb230d5..319e7eb230d5 100644 --- a/packages/SettingsLib/res/drawable/ic_mode_edit.xml +++ b/core/res/res/drawable/ic_mode_edit.xml diff --git a/packages/SettingsLib/res/drawable/ic_plus.xml b/core/res/res/drawable/ic_plus.xml index 2a10e707df2a..2a10e707df2a 100644 --- a/packages/SettingsLib/res/drawable/ic_plus.xml +++ b/core/res/res/drawable/ic_plus.xml diff --git a/packages/SettingsLib/res/drawable/ic_qs_night_display_on.xml b/core/res/res/drawable/ic_qs_night_display_on.xml index 35907cc83fe0..35907cc83fe0 100644 --- a/packages/SettingsLib/res/drawable/ic_qs_night_display_on.xml +++ b/core/res/res/drawable/ic_qs_night_display_on.xml diff --git a/packages/SettingsLib/res/drawable/ic_settings_bluetooth.xml b/core/res/res/drawable/ic_settings_bluetooth.xml index 6e32e1a7f631..6e32e1a7f631 100644 --- a/packages/SettingsLib/res/drawable/ic_settings_bluetooth.xml +++ b/core/res/res/drawable/ic_settings_bluetooth.xml diff --git a/packages/SettingsLib/res/drawable/ic_settings_print.xml b/core/res/res/drawable/ic_settings_print.xml index 68b627cf4dc9..68b627cf4dc9 100644 --- a/packages/SettingsLib/res/drawable/ic_settings_print.xml +++ b/core/res/res/drawable/ic_settings_print.xml diff --git a/packages/SettingsLib/res/drawable/ic_signal_location.xml b/core/res/res/drawable/ic_signal_location.xml index 11870933b8bb..11870933b8bb 100644 --- a/packages/SettingsLib/res/drawable/ic_signal_location.xml +++ b/core/res/res/drawable/ic_signal_location.xml diff --git a/core/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml index 43b3552ff8d4..949c12e7d1da 100644 --- a/core/res/res/values-night/themes_device_defaults.xml +++ b/core/res/res/values-night/themes_device_defaults.xml @@ -51,6 +51,10 @@ easier. --> <!-- DeviceDefault theme for a window that should look like the Settings app. --> <style name="Theme.DeviceDefault.Settings" parent="Theme.DeviceDefault"> + <item name="windowLightStatusBar">false</item> + <item name="navigationBarColor">@android:color/black</item> + <item name="windowLightNavigationBar">false</item> + <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item> <item name="colorBackground">@color/primary_dark_device_default_settings</item> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index c05795de4751..1db8135d31c7 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -861,6 +861,11 @@ The default is false. --> <bool name="config_lidControlsSleep">false</bool> + <!-- Indicate whether closing the lid causes the device to enter the folded state which means + to get a smaller screen and opening the lid causes the device to enter the unfolded state + which means to get a larger screen. --> + <bool name="config_lidControlsDisplayFold">false</bool> + <!-- Desk dock behavior --> <!-- The number of degrees to rotate the display when the device is in a desk dock. diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index f79e22d1f94e..aefa9dfd1056 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1423,6 +1423,27 @@ <java-symbol type="drawable" name="ic_sim_card_multi_48px_clr" /> <java-symbol type="drawable" name="ic_signal_cellular_alt_24px" /> + <java-symbol type="drawable" name="btn_borderless_rect" /> + <java-symbol type="drawable" name="ic_bt_cellphone" /> + <java-symbol type="drawable" name="ic_bt_headphones_a2dp" /> + <java-symbol type="drawable" name="ic_bt_headset_hfp" /> + <java-symbol type="drawable" name="ic_bt_hearing_aid" /> + <java-symbol type="drawable" name="ic_bt_laptop" /> + <java-symbol type="drawable" name="ic_bt_misc_hid" /> + <java-symbol type="drawable" name="ic_bt_network_pan" /> + <java-symbol type="drawable" name="ic_bt_pointing_hid" /> + <java-symbol type="drawable" name="ic_expand_more" /> + <java-symbol type="drawable" name="ic_lockscreen_ime" /> + <java-symbol type="drawable" name="ic_menu" /> + <java-symbol type="drawable" name="ic_minus" /> + <java-symbol type="drawable" name="ic_mode_edit" /> + <java-symbol type="drawable" name="ic_plus" /> + <java-symbol type="drawable" name="ic_qs_night_display_on" /> + <java-symbol type="drawable" name="ic_settings_bluetooth" /> + <java-symbol type="drawable" name="ic_settings_print" /> + <java-symbol type="drawable" name="ic_signal_location" /> + <java-symbol type="drawable" name="ic_info_outline_24" /> + <java-symbol type="drawable" name="stat_notify_mmcc_indication_icn" /> <java-symbol type="drawable" name="autofilled_highlight"/> <java-symbol type="drawable" name="ic_camera" /> @@ -3511,6 +3532,7 @@ <java-symbol type="integer" name="config_defaultRingVibrationIntensity" /> <java-symbol type="bool" name="config_maskMainBuiltInDisplayCutout" /> + <java-symbol type="bool" name="config_lidControlsDisplayFold" /> <java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" /> <java-symbol type="array" name="config_disableApkUnlessMatchedSku_skus_list" /> diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk index 8e8b07a9074b..9d04e6392fd0 100644 --- a/core/tests/coretests/Android.mk +++ b/core/tests/coretests/Android.mk @@ -53,7 +53,6 @@ LOCAL_JAVA_LIBRARIES := \ org.apache.http.legacy \ android.test.base \ android.test.mock \ - framework-oahl-backward-compatibility \ framework-atb-backward-compatibility \ LOCAL_PACKAGE_NAME := FrameworksCoreTests diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index bd7f8527fc6f..ca2e3ed0b5ab 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -132,6 +132,9 @@ public class SettingsBackupTest { Settings.Global.AUTOMATIC_POWER_SAVER_MODE, Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED, Settings.Global.BATTERY_CHARGING_STATE_UPDATE_DELAY, + Settings.Global.BROADCAST_BG_CONSTANTS, + Settings.Global.BROADCAST_FG_CONSTANTS, + Settings.Global.BROADCAST_OFFLOAD_CONSTANTS, Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD, Settings.Global.BATTERY_DISCHARGE_THRESHOLD, Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS, @@ -483,10 +486,10 @@ public class SettingsBackupTest { Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS, Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES, Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST, - Settings.Global.GUP_DEV_ALL_APPS, - Settings.Global.GUP_DEV_OPT_IN_APPS, - Settings.Global.GUP_DEV_OPT_OUT_APPS, - Settings.Global.GUP_BLACKLIST, + Settings.Global.GAME_DRIVER_ALL_APPS, + Settings.Global.GAME_DRIVER_OPT_IN_APPS, + Settings.Global.GAME_DRIVER_OPT_OUT_APPS, + Settings.Global.GAME_DRIVER_BLACKLIST, Settings.Global.GAME_DRIVER_WHITELIST, Settings.Global.GPU_DEBUG_LAYER_APP, Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, @@ -575,6 +578,7 @@ public class SettingsBackupTest { Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS, Settings.Secure.ALWAYS_ON_VPN_APP, Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN, + Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST, Settings.Secure.ANDROID_ID, Settings.Secure.ANR_SHOW_BACKGROUND, Settings.Secure.ASSISTANT, diff --git a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java index 4aa10007bf53..c99777b160f0 100644 --- a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java +++ b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java @@ -77,7 +77,6 @@ public class StatusBarNotificationTest { @Test public void testLogMaker() { final LogMaker logMaker = getNotification(PKG, GROUP_ID_1, CHANNEL_ID).getLogMaker(); - assertEquals(CHANNEL_ID, (String) logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID)); assertEquals(PKG, logMaker.getPackageName()); @@ -85,6 +84,18 @@ public class StatusBarNotificationTest { assertEquals(TAG, logMaker.getTaggedData(MetricsEvent.NOTIFICATION_TAG)); assertEquals(GROUP_ID_1, logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID)); + assertEquals(0, + logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_SUMMARY)); + assertNull(logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_CATEGORY)); + } + + @Test + public void testLogMakerWithCategory() { + Notification.Builder builder = getNotificationBuilder(GROUP_ID_1, CHANNEL_ID) + .setCategory(Notification.CATEGORY_MESSAGE); + final LogMaker logMaker = getNotification(PKG, builder).getLogMaker(); + assertEquals(Notification.CATEGORY_MESSAGE, + logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_CATEGORY)); } @Test @@ -138,6 +149,10 @@ public class StatusBarNotificationTest { } private StatusBarNotification getNotification(String pkg, String group, String channelId) { + return getNotification(pkg, getNotificationBuilder(group, channelId)); + } + + private Notification.Builder getNotificationBuilder(String group, String channelId) { final Notification.Builder builder = new Notification.Builder(mMockContext, channelId) .setContentTitle("foo") .setSmallIcon(android.R.drawable.sym_def_app_icon); @@ -145,10 +160,13 @@ public class StatusBarNotificationTest { if (group != null) { builder.setGroup(group); } + return builder; + } + + private StatusBarNotification getNotification(String pkg, Notification.Builder builder) { - Notification n = builder.build(); return new StatusBarNotification( - pkg, pkg, ID, TAG, UID, UID, n, USER, null, UID); + pkg, pkg, ID, TAG, UID, UID, builder.build(), USER, null, UID); } } diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java new file mode 100644 index 000000000000..b07cb99f35c5 --- /dev/null +++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.view; + +import static android.view.ImeInsetsSourceConsumer.areEditorsSimilar; +import static android.view.InsetsState.TYPE_IME; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import android.content.Context; +import android.graphics.Insets; +import android.graphics.Rect; +import android.os.Bundle; +import android.platform.test.annotations.Presubmit; +import android.view.SurfaceControl.Transaction; +import android.view.WindowManager.BadTokenException; +import android.view.WindowManager.LayoutParams; +import android.view.inputmethod.EditorInfo; +import android.widget.TextView; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.FlakyTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; + +@Presubmit +@FlakyTest(detail = "Promote once confirmed non-flaky") +@RunWith(AndroidJUnit4.class) +public class ImeInsetsSourceConsumerTest { + + Context mContext = InstrumentationRegistry.getTargetContext(); + ImeInsetsSourceConsumer mImeConsumer; + InsetsController mController; + SurfaceControl mLeash; + + @Before + public void setup() { + mLeash = new SurfaceControl.Builder(new SurfaceSession()) + .setName("testSurface") + .build(); + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + ViewRootImpl viewRootImpl = new ViewRootImpl(mContext, mContext.getDisplay()); + try { + viewRootImpl.setView(new TextView(mContext), new LayoutParams(), null); + } catch (BadTokenException e) { + // activity isn't running, we will ignore BadTokenException. + } + mController = new InsetsController(viewRootImpl); + final Rect rect = new Rect(5, 5, 5, 5); + mController.calculateInsets( + false, + false, + new DisplayCutout( + Insets.of(10, 10, 10, 10), rect, rect, rect, rect), + rect, rect); + mImeConsumer = new ImeInsetsSourceConsumer( + new InsetsState(), Transaction::new, mController); + }); + } + + @Test + public void testImeVisibility() { + final InsetsSourceControl ime = new InsetsSourceControl(TYPE_IME, mLeash); + mController.onControlsChanged(new InsetsSourceControl[] { ime }); + + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + // test if setVisibility can show IME + mImeConsumer.onWindowFocusGained(); + mImeConsumer.applyImeVisibility(true); + mController.cancelExistingAnimation(); + assertTrue(mController.getSourceConsumer(ime.getType()).isVisible()); + + // test if setVisibility can hide IME + mImeConsumer.applyImeVisibility(false); + mController.cancelExistingAnimation(); + assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + }); + } + + @Test + public void testAreEditorsSimilar() { + EditorInfo info1 = new EditorInfo(); + info1.privateImeOptions = "dummy"; + EditorInfo info2 = new EditorInfo(); + + assertFalse(areEditorsSimilar(info1, info2)); + + info1.privateImeOptions = null; + assertTrue(areEditorsSimilar(info1, info2)); + + info1.inputType = info2.inputType = 3; + info1.imeOptions = info2.imeOptions = 0x4; + info1.packageName = info2.packageName = "dummy.package"; + assertTrue(areEditorsSimilar(info1, info2)); + + Bundle extras1 = new Bundle(); + extras1.putByteArray("key1", "value1".getBytes()); + extras1.putChar("key2", 'c'); + Bundle extras2 = new Bundle(); + extras2.putByteArray("key1", "value1".getBytes()); + extras2.putChar("key2", 'c'); + info1.extras = extras1; + info2.extras = extras2; + assertTrue(areEditorsSimilar(info1, info2)); + + Bundle extraBundle = new Bundle(); + ArrayList<Integer> list = new ArrayList<>(); + list.add(2); + list.add(5); + extraBundle.putByteArray("key1", "value1".getBytes()); + extraBundle.putChar("key2", 'c'); + extraBundle.putIntegerArrayList("key3", list); + + extras1.putAll(extraBundle); + extras2.putAll(extraBundle); + assertTrue(areEditorsSimilar(info1, info2)); + + extras2.putChar("key2", 'd'); + assertFalse(areEditorsSimilar(info1, info2)); + + extras2.putChar("key2", 'c'); + extras2.putInt("key4", 1); + assertFalse(areEditorsSimilar(info1, info2)); + } +} diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java index 2db2f5f2a4fd..03af67df13d0 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -155,10 +155,10 @@ public class InsetsStateTest { @Test public void testGetDefaultVisibility() { - assertTrue(InsetsState.getDefaultVisibly(TYPE_TOP_BAR)); - assertTrue(InsetsState.getDefaultVisibly(TYPE_SIDE_BAR_1)); - assertTrue(InsetsState.getDefaultVisibly(TYPE_SIDE_BAR_2)); - assertTrue(InsetsState.getDefaultVisibly(TYPE_SIDE_BAR_3)); - assertFalse(InsetsState.getDefaultVisibly(TYPE_IME)); + assertTrue(InsetsState.getDefaultVisibility(TYPE_TOP_BAR)); + assertTrue(InsetsState.getDefaultVisibility(TYPE_SIDE_BAR_1)); + assertTrue(InsetsState.getDefaultVisibility(TYPE_SIDE_BAR_2)); + assertTrue(InsetsState.getDefaultVisibility(TYPE_SIDE_BAR_3)); + assertFalse(InsetsState.getDefaultVisibility(TYPE_IME)); } } diff --git a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java new file mode 100644 index 000000000000..68099fe19d13 --- /dev/null +++ b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.inputmethod; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import android.os.Parcel; +import android.os.UserHandle; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Supplemental tests that cannot be covered by CTS (e.g. due to hidden API dependencies). + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class EditorInfoTest { + private static final int TEST_USER_ID = 42; + + /** + * Makes sure that {@code null} {@link EditorInfo#targetInputMethodUser} can be copied via + * {@link Parcel}. + */ + @Test + public void testNullTargetInputMethodUserParcelable() throws Exception { + final EditorInfo editorInfo = new EditorInfo(); + editorInfo.targetInputMethodUser = null; + assertNull(cloneViaParcel(editorInfo).targetInputMethodUser); + } + + /** + * Makes sure that non-{@code null} {@link EditorInfo#targetInputMethodUser} can be copied via + * {@link Parcel}. + */ + @Test + public void testNonNullTargetInputMethodUserParcelable() throws Exception { + final EditorInfo editorInfo = new EditorInfo(); + editorInfo.targetInputMethodUser = UserHandle.of(TEST_USER_ID); + assertEquals(UserHandle.of(TEST_USER_ID), cloneViaParcel(editorInfo).targetInputMethodUser); + } + + private static EditorInfo cloneViaParcel(EditorInfo original) { + Parcel parcel = null; + try { + parcel = Parcel.obtain(); + original.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + return EditorInfo.CREATOR.createFromParcel(parcel); + } finally { + if (parcel != null) { + parcel.recycle(); + } + } + } +} diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 904c3fb6d549..1684138455e5 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -87,6 +87,7 @@ applications that come with the platform <permission name="android.permission.MASTER_CLEAR"/> <permission name="android.permission.NETWORK_MANAGED_PROVISIONING"/> <permission name="android.permission.PERFORM_CDMA_PROVISIONING"/> + <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.permission.SET_TIME"/> <permission name="android.permission.SET_TIME_ZONE"/> <permission name="android.permission.SHUTDOWN"/> diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 8636949943b7..f0e236168858 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -1751,6 +1751,50 @@ public final class Bitmap implements Parcelable { } /** + * <p>Modifies the bitmap to have the specified {@link ColorSpace}, without + * affecting the underlying allocation backing the bitmap.</p> + * + * @throws IllegalArgumentException If the specified color space is {@code null}, not + * {@link ColorSpace.Model#RGB RGB}, has a transfer function that is not an + * {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}, or whose + * components min/max values reduce the numerical range compared to the + * previously assigned color space. + * + * @param colorSpace to assign to the bitmap + * @hide + */ + @TestApi + public void setColorSpace(@NonNull ColorSpace colorSpace) { + checkRecycled("setColorSpace called on a recycled bitmap"); + if (colorSpace == null) { + throw new IllegalArgumentException("The colorSpace cannot be set to null"); + } + if (getColorSpace() != null) { + if (mColorSpace.getComponentCount() != colorSpace.getComponentCount()) { + throw new IllegalArgumentException("The new ColorSpace must have the same " + + "component count as the current ColorSpace"); + } + for (int i = 0; i < mColorSpace.getComponentCount(); i++) { + if (mColorSpace.getMinValue(i) < colorSpace.getMinValue(i)) { + throw new IllegalArgumentException("The new ColorSpace cannot increase the " + + "minimum value for any of the components compared to the current " + + "ColorSpace. To perform this type of conversion create a new Bitmap " + + "in the desired ColorSpace and draw this Bitmap into it."); + } + if (mColorSpace.getMaxValue(i) > colorSpace.getMaxValue(i)) { + throw new IllegalArgumentException("The new ColorSpace cannot decrease the " + + "maximum value for any of the components compared to the current " + + "ColorSpace/ To perform this type of conversion create a new Bitmap" + + "in the desired ColorSpace and draw this Bitmap into it."); + } + } + } + + nativeSetColorSpace(mNativePtr, colorSpace.getNativeInstance()); + mColorSpace = colorSpace; + } + + /** * Fills the bitmap's pixels with the specified {@link Color}. * * @throws IllegalStateException if the bitmap is not mutable. @@ -2174,6 +2218,7 @@ public final class Bitmap implements Parcelable { long nativeColorSpace); private static native GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap); private static native boolean nativeGetColorSpace(long nativePtr, float[] xyz, float[] params); + private static native void nativeSetColorSpace(long nativePtr, long nativeColorSpace); private static native boolean nativeIsSRGB(long nativePtr); private static native boolean nativeIsSRGBLinear(long nativePtr); private static native void nativeCopyColorSpace(long srcBitmap, long dstBitmap); diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java index c580c46cc888..0787d8518fa5 100644 --- a/graphics/java/android/graphics/ImageFormat.java +++ b/graphics/java/android/graphics/ImageFormat.java @@ -183,6 +183,14 @@ public class ImageFormat { public static final int JPEG = 0x100; /** + * Depth augmented compressed JPEG format. + * + * <p>JPEG compressed main image along with XMP embedded depth metadata + * following ISO 16684-1:2011(E).</p> + */ + public static final int DEPTH_JPEG = 0x69656963; + + /** * <p>Multi-plane Android YUV 420 format</p> * * <p>This format is a generic YCbCr format, capable of describing any 4:2:0 @@ -787,6 +795,7 @@ public class ImageFormat { case PRIVATE: case RAW_DEPTH: case Y8: + case DEPTH_JPEG: return true; } diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 25f6775f81b4..cb1f69c0ada3 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -33,6 +33,7 @@ import android.graphics.fonts.FontStyle; import android.graphics.fonts.FontVariationAxis; import android.graphics.fonts.SystemFonts; import android.os.Build; +import android.os.Build.VERSION_CODES; import android.provider.FontRequest; import android.provider.FontsContract; import android.text.FontConfig; @@ -46,6 +47,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import dalvik.annotation.optimization.CriticalNative; +import dalvik.system.VMRuntime; import libcore.util.NativeAllocationRegistry; @@ -243,7 +245,16 @@ public class Typeface { if (familyBuilder == null) { familyBuilder = new FontFamily.Builder(fontBuilder.build()); } else { - familyBuilder.addFont(fontBuilder.build()); + try { + familyBuilder.addFont(fontBuilder.build()); + } catch (IllegalArgumentException e) { + if (VMRuntime.getRuntime().getTargetSdkVersion() <= VERSION_CODES.P) { + // Surpress the IllegalArgumentException for keeping the backward + // compatibility. + continue; + } + throw e; + } } } if (familyBuilder == null) { diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java index 072fe7321826..2ae28f507e0d 100644 --- a/keystore/java/android/security/Credentials.java +++ b/keystore/java/android/security/Credentials.java @@ -16,12 +16,6 @@ package android.security; -import android.annotation.UnsupportedAppUsage; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.util.Log; - import com.android.org.bouncycastle.util.io.pem.PemObject; import com.android.org.bouncycastle.util.io.pem.PemReader; import com.android.org.bouncycastle.util.io.pem.PemWriter; @@ -34,7 +28,6 @@ import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; import java.nio.charset.StandardCharsets; -import java.security.KeyPair; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; @@ -53,8 +46,6 @@ public class Credentials { public static final String INSTALL_AS_USER_ACTION = "android.credentials.INSTALL_AS_USER"; - public static final String UNLOCK_ACTION = "com.android.credentials.UNLOCK"; - /** Key prefix for CA certificates. */ public static final String CA_CERTIFICATE = "CACERT_"; @@ -171,58 +162,6 @@ public class Credentials { } } - private static Credentials singleton; - - @UnsupportedAppUsage - public static Credentials getInstance() { - if (singleton == null) { - singleton = new Credentials(); - } - return singleton; - } - - @UnsupportedAppUsage - public void unlock(Context context) { - try { - Intent intent = new Intent(UNLOCK_ACTION); - context.startActivity(intent); - } catch (ActivityNotFoundException e) { - Log.w(LOGTAG, e.toString()); - } - } - - public void install(Context context) { - try { - Intent intent = KeyChain.createInstallIntent(); - context.startActivity(intent); - } catch (ActivityNotFoundException e) { - Log.w(LOGTAG, e.toString()); - } - } - - @UnsupportedAppUsage - public void install(Context context, KeyPair pair) { - try { - Intent intent = KeyChain.createInstallIntent(); - intent.putExtra(EXTRA_PRIVATE_KEY, pair.getPrivate().getEncoded()); - intent.putExtra(EXTRA_PUBLIC_KEY, pair.getPublic().getEncoded()); - context.startActivity(intent); - } catch (ActivityNotFoundException e) { - Log.w(LOGTAG, e.toString()); - } - } - - @UnsupportedAppUsage - public void install(Context context, String type, byte[] value) { - try { - Intent intent = KeyChain.createInstallIntent(); - intent.putExtra(type, value); - context.startActivity(intent); - } catch (ActivityNotFoundException e) { - Log.w(LOGTAG, e.toString()); - } - } - /** * Delete all types (private key, user certificate, CA certificate) for a * particular {@code alias}. All three can exist for any given alias. diff --git a/media/Android.bp b/media/Android.bp index 0675a36f3e81..753f4b7bbfa2 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -25,9 +25,10 @@ java_library { } java_library { - name: "updatable-mediasession2", + name: "updatable-media", srcs: [ + ":mediaplayer2-srcs", ":mediasession2-srcs", ":framework-media-annotation-srcs", ], @@ -37,26 +38,12 @@ java_library { "apex/java", ], - // TODO: find out a way to include only the necessary aidl files instead of dirs. include_dirs: [ + // For the usage of android.os.Bundle and android.os.ResultReceiver in aidl files "frameworks/base/core/java", ], }, - installable: true, - - // Make sure that the implementaion only relies on SDK or system APIs. - sdk_version: "system_current", -} - -java_library { - name: "updatable-media", - - srcs: [ - ":mediaplayer2-srcs", - ":framework-media-annotation-srcs", - ], - static_libs: [ "mediaplayer2-protos", ], diff --git a/media/apex/java/android/media/MediaPlayer2.java b/media/apex/java/android/media/MediaPlayer2.java index a2feec2e9faf..1a1f6fb4457a 100644 --- a/media/apex/java/android/media/MediaPlayer2.java +++ b/media/apex/java/android/media/MediaPlayer2.java @@ -4482,6 +4482,7 @@ public class MediaPlayer2 implements AutoCloseable }; // Modular DRM + private final Map<UUID, MediaDrm> mDrmObjs = new HashMap<>(); private class DrmHandle { static final int PROVISION_TIMEOUT_MS = 60000; @@ -4594,7 +4595,13 @@ public class MediaPlayer2 implements AutoCloseable Log.v(TAG, "prepareDrm_createDrmStep: UUID: " + uuid); try { - mDrmObj = new MediaDrm(uuid); + mDrmObj = mDrmObjs.computeIfAbsent(uuid, scheme -> { + try { + return new MediaDrm(scheme); + } catch (UnsupportedSchemeException e) { + throw new IllegalArgumentException(e); + } + }); Log.v(TAG, "prepareDrm_createDrmStep: Created mDrmObj=" + mDrmObj); } catch (Exception e) { // UnsupportedSchemeException Log.e(TAG, "prepareDrm_createDrmStep: MediaDrm failed with " + e); @@ -4837,7 +4844,12 @@ public class MediaPlayer2 implements AutoCloseable msg = mTaskHandler.obtainMessage( MEDIA_ERROR, status, MEDIA_ERROR_UNKNOWN, null); } - mTaskHandler.sendMessage(msg); + mTaskHandler.post(new Runnable() { + @Override + public void run() { + mTaskHandler.handleMessage(msg, mSrcId); + } + }); sendDrmEvent(new DrmEventNotifier() { @Override diff --git a/media/apex/java/android/media/MediaSession2Service.java b/media/apex/java/android/media/MediaSession2Service.java index f18cd317ef12..54d0ed28cb83 100644 --- a/media/apex/java/android/media/MediaSession2Service.java +++ b/media/apex/java/android/media/MediaSession2Service.java @@ -16,8 +16,6 @@ package android.media; -import static android.media.Session2Token.SESSION_SERVICE_INTERFACE; - import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; @@ -47,6 +45,10 @@ import java.util.Map; * for consistent behavior across all devices. */ public abstract class MediaSession2Service extends Service { + /** + * The {@link Intent} that must be declared as handled by the service. + */ + public static final String SERVICE_INTERFACE = Session2Token.SESSION_SERVICE_INTERFACE; private static final String TAG = "MediaSession2Service"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -98,7 +100,7 @@ public abstract class MediaSession2Service extends Service { @Override @Nullable public IBinder onBind(@NonNull Intent intent) { - if (SESSION_SERVICE_INTERFACE.equals(intent.getAction())) { + if (SERVICE_INTERFACE.equals(intent.getAction())) { synchronized (mLock) { return mStub; } diff --git a/media/apex/java/android/media/session/MediaController.java b/media/apex/java/android/media/session/MediaController.java index d43acf47b863..79389a8b529c 100644 --- a/media/apex/java/android/media/session/MediaController.java +++ b/media/apex/java/android/media/session/MediaController.java @@ -18,6 +18,7 @@ package android.media.session; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.app.PendingIntent; import android.content.Context; @@ -82,42 +83,26 @@ public final class MediaController { private final TransportControls mTransportControls; /** - * Call for creating a MediaController directly from a controller link. Should only - * be used by framework code. - * @hide - */ - public MediaController(Context context, ControllerLink sessionBinder) { - if (sessionBinder == null) { - throw new IllegalArgumentException("Session token cannot be null"); - } - if (context == null) { - throw new IllegalArgumentException("Context cannot be null"); - } - mSessionBinder = sessionBinder; - mTransportControls = new TransportControls(); - mToken = new MediaSession.Token(sessionBinder); - mContext = context; - mCbStub = new ControllerCallbackLink(context, new CallbackStub(this)); - } - - /** - * Call for creating a MediaController directly from a binder. Should only - * be used by framework code. - * @hide - * TODO: remove this constructor - */ - public MediaController(Context context, ISessionController sessionBinder) { - this(context, new ControllerLink(sessionBinder.asBinder())); - } - - /** * Create a new MediaController from a session's token. * * @param context The caller's context. * @param token The token for the session. */ public MediaController(@NonNull Context context, @NonNull MediaSession.Token token) { - this(context, token.getControllerLink()); + if (context == null) { + throw new IllegalArgumentException("context shouldn't be null"); + } + if (token == null) { + throw new IllegalArgumentException("token shouldn't be null"); + } + if (token.getControllerLink() == null) { + throw new IllegalArgumentException("token.getControllerLink() shouldn't be null"); + } + mSessionBinder = token.getControllerLink(); + mTransportControls = new TransportControls(); + mToken = token; + mContext = context; + mCbStub = new ControllerCallbackLink(context, new CallbackStub(this)); } /** @@ -501,7 +486,6 @@ public final class MediaController { * Get the session's tag for debugging purposes. * * @return The session's tag. - * @hide */ public String getTag() { if (mTag == null) { @@ -1011,6 +995,7 @@ public final class MediaController { /** * @hide */ + @SystemApi public PlaybackInfo(int type, int control, int max, int current, AudioAttributes attrs) { mVolumeType = type; mVolumeControl = control; diff --git a/media/apex/java/android/media/session/MediaSessionEngine.java b/media/apex/java/android/media/session/MediaSessionEngine.java index 1f5fa5fe4127..edf283e0caa8 100644 --- a/media/apex/java/android/media/session/MediaSessionEngine.java +++ b/media/apex/java/android/media/session/MediaSessionEngine.java @@ -451,12 +451,24 @@ public final class MediaSessionEngine implements AutoCloseable { } /** + * Returns the name of the package that sent the last media button, transport control, or + * command from controllers and the system. This is only valid while in a request callback, such + * as {@link MediaSession.Callback#onPlay}. + */ + public String getCallingPackage() { + if (mCallbackHandler != null && mCallbackHandler.mCurrentControllerInfo != null) { + return mCallbackHandler.mCurrentControllerInfo.getPackageName(); + } + return null; + } + + + /** * Notify the system that the remote volume changed. * * @param provider The provider that is handling volume changes. - * @hide */ - public void notifyRemoteVolumeChanged(VolumeProvider provider) { + private void notifyRemoteVolumeChanged(VolumeProvider provider) { synchronized (mLock) { if (provider == null || provider != mVolumeProvider) { Log.w(TAG, "Received update from stale volume provider"); @@ -470,18 +482,6 @@ public final class MediaSessionEngine implements AutoCloseable { } } - /** - * Returns the name of the package that sent the last media button, transport control, or - * command from controllers and the system. This is only valid while in a request callback, such - * as {@link MediaSession.Callback#onPlay}. - */ - public String getCallingPackage() { - if (mCallbackHandler != null && mCallbackHandler.mCurrentControllerInfo != null) { - return mCallbackHandler.mCurrentControllerInfo.getPackageName(); - } - return null; - } - private void dispatchPrepare(RemoteUserInfo caller) { postToCallback(caller, CallbackMessageHandler.MSG_PREPARE, null, null); } diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java index 9ac147b59ee5..60ef1d93191a 100644 --- a/media/java/android/media/ImageReader.java +++ b/media/java/android/media/ImageReader.java @@ -17,12 +17,10 @@ package android.media; import android.graphics.ImageFormat; -import android.graphics.PixelFormat; import android.hardware.HardwareBuffer; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.util.Log; import android.view.Surface; import dalvik.system.VMRuntime; @@ -74,12 +72,6 @@ public class ImageReader implements AutoCloseable { private static final int ACQUIRE_MAX_IMAGES = 2; /** - * Invalid consumer buffer usage flag. This usage flag will be ignored - * by the {@code ImageReader} instance is constructed with this value. - */ - private static final long BUFFER_USAGE_UNKNOWN = 0; - - /** * <p> * Create a new reader for images of the desired size and format. * </p> @@ -129,7 +121,10 @@ public class ImageReader implements AutoCloseable { * @see Image */ public static ImageReader newInstance(int width, int height, int format, int maxImages) { - return new ImageReader(width, height, format, maxImages, BUFFER_USAGE_UNKNOWN); + // If the format is private don't default to USAGE_CPU_READ_OFTEN since it may not + // work, and is inscrutable anyway + return new ImageReader(width, height, format, maxImages, + format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN); } /** @@ -207,18 +202,23 @@ public class ImageReader implements AutoCloseable { * obtained by the user, one of them has to be released before a new Image will * become available for access through {@link #acquireLatestImage()} or * {@link #acquireNextImage()}. Must be greater than 0. - * @param usage The intended usage of the images produced by this ImageReader. It needs - * to be one of the Usage defined by {@link HardwareBuffer}, or an - * {@link IllegalArgumentException} will be thrown. + * @param usage The intended usage of the images produced by this ImageReader. See the usages + * on {@link HardwareBuffer} for a list of valid usage bits. See also + * {@link HardwareBuffer#isSupported(int, int, int, int, long)} for checking + * if a combination is supported. If it's not supported this will throw + * an {@link IllegalArgumentException}. * @see Image * @see HardwareBuffer */ public static ImageReader newInstance(int width, int height, int format, int maxImages, long usage) { - if (!isFormatUsageCombinationAllowed(format, usage)) { - throw new IllegalArgumentException("Format usage combination is not supported:" - + " format = " + format + ", usage = " + usage); - } + // TODO: Check this - can't do it just yet because format support is different + // Unify formats! The only reliable way to validate usage is to just try it and see. + +// if (!HardwareBuffer.isSupported(width, height, format, 1, usage)) { +// throw new IllegalArgumentException("The given format=" + Integer.toHexString(format) +// + " & usage=" + Long.toHexString(usage) + " is not supported"); +// } return new ImageReader(width, height, format, maxImages, usage); } @@ -716,19 +716,6 @@ public class ImageReader implements AutoCloseable { return si.getReader() == this; } - private static boolean isFormatUsageCombinationAllowed(int format, long usage) { - if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) { - return false; - } - - // Valid usage needs to be provided. - if (usage == BUFFER_USAGE_UNKNOWN) { - return false; - } - - return true; - } - /** * Called from Native code when an Event happens. * @@ -833,6 +820,7 @@ public class ImageReader implements AutoCloseable { case ImageFormat.JPEG: case ImageFormat.DEPTH_POINT_CLOUD: case ImageFormat.RAW_PRIVATE: + case ImageFormat.DEPTH_JPEG: width = ImageReader.this.getWidth(); break; default: @@ -849,6 +837,7 @@ public class ImageReader implements AutoCloseable { case ImageFormat.JPEG: case ImageFormat.DEPTH_POINT_CLOUD: case ImageFormat.RAW_PRIVATE: + case ImageFormat.DEPTH_JPEG: height = ImageReader.this.getHeight(); break; default: diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java index 2a0e04ebf051..b77a884d3412 100644 --- a/media/java/android/media/ImageUtils.java +++ b/media/java/android/media/ImageUtils.java @@ -63,6 +63,7 @@ class ImageUtils { case ImageFormat.DEPTH16: case ImageFormat.DEPTH_POINT_CLOUD: case ImageFormat.RAW_DEPTH: + case ImageFormat.DEPTH_JPEG: return 1; case ImageFormat.PRIVATE: return 0; @@ -192,6 +193,7 @@ class ImageUtils { // 10x compression from RGB_888 case ImageFormat.JPEG: case ImageFormat.DEPTH_POINT_CLOUD: + case ImageFormat.DEPTH_JPEG: estimatedBytePerPixel = 0.3; break; case ImageFormat.Y8: diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java index 33b8c4202fdd..112ce1dd1acc 100644 --- a/media/java/android/media/MediaMetadataRetriever.java +++ b/media/java/android/media/MediaMetadataRetriever.java @@ -920,12 +920,14 @@ public class MediaMetadataRetriever implements AutoCloseable { public static final int METADATA_KEY_VIDEO_FRAME_COUNT = 32; /** - * @hide + * If the media contains EXIF data, this key retrieves the offset value + * of the data. */ public static final int METADATA_KEY_EXIF_OFFSET = 33; /** - * @hide + * If the media contains EXIF data, this key retrieves the length of the + * data. */ public static final int METADATA_KEY_EXIF_LENGTH = 34; diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index ffeff4d93d15..3444e9277949 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -2357,8 +2357,7 @@ public class MediaRouter { return; } if (mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) { - @VolumeProvider.ControlType int volumeControl = - VolumeProvider.VOLUME_CONTROL_FIXED; + int volumeControl = VolumeProvider.VOLUME_CONTROL_FIXED; switch (mVolumeHandling) { case RemoteControlClient.PLAYBACK_VOLUME_VARIABLE: volumeControl = VolumeProvider.VOLUME_CONTROL_ABSOLUTE; @@ -2384,8 +2383,7 @@ public class MediaRouter { class SessionVolumeProvider extends VolumeProvider { - public SessionVolumeProvider(@VolumeProvider.ControlType int volumeControl, - int maxVolume, int currentVolume) { + SessionVolumeProvider(int volumeControl, int maxVolume, int currentVolume) { super(volumeControl, maxVolume, currentVolume); } diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 33e7d8ea1b94..30719fd8c750 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -1277,7 +1277,8 @@ public class MediaScanner implements AutoCloseable { // we need to query the database in small batches, to avoid problems // with CursorWindow positioning. long lastId = Long.MIN_VALUE; - Uri limitUri = mFilesUri.buildUpon().appendQueryParameter("limit", "1000").build(); + Uri limitUri = mFilesUri.buildUpon() + .appendQueryParameter(MediaStore.PARAM_LIMIT, "1000").build(); while (true) { selectionArgs[0] = "" + lastId; diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl index fa6e03430315..e3608086395e 100644 --- a/media/java/android/media/session/ISessionManager.aidl +++ b/media/java/android/media/session/ISessionManager.aidl @@ -18,12 +18,12 @@ package android.media.session; import android.content.ComponentName; import android.media.IRemoteVolumeController; import android.media.Session2Token; -import android.media.session.ControllerLink; import android.media.session.IActiveSessionsListener; import android.media.session.ICallback; import android.media.session.IOnMediaKeyListener; import android.media.session.IOnVolumeKeyLongPressListener; import android.media.session.ISession2TokensListener; +import android.media.session.MediaSession; import android.media.session.SessionCallbackLink; import android.media.session.SessionLink; import android.os.Bundle; @@ -38,7 +38,7 @@ interface ISessionManager { int userId); void notifySession2Created(in Session2Token sessionToken); void notifySession2Destroyed(in Session2Token sessionToken); - List<ControllerLink> getSessions(in ComponentName compName, int userId); + List<MediaSession.Token> getSessions(in ComponentName compName, int userId); List<Session2Token> getSession2Tokens(int userId); void dispatchMediaKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent, boolean needWakeLock); diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 1a185e982cf1..682e79ae1401 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -392,16 +392,6 @@ public final class MediaSession { } /** - * Notify the system that the remote volume changed. - * - * @param provider The provider that is handling volume changes. - * @hide - */ - public void notifyRemoteVolumeChanged(VolumeProvider provider) { - mImpl.notifyRemoteVolumeChanged(provider); - } - - /** * Returns the name of the package that sent the last media button, transport control, or * command from controllers and the system. This is only valid while in a request callback, such * as {@link Callback#onPlay}. diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index cae4d1749287..756386736aec 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -202,10 +202,10 @@ public final class MediaSessionManager { @Nullable ComponentName notificationListener, int userId) { ArrayList<MediaController> controllers = new ArrayList<MediaController>(); try { - List<ControllerLink> binders = mService.getSessions(notificationListener, userId); - int size = binders.size(); + List<MediaSession.Token> tokens = mService.getSessions(notificationListener, userId); + int size = tokens.size(); for (int i = 0; i < size; i++) { - MediaController controller = new MediaController(mContext, binders.get(i)); + MediaController controller = new MediaController(mContext, tokens.get(i)); controllers.add(controller); } } catch (RemoteException e) { diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index 21c58b377e94..09b755912c43 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -20,9 +20,9 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.annotation.StringDef; import android.annotation.SystemApi; -import android.annotation.SdkConstant.SdkConstantType; import android.app.Activity; import android.content.ComponentName; import android.content.ContentResolver; @@ -1680,6 +1680,7 @@ public final class TvContract { TYPE_ISDB_T, TYPE_ISDB_TB, TYPE_ISDB_S, + TYPE_ISDB_S3, TYPE_ISDB_C, TYPE_1SEG, TYPE_DTMB, @@ -1821,6 +1822,13 @@ public final class TvContract { public static final String TYPE_ISDB_S = "TYPE_ISDB_S"; /** + * The channel type for ISDB-S3 (satellite). + * + * @see #COLUMN_TYPE + */ + public static final String TYPE_ISDB_S3 = "TYPE_ISDB_S3"; + + /** * The channel type for ISDB-C (cable). * * @see #COLUMN_TYPE @@ -2026,6 +2034,7 @@ public final class TvContract { * {@link #TYPE_DVB_T2}, * {@link #TYPE_ISDB_C}, * {@link #TYPE_ISDB_S}, + * {@link #TYPE_ISDB_S3}, * {@link #TYPE_ISDB_T}, * {@link #TYPE_ISDB_TB}, * {@link #TYPE_NTSC}, diff --git a/media/jni/Android.bp b/media/jni/Android.bp index 852d2962ee66..0fb4d2a5e446 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -94,26 +94,14 @@ cc_library_shared { ], shared_libs: [ - // MediaCas - "android.hardware.cas@1.0", - "android.hardware.cas.native@1.0", - "android.hidl.allocator@1.0", - "libhidlbase", - "libhidlmemory", - - "libmediametrics", - "libbinder", // Used by JWakeLock and MediaMetrics. - - "libutils", // Have to use shared lib to make libandroid_runtime behave correctly. - // Otherwise, AndroidRuntime::getJNIEnv() will return NULL. - - // NDK or NDK-compliant + // NDK or LLNDK or NDK-compliant "libandroid", "libbinder_ndk", "libmediandk", + "libmediametrics", "libnativehelper_compat_libc++", "liblog", - "libz", + "libvndksupport", ], header_libs: [ @@ -122,6 +110,16 @@ cc_library_shared { ], static_libs: [ + // MediaCas + "android.hidl.allocator@1.0", + "android.hidl.memory@1.0", + "libhidlbase", + "libhidlmemory", + "libhidltransport", + "libhwbinder", + "libbinderthreadstate", + + // MediaPlayer2 implementation "libbase", "libcrypto", "libcutils", @@ -131,10 +129,11 @@ cc_library_shared { "libmediaplayer2-protos", "libmediandk_utils", "libmediautils", + "libprocessgroup", "libprotobuf-cpp-lite", "libstagefright", "libstagefright_esds", - "libstagefright_foundation", + "libstagefright_foundation_without_imemory", "libstagefright_httplive", "libstagefright_id3", "libstagefright_mpeg2support", @@ -142,6 +141,7 @@ cc_library_shared { "libstagefright_player2", "libstagefright_rtsp_player2", "libstagefright_timedtext2", + "libutils", "libmedia2_jni_core", ], @@ -161,6 +161,7 @@ cc_library_shared { "-Wno-error=deprecated-declarations", "-Wunused", "-Wunreachable-code", + "-fvisibility=hidden", ], ldflags: ["-Wl,--exclude-libs=ALL,-error-limit=0"], diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp index 417a427b6c7c..7168b2dadf92 100644 --- a/media/jni/android_media_ImageReader.cpp +++ b/media/jni/android_media_ImageReader.cpp @@ -379,24 +379,9 @@ static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz, jint w String8 consumerName = String8::format("ImageReader-%dx%df%xm%d-%d-%d", width, height, format, maxImages, getpid(), createProcessUniqueId()); - uint32_t consumerUsage = GRALLOC_USAGE_SW_READ_OFTEN; - bool needUsageOverride = ndkUsage != CONSUMER_BUFFER_USAGE_UNKNOWN; - uint64_t outProducerUsage = 0; - uint64_t outConsumerUsage = 0; - android_hardware_HardwareBuffer_convertToGrallocUsageBits(&outProducerUsage, &outConsumerUsage, - ndkUsage, 0); - - if (isFormatOpaque(nativeFormat)) { - // Use the SW_READ_NEVER usage to tell producer that this format is not for preview or video - // encoding. The only possibility will be ZSL output. - consumerUsage = GRALLOC_USAGE_SW_READ_NEVER; - if (needUsageOverride) { - consumerUsage = android_convertGralloc1To0Usage(0, outConsumerUsage); - } - } else if (needUsageOverride) { - ALOGW("Consumer usage override for non-opaque format is not implemented yet, " - "ignore the provided usage from the application"); - } + uint64_t consumerUsage = + android_hardware_HardwareBuffer_convertToGrallocUsageBits(ndkUsage); + bufferConsumer = new BufferItemConsumer(gbConsumer, consumerUsage, maxImages, /*controlledByApp*/true); if (bufferConsumer == nullptr) { diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java index 5ab50925a268..0340cec7432d 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java @@ -857,7 +857,7 @@ public class CameraTestUtils extends Assert { // JPEG doesn't have pixelstride and rowstride, treat it as 1D buffer. // Same goes for DEPTH_POINT_CLOUD if (format == ImageFormat.JPEG || format == ImageFormat.DEPTH_POINT_CLOUD || - format == ImageFormat.RAW_PRIVATE) { + format == ImageFormat.DEPTH_JPEG || format == ImageFormat.RAW_PRIVATE) { buffer = planes[0].getBuffer(); assertNotNull("Fail to get jpeg or depth ByteBuffer", buffer); data = new byte[buffer.remaining()]; @@ -940,6 +940,7 @@ public class CameraTestUtils extends Assert { case ImageFormat.RAW_PRIVATE: case ImageFormat.DEPTH16: case ImageFormat.DEPTH_POINT_CLOUD: + case ImageFormat.DEPTH_JPEG: assertEquals("JPEG/RAW/depth Images should have one plane", 1, planes.length); break; default: @@ -1363,6 +1364,9 @@ public class CameraTestUtils extends Assert { case ImageFormat.RAW_PRIVATE: validateRawPrivateData(data, width, height, image.getTimestamp(), filePath); break; + case ImageFormat.DEPTH_JPEG: + validateDepthJpegData(data, width, height, format, image.getTimestamp(), filePath); + break; default: throw new UnsupportedOperationException("Unsupported format for validation: " + format); @@ -1528,6 +1532,23 @@ public class CameraTestUtils extends Assert { } + private static void validateDepthJpegData(byte[] depthData, int width, int height, int format, + long ts, String filePath) { + + if (VERBOSE) Log.v(TAG, "Validating depth jpeg data"); + + // Can't validate size since it is variable + + if (DEBUG && filePath != null) { + String fileName = + filePath + "/" + width + "x" + height + "_" + ts / 1e6 + ".jpg"; + dumpFile(fileName, depthData); + } + + return; + + } + public static <T> T getValueNotNull(CaptureResult result, CaptureResult.Key<T> key) { if (result == null) { throw new IllegalArgumentException("Result must not be null"); diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java index dce8b619494e..eac8d2a3b410 100644 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java +++ b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java @@ -18,7 +18,7 @@ package android.net.dhcp; import android.annotation.NonNull; import android.annotation.Nullable; -import android.net.util.FdEventsReader; +import android.net.shared.FdEventsReader; import android.os.Handler; import android.system.Os; diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java index f20e01636d72..4315d34ba447 100644 --- a/packages/NetworkStack/src/android/net/ip/IpClient.java +++ b/packages/NetworkStack/src/android/net/ip/IpClient.java @@ -40,15 +40,12 @@ import android.net.ip.IIpClientCallbacks; import android.net.metrics.IpConnectivityLog; import android.net.metrics.IpManagerEvent; import android.net.shared.InitialConfiguration; -import android.net.shared.NetdService; import android.net.shared.ProvisioningConfiguration; import android.net.util.InterfaceParams; import android.net.util.SharedLog; import android.os.ConditionVariable; -import android.os.INetworkManagementService; import android.os.Message; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemClock; import android.text.TextUtils; import android.util.LocalLog; @@ -64,7 +61,7 @@ import com.android.internal.util.Preconditions; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.WakeupMessage; -import com.android.server.net.NetlinkTracker; +import com.android.server.NetworkObserverRegistry; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -338,8 +335,9 @@ public class IpClient extends StateMachine { private final Dependencies mDependencies; private final CountDownLatch mShutdownLatch; private final ConnectivityManager mCm; - private final INetworkManagementService mNwService; - private final NetlinkTracker mNetlinkTracker; + private final INetd mNetd; + private final NetworkObserverRegistry mObserverRegistry; + private final IpClientLinkObserver mLinkObserver; private final WakeupMessage mProvisioningTimeoutAlarm; private final WakeupMessage mDhcpActionTimeoutAlarm; private final SharedLog mLog; @@ -373,15 +371,6 @@ public class IpClient extends StateMachine { private final ConditionVariable mApfDataSnapshotComplete = new ConditionVariable(); public static class Dependencies { - public INetworkManagementService getNMS() { - return INetworkManagementService.Stub.asInterface( - ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); - } - - public INetd getNetd() { - return NetdService.getInstance(); - } - /** * Get interface parameters for the specified interface. */ @@ -390,26 +379,14 @@ public class IpClient extends StateMachine { } } - public IpClient(Context context, String ifName, IIpClientCallbacks callback) { - this(context, ifName, callback, new Dependencies()); - } - - /** - * An expanded constructor, useful for dependency injection. - * TODO: migrate all test users to mock IpClient directly and remove this ctor. - */ public IpClient(Context context, String ifName, IIpClientCallbacks callback, - INetworkManagementService nwService) { - this(context, ifName, callback, new Dependencies() { - @Override - public INetworkManagementService getNMS() { - return nwService; - } - }); + NetworkObserverRegistry observerRegistry) { + this(context, ifName, callback, observerRegistry, new Dependencies()); } @VisibleForTesting - IpClient(Context context, String ifName, IIpClientCallbacks callback, Dependencies deps) { + IpClient(Context context, String ifName, IIpClientCallbacks callback, + NetworkObserverRegistry observerRegistry, Dependencies deps) { super(IpClient.class.getSimpleName() + "." + ifName); Preconditions.checkNotNull(ifName); Preconditions.checkNotNull(callback); @@ -422,7 +399,7 @@ public class IpClient extends StateMachine { mDependencies = deps; mShutdownLatch = new CountDownLatch(1); mCm = mContext.getSystemService(ConnectivityManager.class); - mNwService = deps.getNMS(); + mObserverRegistry = observerRegistry; sSmLogs.putIfAbsent(mInterfaceName, new SharedLog(MAX_LOG_RECORDS, mTag)); mLog = sSmLogs.get(mInterfaceName); @@ -433,19 +410,15 @@ public class IpClient extends StateMachine { // TODO: Consider creating, constructing, and passing in some kind of // InterfaceController.Dependencies class. - mInterfaceCtrl = new InterfaceController(mInterfaceName, deps.getNetd(), mLog); + mNetd = mContext.getSystemService(INetd.class); + mInterfaceCtrl = new InterfaceController(mInterfaceName, mNetd, mLog); - mNetlinkTracker = new NetlinkTracker( + mLinkObserver = new IpClientLinkObserver( mInterfaceName, - new NetlinkTracker.Callback() { - @Override - public void update() { - sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED); - } - }) { + () -> sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED)) { @Override - public void interfaceAdded(String iface) { - super.interfaceAdded(iface); + public void onInterfaceAdded(String iface) { + super.onInterfaceAdded(iface); if (mClatInterfaceName.equals(iface)) { mCallback.setNeighborDiscoveryOffload(false); } else if (!mInterfaceName.equals(iface)) { @@ -457,8 +430,8 @@ public class IpClient extends StateMachine { } @Override - public void interfaceRemoved(String iface) { - super.interfaceRemoved(iface); + public void onInterfaceRemoved(String iface) { + super.onInterfaceRemoved(iface); // TODO: Also observe mInterfaceName going down and take some // kind of appropriate action. if (mClatInterfaceName.equals(iface)) { @@ -570,19 +543,11 @@ public class IpClient extends StateMachine { } private void startStateMachineUpdaters() { - try { - mNwService.registerObserver(mNetlinkTracker); - } catch (RemoteException e) { - logError("Couldn't register NetlinkTracker: %s", e); - } + mObserverRegistry.registerObserverForNonblockingCallback(mLinkObserver); } private void stopStateMachineUpdaters() { - try { - mNwService.unregisterObserver(mNetlinkTracker); - } catch (RemoteException e) { - logError("Couldn't unregister NetlinkTracker: %s", e); - } + mObserverRegistry.unregisterObserver(mLinkObserver); } @Override @@ -805,7 +770,7 @@ public class IpClient extends StateMachine { // we should only call this if we know for sure that there are no IP addresses // assigned to the interface, etc. private void resetLinkProperties() { - mNetlinkTracker.clearLinkProperties(); + mLinkObserver.clearLinkProperties(); mConfiguration = null; mDhcpResults = null; mTcpBufferSizes = ""; @@ -984,10 +949,10 @@ public class IpClient extends StateMachine { // - IPv6 DNS servers // // N.B.: this is fundamentally race-prone and should be fixed by - // changing NetlinkTracker from a hybrid edge/level model to an + // changing IpClientLinkObserver from a hybrid edge/level model to an // edge-only model, or by giving IpClient its own netlink socket(s) // so as to track all required information directly. - LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties(); + LinkProperties netlinkLinkProperties = mLinkObserver.getLinkProperties(); newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses()); for (RouteInfo route : netlinkLinkProperties.getRoutes()) { newLp.addRoute(route); @@ -1166,8 +1131,7 @@ public class IpClient extends StateMachine { // necessary or does reading from settings at startup suffice?). final int numSolicits = 5; final int interSolicitIntervalMs = 750; - setNeighborParameters(mDependencies.getNetd(), mInterfaceName, - numSolicits, interSolicitIntervalMs); + setNeighborParameters(mNetd, mInterfaceName, numSolicits, interSolicitIntervalMs); } catch (Exception e) { mLog.e("Failed to adjust neighbor parameters", e); // Carry on using the system defaults (currently: 3, 1000); diff --git a/packages/NetworkStack/src/android/net/ip/IpClientLinkObserver.java b/packages/NetworkStack/src/android/net/ip/IpClientLinkObserver.java new file mode 100644 index 000000000000..8ad99aa0399a --- /dev/null +++ b/packages/NetworkStack/src/android/net/ip/IpClientLinkObserver.java @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.ip; + +import android.net.InetAddresses; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.RouteInfo; +import android.util.Log; + +import com.android.server.NetworkObserver; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +/** + * Keeps track of link configuration received from Netd. + * + * An instance of this class is constructed by passing in an interface name and a callback. The + * owner is then responsible for registering the tracker with NetworkObserverRegistry. When the + * class receives update notifications, it applies the update to its local LinkProperties, and if + * something has changed, notifies its owner of the update via the callback. + * + * The owner can then call {@code getLinkProperties()} in order to find out + * what changed. If in the meantime the LinkProperties stored here have changed, + * this class will return the current LinkProperties. Because each change + * triggers an update callback after the change is made, the owner may get more + * callbacks than strictly necessary (some of which may be no-ops), but will not + * be out of sync once all callbacks have been processed. + * + * Threading model: + * + * - The owner of this class is expected to create it, register it, and call + * getLinkProperties or clearLinkProperties on its thread. + * - Most of the methods in the class are implementing NetworkObserver and are called + * on the handler used to register the observer. + * - All accesses to mLinkProperties must be synchronized(this). All the other + * member variables are immutable once the object is constructed. + * + * @hide + */ +public class IpClientLinkObserver implements NetworkObserver { + private final String mTag; + + /** + * Callback used by {@link IpClientLinkObserver} to send update notifications. + */ + public interface Callback { + /** + * Called when some properties of the link were updated. + */ + void update(); + } + + private final String mInterfaceName; + private final Callback mCallback; + private final LinkProperties mLinkProperties; + private DnsServerRepository mDnsServerRepository; + + private static final boolean DBG = false; + + public IpClientLinkObserver(String iface, Callback callback) { + mTag = "NetlinkTracker/" + iface; + mInterfaceName = iface; + mCallback = callback; + mLinkProperties = new LinkProperties(); + mLinkProperties.setInterfaceName(mInterfaceName); + mDnsServerRepository = new DnsServerRepository(); + } + + private void maybeLog(String operation, String iface, LinkAddress address) { + if (DBG) { + Log.d(mTag, operation + ": " + address + " on " + iface + + " flags " + address.getFlags() + " scope " + address.getScope()); + } + } + + private void maybeLog(String operation, Object o) { + if (DBG) { + Log.d(mTag, operation + ": " + o.toString()); + } + } + + @Override + public void onInterfaceRemoved(String iface) { + maybeLog("interfaceRemoved", iface); + if (mInterfaceName.equals(iface)) { + // Our interface was removed. Clear our LinkProperties and tell our owner that they are + // now empty. Note that from the moment that the interface is removed, any further + // interface-specific messages (e.g., RTM_DELADDR) will not reach us, because the netd + // code that parses them will not be able to resolve the ifindex to an interface name. + clearLinkProperties(); + mCallback.update(); + } + } + + @Override + public void onInterfaceAddressUpdated(LinkAddress address, String iface) { + if (mInterfaceName.equals(iface)) { + maybeLog("addressUpdated", iface, address); + boolean changed; + synchronized (this) { + changed = mLinkProperties.addLinkAddress(address); + } + if (changed) { + mCallback.update(); + } + } + } + + @Override + public void onInterfaceAddressRemoved(LinkAddress address, String iface) { + if (mInterfaceName.equals(iface)) { + maybeLog("addressRemoved", iface, address); + boolean changed; + synchronized (this) { + changed = mLinkProperties.removeLinkAddress(address); + } + if (changed) { + mCallback.update(); + } + } + } + + @Override + public void onRouteUpdated(RouteInfo route) { + if (mInterfaceName.equals(route.getInterface())) { + maybeLog("routeUpdated", route); + boolean changed; + synchronized (this) { + changed = mLinkProperties.addRoute(route); + } + if (changed) { + mCallback.update(); + } + } + } + + @Override + public void onRouteRemoved(RouteInfo route) { + if (mInterfaceName.equals(route.getInterface())) { + maybeLog("routeRemoved", route); + boolean changed; + synchronized (this) { + changed = mLinkProperties.removeRoute(route); + } + if (changed) { + mCallback.update(); + } + } + } + + @Override + public void onInterfaceDnsServerInfo(String iface, long lifetime, String[] addresses) { + if (mInterfaceName.equals(iface)) { + maybeLog("interfaceDnsServerInfo", Arrays.toString(addresses)); + boolean changed = mDnsServerRepository.addServers(lifetime, addresses); + if (changed) { + synchronized (this) { + mDnsServerRepository.setDnsServersOn(mLinkProperties); + } + mCallback.update(); + } + } + } + + /** + * Returns a copy of this object's LinkProperties. + */ + public synchronized LinkProperties getLinkProperties() { + return new LinkProperties(mLinkProperties); + } + + /** + * Reset this object's LinkProperties. + */ + public synchronized void clearLinkProperties() { + // Clear the repository before clearing mLinkProperties. That way, if a clear() happens + // while interfaceDnsServerInfo() is being called, we'll end up with no DNS servers in + // mLinkProperties, as desired. + mDnsServerRepository = new DnsServerRepository(); + mLinkProperties.clear(); + mLinkProperties.setInterfaceName(mInterfaceName); + } + + /** + * Tracks DNS server updates received from Netlink. + * + * The network may announce an arbitrary number of DNS servers in Router Advertisements at any + * time. Each announcement has a lifetime; when the lifetime expires, the servers should not be + * used any more. In this way, the network can gracefully migrate clients from one set of DNS + * servers to another. Announcements can both raise and lower the lifetime, and an announcement + * can expire servers by announcing them with a lifetime of zero. + * + * Typically the system will only use a small number (2 or 3; {@code NUM_CURRENT_SERVERS}) of + * DNS servers at any given time. These are referred to as the current servers. In case all the + * current servers expire, the class also keeps track of a larger (but limited) number of + * servers that are promoted to current servers when the current ones expire. In order to + * minimize updates to the rest of the system (and potentially expensive cache flushes) this + * class attempts to keep the list of current servers constant where possible. More + * specifically, the list of current servers is only updated if a new server is learned and + * there are not yet {@code NUM_CURRENT_SERVERS} current servers, or if one or more of the + * current servers expires or is pushed out of the set. Therefore, the current servers will not + * necessarily be the ones with the highest lifetime, but the ones learned first. + * + * This is by design: if instead the class always preferred the servers with the highest + * lifetime, a (misconfigured?) network where two or more routers announce more than + * {@code NUM_CURRENT_SERVERS} unique servers would cause persistent oscillations. + * + * TODO: Currently servers are only expired when a new DNS update is received. + * Update them using timers, or possibly on every notification received by NetlinkTracker. + * + * Threading model: run by NetlinkTracker. Methods are synchronized(this) just in case netlink + * notifications are sent by multiple threads. If future threads use alarms to expire, those + * alarms must also be synchronized(this). + * + */ + private static class DnsServerRepository { + + /** How many DNS servers we will use. 3 is suggested by RFC 6106. */ + static final int NUM_CURRENT_SERVERS = 3; + + /** How many DNS servers we'll keep track of, in total. */ + static final int NUM_SERVERS = 12; + + /** Stores up to {@code NUM_CURRENT_SERVERS} DNS servers we're currently using. */ + private Set<InetAddress> mCurrentServers; + + public static final String TAG = "DnsServerRepository"; + + /** + * Stores all the DNS servers we know about, for use when the current servers expire. + * Always sorted in order of decreasing expiry. The elements in this list are also the + * values of mIndex, and may be elements in mCurrentServers. + */ + private ArrayList<DnsServerEntry> mAllServers; + + /** + * Indexes the servers so we can update their lifetimes more quickly in the common case + * where servers are not being added, but only being refreshed. + */ + private HashMap<InetAddress, DnsServerEntry> mIndex; + + DnsServerRepository() { + mCurrentServers = new HashSet<>(); + mAllServers = new ArrayList<>(NUM_SERVERS); + mIndex = new HashMap<>(NUM_SERVERS); + } + + /** Sets the DNS servers of the provided LinkProperties object to the current servers. */ + public synchronized void setDnsServersOn(LinkProperties lp) { + lp.setDnsServers(mCurrentServers); + } + + /** + * Notifies the class of new DNS server information. + * @param lifetime the time in seconds that the DNS servers are valid. + * @param addresses the string representations of the IP addresses of DNS servers to use. + */ + public synchronized boolean addServers(long lifetime, String[] addresses) { + // The lifetime is actually an unsigned 32-bit number, but Java doesn't have unsigned. + // Technically 0xffffffff (the maximum) is special and means "forever", but 2^32 seconds + // (136 years) is close enough. + long now = System.currentTimeMillis(); + long expiry = now + 1000 * lifetime; + + // Go through the list of servers. For each one, update the entry if one exists, and + // create one if it doesn't. + for (String addressString : addresses) { + InetAddress address; + try { + address = InetAddresses.parseNumericAddress(addressString); + } catch (IllegalArgumentException ex) { + continue; + } + + if (!updateExistingEntry(address, expiry)) { + // There was no entry for this server. Create one, unless it's already expired + // (i.e., if the lifetime is zero; it cannot be < 0 because it's unsigned). + if (expiry > now) { + DnsServerEntry entry = new DnsServerEntry(address, expiry); + mAllServers.add(entry); + mIndex.put(address, entry); + } + } + } + + // Sort the servers by expiry. + Collections.sort(mAllServers); + + // Prune excess entries and update the current server list. + return updateCurrentServers(); + } + + private synchronized boolean updateExistingEntry(InetAddress address, long expiry) { + DnsServerEntry existing = mIndex.get(address); + if (existing != null) { + existing.expiry = expiry; + return true; + } + return false; + } + + private synchronized boolean updateCurrentServers() { + long now = System.currentTimeMillis(); + boolean changed = false; + + // Prune excess or expired entries. + for (int i = mAllServers.size() - 1; i >= 0; i--) { + if (i >= NUM_SERVERS || mAllServers.get(i).expiry < now) { + DnsServerEntry removed = mAllServers.remove(i); + mIndex.remove(removed.address); + changed |= mCurrentServers.remove(removed.address); + } else { + break; + } + } + + // Add servers to the current set, in order of decreasing lifetime, until it has enough. + // Prefer existing servers over new servers in order to minimize updates to the rest of + // the system and avoid persistent oscillations. + for (DnsServerEntry entry : mAllServers) { + if (mCurrentServers.size() < NUM_CURRENT_SERVERS) { + changed |= mCurrentServers.add(entry.address); + } else { + break; + } + } + return changed; + } + } + + /** + * Represents a DNS server entry with an expiry time. + * + * Implements Comparable so DNS server entries can be sorted by lifetime, longest-lived first. + * The ordering of entries with the same lifetime is unspecified, because given two servers with + * identical lifetimes, we don't care which one we use, and only comparing the lifetime is much + * faster than comparing the IP address as well. + * + * Note: this class has a natural ordering that is inconsistent with equals. + */ + private static class DnsServerEntry implements Comparable<DnsServerEntry> { + /** The IP address of the DNS server. */ + public final InetAddress address; + /** The time until which the DNS server may be used. A Java millisecond time as might be + * returned by currentTimeMillis(). */ + public long expiry; + + DnsServerEntry(InetAddress address, long expiry) throws IllegalArgumentException { + this.address = address; + this.expiry = expiry; + } + + public int compareTo(DnsServerEntry other) { + return Long.compare(other.expiry, this.expiry); + } + } +} diff --git a/packages/NetworkStack/src/android/net/util/PacketReader.java b/packages/NetworkStack/src/android/net/util/PacketReader.java index 4aec6b6753a6..94b1e9f2e14e 100644 --- a/packages/NetworkStack/src/android/net/util/PacketReader.java +++ b/packages/NetworkStack/src/android/net/util/PacketReader.java @@ -18,6 +18,7 @@ package android.net.util; import static java.lang.Math.max; +import android.net.shared.FdEventsReader; import android.os.Handler; import android.system.Os; diff --git a/packages/NetworkStack/src/com/android/server/NetworkObserver.java b/packages/NetworkStack/src/com/android/server/NetworkObserver.java index d3b40a67633d..cccec0bb5d40 100644 --- a/packages/NetworkStack/src/com/android/server/NetworkObserver.java +++ b/packages/NetworkStack/src/com/android/server/NetworkObserver.java @@ -17,6 +17,7 @@ package com.android.server; import android.net.LinkAddress; +import android.net.RouteInfo; /** * Observer for network events, to use with {@link NetworkObserverRegistry}. @@ -77,11 +78,11 @@ public interface NetworkObserver { * @see android.net.INetdUnsolicitedEventListener * #onRouteChanged(boolean, String, String, String) */ - default void onRouteUpdated(String route, String gateway, String ifName) {} + default void onRouteUpdated(RouteInfo route) {} /** * @see android.net.INetdUnsolicitedEventListener * #onRouteChanged(boolean, String, String, String) */ - default void onRouteRemoved(String route, String gateway, String ifName) {} + default void onRouteRemoved(RouteInfo route) {} } diff --git a/packages/NetworkStack/src/com/android/server/NetworkObserverRegistry.java b/packages/NetworkStack/src/com/android/server/NetworkObserverRegistry.java index 14e6c5fdadb9..4f55779f473b 100644 --- a/packages/NetworkStack/src/com/android/server/NetworkObserverRegistry.java +++ b/packages/NetworkStack/src/com/android/server/NetworkObserverRegistry.java @@ -18,11 +18,16 @@ package com.android.server; import android.annotation.NonNull; import android.net.INetd; import android.net.INetdUnsolicitedEventListener; +import android.net.InetAddresses; +import android.net.IpPrefix; import android.net.LinkAddress; +import android.net.RouteInfo; import android.os.Handler; import android.os.RemoteException; +import android.util.Log; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; /** @@ -32,6 +37,7 @@ import java.util.concurrent.ConcurrentHashMap; * all INetworkManagementEventObserver objects that have registered with it. */ public class NetworkObserverRegistry extends INetdUnsolicitedEventListener.Stub { + private static final String TAG = NetworkObserverRegistry.class.getSimpleName(); /** * Constructs a new NetworkObserverRegistry. @@ -50,7 +56,7 @@ public class NetworkObserverRegistry extends INetdUnsolicitedEventListener.Stub netd.registerUnsolicitedEventListener(this); } - private final ConcurrentHashMap<NetworkObserver, Handler> mObservers = + private final ConcurrentHashMap<NetworkObserver, Optional<Handler>> mObservers = new ConcurrentHashMap<>(); /** @@ -58,7 +64,20 @@ public class NetworkObserverRegistry extends INetdUnsolicitedEventListener.Stub * This method may be called on any thread. */ public void registerObserver(@NonNull NetworkObserver observer, @NonNull Handler handler) { - mObservers.put(observer, handler); + if (handler == null) { + throw new IllegalArgumentException("handler must be non-null"); + } + mObservers.put(observer, Optional.of(handler)); + } + + /** + * Registers the specified observer, and start sending callbacks to it. + * + * <p>This method must only be called with callbacks that are nonblocking, such as callbacks + * that only send a message to a StateMachine. + */ + public void registerObserverForNonblockingCallback(@NonNull NetworkObserver observer) { + mObservers.put(observer, Optional.empty()); } /** @@ -77,9 +96,19 @@ public class NetworkObserverRegistry extends INetdUnsolicitedEventListener.Stub private void invokeForAllObservers(@NonNull final NetworkObserverEventCallback callback) { // ConcurrentHashMap#entrySet is weakly consistent: observers that were in the map before // creation will be processed, those added during traversal may or may not. - for (Map.Entry<NetworkObserver, Handler> entry : mObservers.entrySet()) { + for (Map.Entry<NetworkObserver, Optional<Handler>> entry : mObservers.entrySet()) { final NetworkObserver observer = entry.getKey(); - entry.getValue().post(() -> callback.sendCallback(observer)); + final Optional<Handler> handler = entry.getValue(); + if (handler.isPresent()) { + handler.get().post(() -> callback.sendCallback(observer)); + return; + } + + try { + callback.sendCallback(observer); + } catch (RuntimeException e) { + Log.e(TAG, "Error sending callback to observer", e); + } } } @@ -138,10 +167,13 @@ public class NetworkObserverRegistry extends INetdUnsolicitedEventListener.Stub @Override public void onRouteChanged(boolean updated, String route, String gateway, String ifName) { + final RouteInfo processRoute = new RouteInfo(new IpPrefix(route), + ("".equals(gateway)) ? null : InetAddresses.parseNumericAddress(gateway), + ifName); if (updated) { - invokeForAllObservers(o -> o.onRouteUpdated(route, gateway, ifName)); + invokeForAllObservers(o -> o.onRouteUpdated(processRoute)); } else { - invokeForAllObservers(o -> o.onRouteRemoved(route, gateway, ifName)); + invokeForAllObservers(o -> o.onRouteRemoved(processRoute)); } } diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java index 631ee453671a..7405c471808a 100644 --- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java +++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java @@ -117,7 +117,11 @@ public class NetworkStackService extends Service { mObserverRegistry = new NetworkObserverRegistry(); mCm = context.getSystemService(ConnectivityManager.class); - // TODO: call mObserverRegistry here after adding sepolicy changes + try { + mObserverRegistry.register(mNetd); + } catch (RemoteException e) { + mLog.e("Error registering observer on Netd", e); + } } @NonNull @@ -158,7 +162,7 @@ public class NetworkStackService extends Service { @Override public void makeIpClient(String ifName, IIpClientCallbacks cb) throws RemoteException { - final IpClient ipClient = new IpClient(mContext, ifName, cb); + final IpClient ipClient = new IpClient(mContext, ifName, cb, mObserverRegistry); synchronized (mIpClients) { final Iterator<WeakReference<IpClient>> it = mIpClients.iterator(); diff --git a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java index 4ae044dec20b..7e57d1eb00b0 100644 --- a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java +++ b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java @@ -46,7 +46,6 @@ import android.net.RouteInfo; import android.net.shared.InitialConfiguration; import android.net.shared.ProvisioningConfiguration; import android.net.util.InterfaceParams; -import android.os.INetworkManagementService; import android.provider.Settings; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -54,7 +53,8 @@ import android.test.mock.MockContentResolver; import com.android.internal.R; import com.android.internal.util.test.FakeSettingsProvider; -import com.android.server.net.BaseNetworkObserver; +import com.android.server.NetworkObserver; +import com.android.server.NetworkObserverRegistry; import org.junit.Before; import org.junit.Test; @@ -87,15 +87,15 @@ public class IpClientTest { @Mock private Context mContext; @Mock private ConnectivityManager mCm; - @Mock private INetworkManagementService mNMService; + @Mock private NetworkObserverRegistry mObserverRegistry; @Mock private INetd mNetd; @Mock private Resources mResources; @Mock private IIpClientCallbacks mCb; @Mock private AlarmManager mAlarm; - @Mock private IpClient.Dependencies mDependecies; + @Mock private IpClient.Dependencies mDependencies; private MockContentResolver mContentResolver; - private BaseNetworkObserver mObserver; + private NetworkObserver mObserver; private InterfaceParams mIfParams; @Before @@ -104,6 +104,7 @@ public class IpClientTest { when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm); when(mContext.getSystemService(eq(ConnectivityManager.class))).thenReturn(mCm); + when(mContext.getSystemService(INetd.class)).thenReturn(mNetd); when(mContext.getResources()).thenReturn(mResources); when(mResources.getInteger(R.integer.config_networkAvoidBadWifi)) .thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE); @@ -113,28 +114,24 @@ public class IpClientTest { when(mContext.getContentResolver()).thenReturn(mContentResolver); mIfParams = null; - - when(mDependecies.getNMS()).thenReturn(mNMService); - when(mDependecies.getNetd()).thenReturn(mNetd); } private void setTestInterfaceParams(String ifname) { mIfParams = (ifname != null) ? new InterfaceParams(ifname, TEST_IFINDEX, TEST_MAC) : null; - when(mDependecies.getInterfaceParams(anyString())).thenReturn(mIfParams); + when(mDependencies.getInterfaceParams(anyString())).thenReturn(mIfParams); } private IpClient makeIpClient(String ifname) throws Exception { setTestInterfaceParams(ifname); - final IpClient ipc = new IpClient(mContext, ifname, mCb, mDependecies); + final IpClient ipc = new IpClient(mContext, ifname, mCb, mObserverRegistry, mDependencies); verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(ifname, false); verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(ifname); - ArgumentCaptor<BaseNetworkObserver> arg = - ArgumentCaptor.forClass(BaseNetworkObserver.class); - verify(mNMService, times(1)).registerObserver(arg.capture()); + ArgumentCaptor<NetworkObserver> arg = ArgumentCaptor.forClass(NetworkObserver.class); + verify(mObserverRegistry, times(1)).registerObserverForNonblockingCallback(arg.capture()); mObserver = arg.getValue(); - reset(mNMService); + reset(mObserverRegistry); reset(mNetd); // Verify IpClient doesn't call onLinkPropertiesChange() when it starts. verify(mCb, never()).onLinkPropertiesChange(any()); @@ -152,7 +149,8 @@ public class IpClientTest { public void testNullInterfaceNameMostDefinitelyThrows() throws Exception { setTestInterfaceParams(null); try { - final IpClient ipc = new IpClient(mContext, null, mCb, mDependecies); + final IpClient ipc = new IpClient( + mContext, null, mCb, mObserverRegistry, mDependencies); ipc.shutdown(); fail(); } catch (NullPointerException npe) { @@ -165,7 +163,8 @@ public class IpClientTest { final String ifname = "lo"; setTestInterfaceParams(ifname); try { - final IpClient ipc = new IpClient(mContext, ifname, null, mDependecies); + final IpClient ipc = new IpClient( + mContext, ifname, null, mObserverRegistry, mDependencies); ipc.shutdown(); fail(); } catch (NullPointerException npe) { @@ -176,14 +175,16 @@ public class IpClientTest { @Test public void testInvalidInterfaceDoesNotThrow() throws Exception { setTestInterfaceParams(TEST_IFNAME); - final IpClient ipc = new IpClient(mContext, TEST_IFNAME, mCb, mDependecies); + final IpClient ipc = new IpClient( + mContext, TEST_IFNAME, mCb, mObserverRegistry, mDependencies); ipc.shutdown(); } @Test public void testInterfaceNotFoundFailsImmediately() throws Exception { setTestInterfaceParams(null); - final IpClient ipc = new IpClient(mContext, TEST_IFNAME, mCb, mDependecies); + final IpClient ipc = new IpClient( + mContext, TEST_IFNAME, mCb, mObserverRegistry, mDependencies); ipc.startProvisioning(new ProvisioningConfiguration()); verify(mCb, times(1)).onProvisioningFailure(any()); ipc.shutdown(); @@ -247,13 +248,13 @@ public class IpClientTest { // Add N - 1 addresses for (int i = 0; i < lastAddr; i++) { - mObserver.addressUpdated(iface, new LinkAddress(addresses[i])); + mObserver.onInterfaceAddressUpdated(new LinkAddress(addresses[i]), iface); verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(any()); reset(mCb); } // Add Nth address - mObserver.addressUpdated(iface, new LinkAddress(addresses[lastAddr])); + mObserver.onInterfaceAddressUpdated(new LinkAddress(addresses[lastAddr]), iface); LinkProperties want = linkproperties(links(addresses), routes(prefixes)); want.setInterfaceName(iface); verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).onProvisioningSuccess(argThat( diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java index 8b00ed053b26..00b3736f8d6b 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java +++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java @@ -23,6 +23,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; + import com.android.printspooler.R; /** @@ -366,7 +367,8 @@ public final class PrintContentView extends ViewGroup implements View.OnClickLis // and is janky. Now it is there but transparent, doing nothing. mEmbeddedContentScrim.setOnClickListener(null); mEmbeddedContentScrim.setClickable(false); - mExpandCollapseIcon.setBackgroundResource(R.drawable.ic_expand_more); + mExpandCollapseIcon.setBackgroundResource( + com.android.internal.R.drawable.ic_expand_more); } else { if (mMoreOptionsButton.getVisibility() != View.GONE) { mMoreOptionsButton.setVisibility(View.VISIBLE); diff --git a/packages/SettingsLib/res/drawable/ic_info_outline_24dp.xml b/packages/SettingsLib/res/drawable/ic_info_outline_24dp.xml deleted file mode 100644 index 46b83094f9a8..000000000000 --- a/packages/SettingsLib/res/drawable/ic_info_outline_24dp.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2016 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0" - android:tint="?android:attr/textColorSecondary"> - <path - android:fillColor="#FF000000" - android:pathData="M11,7h2v2h-2z"/> - <path - android:fillColor="#FF000000" - android:pathData="M11,11h2v6h-2z"/> - <path - android:fillColor="#FF000000" - android:pathData="M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10s10,-4.48 10,-10C22,6.48 17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8c0,-4.41 3.59,-8 8,-8s8,3.59 8,8C20,16.41 16.41,20 12,20z"/> -</vector> diff --git a/packages/SettingsLib/res/drawable/ic_landscape_from_auto_rotate.xml b/packages/SettingsLib/res/drawable/ic_landscape_from_auto_rotate.xml deleted file mode 100644 index 44b18661ae60..000000000000 --- a/packages/SettingsLib/res/drawable/ic_landscape_from_auto_rotate.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:name="ic_rotate_to_landscape" - android:width="24dp" - android:height="24dp" - android:viewportHeight="24.0" - android:viewportWidth="24.0" - android:tint="?android:attr/colorControlNormal" > - <path - android:fillColor="#FF000000" - android:pathData="M12.72,23H11C5.49,23 1,18.51 1,13h2c0,3.78 2.63,6.95 6.15,7.79L8.13,19L9.87,18L12.72,23zM13,1h-1.72l2.85,5L15.87,5l-1.02,-1.79C18.37,4.05 21,7.22 21,11h2C23,5.49 18.51,1 13,1zM10.23,6L18,13.76L13.77,18L6,10.24L10.23,6C10.23,6 10.23,6 10.23,6M10.23,4C9.72,4 9.21,4.2 8.82,4.59L4.59,8.82c-0.78,0.78 -0.78,2.04 0,2.82l7.77,7.77c0.39,0.39 0.9,0.59 1.41,0.59c0.51,0 1.02,-0.2 1.41,-0.59l4.24,-4.24c0.78,-0.78 0.78,-2.04 0,-2.82l-7.77,-7.77C11.26,4.2 10.75,4 10.23,4L10.23,4z"/> -</vector> diff --git a/packages/SettingsLib/res/drawable/notification_auto_importance.xml b/packages/SettingsLib/res/drawable/notification_auto_importance.xml deleted file mode 100644 index c94615312306..000000000000 --- a/packages/SettingsLib/res/drawable/notification_auto_importance.xml +++ /dev/null @@ -1,27 +0,0 @@ -<!-- - Copyright (C) 2016 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0"> - <path - android:fillColor="#FFFFFFFF" - android:pathData="M10.8,12.7l2.4,0l-1.2,-3.7z"/> - <path - android:fillColor="#FF000000" - android:pathData="M12,2C6.5,2 2,6.5 2,12s4.5,10 10,10s10,-4.5 10,-10S17.5,2 12,2zM14.3,16l-0.7,-2h-3.2l-0.7,2H7.8L11,7h2l3.2,9H14.3z"/> -</vector>
\ No newline at end of file diff --git a/packages/SettingsLib/res/layout/zen_mode_condition.xml b/packages/SettingsLib/res/layout/zen_mode_condition.xml index c85a8922a552..32221744e26f 100644 --- a/packages/SettingsLib/res/layout/zen_mode_condition.xml +++ b/packages/SettingsLib/res/layout/zen_mode_condition.xml @@ -67,7 +67,7 @@ android:layout_toStartOf="@android:id/button2" android:contentDescription="@string/accessibility_manual_zen_less_time" android:tint="?android:attr/colorAccent" - android:src="@drawable/ic_minus" /> + android:src="@*android:drawable/ic_minus" /> <ImageView android:id="@android:id/button2" @@ -79,6 +79,6 @@ android:layout_centerVertical="true" android:contentDescription="@string/accessibility_manual_zen_more_time" android:tint="?android:attr/colorAccent" - android:src="@drawable/ic_plus" /> + android:src="@*android:drawable/ic_plus" /> -</RelativeLayout>
\ No newline at end of file +</RelativeLayout> diff --git a/packages/SettingsLib/res/values/styles.xml b/packages/SettingsLib/res/values/styles.xml index bae838749966..015f52cde789 100644 --- a/packages/SettingsLib/res/values/styles.xml +++ b/packages/SettingsLib/res/values/styles.xml @@ -24,7 +24,7 @@ <style name="BorderlessButton"> <item name="android:padding">12dp</item> - <item name="android:background">@drawable/btn_borderless_rect</item> + <item name="android:background">@*android:drawable/btn_borderless_rect</item> <item name="android:gravity">center</item> </style> </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java index c05915685e83..bed303078805 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java @@ -296,7 +296,7 @@ public class A2dpProfile implements LocalBluetoothProfile { } public int getDrawableResource(BluetoothClass btClass) { - return R.drawable.ic_bt_headphones_a2dp; + return com.android.internal.R.drawable.ic_bt_headphones_a2dp; } protected void finalize() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java index 873dd1a643a4..4ce9d3e2dff2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java @@ -205,7 +205,7 @@ final class A2dpSinkProfile implements LocalBluetoothProfile { } public int getDrawableResource(BluetoothClass btClass) { - return R.drawable.ic_bt_headphones_a2dp; + return com.android.internal.R.drawable.ic_bt_headphones_a2dp; } protected void finalize() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java index ee80aa159ab1..a8a0b6df95c6 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java @@ -61,13 +61,14 @@ public class BluetoothUtils { if (btClass != null) { switch (btClass.getMajorDeviceClass()) { case BluetoothClass.Device.Major.COMPUTER: - return new Pair<>(getBluetoothDrawable(context, R.drawable.ic_bt_laptop, level, - iconScale), + return new Pair<>(getBluetoothDrawable(context, + com.android.internal.R.drawable.ic_bt_laptop, level, iconScale), context.getString(R.string.bluetooth_talkback_computer)); case BluetoothClass.Device.Major.PHONE: return new Pair<>( - getBluetoothDrawable(context, R.drawable.ic_bt_cellphone, level, + getBluetoothDrawable(context, + com.android.internal.R.drawable.ic_bt_cellphone, level, iconScale), context.getString(R.string.bluetooth_talkback_phone)); @@ -79,7 +80,8 @@ public class BluetoothUtils { case BluetoothClass.Device.Major.IMAGING: return new Pair<>( - getBluetoothDrawable(context, R.drawable.ic_settings_print, level, + getBluetoothDrawable(context, + com.android.internal.R.drawable.ic_settings_print, level, iconScale), context.getString(R.string.bluetooth_talkback_imaging)); @@ -98,19 +100,22 @@ public class BluetoothUtils { if (btClass != null) { if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) { return new Pair<>( - getBluetoothDrawable(context, R.drawable.ic_bt_headset_hfp, level, + getBluetoothDrawable(context, + com.android.internal.R.drawable.ic_bt_headset_hfp, level, iconScale), context.getString(R.string.bluetooth_talkback_headset)); } if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) { return new Pair<>( - getBluetoothDrawable(context, R.drawable.ic_bt_headphones_a2dp, level, + getBluetoothDrawable(context, + com.android.internal.R.drawable.ic_bt_headphones_a2dp, level, iconScale), context.getString(R.string.bluetooth_talkback_headphone)); } } return new Pair<>( - getBluetoothDrawable(context, R.drawable.ic_settings_bluetooth, level, iconScale), + getBluetoothDrawable(context, + com.android.internal.R.drawable.ic_settings_bluetooth, level, iconScale), context.getString(R.string.bluetooth_talkback_bluetooth)); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java index 6b6df9b928e5..a1bf93654774 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java @@ -226,7 +226,7 @@ public class HeadsetProfile implements LocalBluetoothProfile { } public int getDrawableResource(BluetoothClass btClass) { - return R.drawable.ic_bt_headset_hfp; + return com.android.internal.R.drawable.ic_bt_headset_hfp; } protected void finalize() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java index 77dfbe994488..41c1d60ca551 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java @@ -228,7 +228,7 @@ public class HearingAidProfile implements LocalBluetoothProfile { } public int getDrawableResource(BluetoothClass btClass) { - return R.drawable.ic_bt_hearing_aid; + return com.android.internal.R.drawable.ic_bt_hearing_aid; } protected void finalize() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java index c6bb2b304d6c..4bdbc3196556 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java @@ -211,7 +211,7 @@ final class HfpClientProfile implements LocalBluetoothProfile { @Override public int getDrawableResource(BluetoothClass btClass) { - return R.drawable.ic_bt_headset_hfp; + return com.android.internal.R.drawable.ic_bt_headset_hfp; } protected void finalize() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java index 4dc050c6d5e3..35600b538d4d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java @@ -171,7 +171,7 @@ public class HidDeviceProfile implements LocalBluetoothProfile { @Override public int getDrawableResource(BluetoothClass btClass) { - return R.drawable.ic_bt_misc_hid; + return com.android.internal.R.drawable.ic_bt_misc_hid; } protected void finalize() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java index ca840d9a8ba4..7f906f6c5b06 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java @@ -165,7 +165,7 @@ public class HidProfile implements LocalBluetoothProfile { public int getDrawableResource(BluetoothClass btClass) { if (btClass == null) { - return R.drawable.ic_lockscreen_ime; + return com.android.internal.R.drawable.ic_lockscreen_ime; } return getHidClassDrawable(btClass); } @@ -174,11 +174,11 @@ public class HidProfile implements LocalBluetoothProfile { switch (btClass.getDeviceClass()) { case BluetoothClass.Device.PERIPHERAL_KEYBOARD: case BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING: - return R.drawable.ic_lockscreen_ime; + return com.android.internal.R.drawable.ic_lockscreen_ime; case BluetoothClass.Device.PERIPHERAL_POINTING: - return R.drawable.ic_bt_pointing_hid; + return com.android.internal.R.drawable.ic_bt_pointing_hid; default: - return R.drawable.ic_bt_misc_hid; + return com.android.internal.R.drawable.ic_bt_misc_hid; } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java index 6acdcac86d92..0afc878bcc64 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java @@ -200,7 +200,7 @@ public final class MapClientProfile implements LocalBluetoothProfile { } public int getDrawableResource(BluetoothClass btClass) { - return R.drawable.ic_bt_cellphone; + return com.android.internal.R.drawable.ic_bt_cellphone; } protected void finalize() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java index 28975d4b94c2..bea944cb323d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java @@ -196,7 +196,7 @@ public class MapProfile implements LocalBluetoothProfile { } public int getDrawableResource(BluetoothClass btClass) { - return R.drawable.ic_bt_cellphone; + return com.android.internal.R.drawable.ic_bt_cellphone; } protected void finalize() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java index 2d0a0902de24..6638592e8be5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java @@ -153,7 +153,7 @@ public class PanProfile implements LocalBluetoothProfile { } public int getDrawableResource(BluetoothClass btClass) { - return R.drawable.ic_bt_network_pan; + return com.android.internal.R.drawable.ic_bt_network_pan; } // Tethering direction determines UI strings. diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java index 46723937a941..4a27715d8681 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java @@ -192,7 +192,7 @@ public final class PbapClientProfile implements LocalBluetoothProfile { } public int getDrawableResource(BluetoothClass btClass) { - return R.drawable.ic_bt_cellphone; + return com.android.internal.R.drawable.ic_bt_cellphone; } protected void finalize() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java index 1b3c453be4ed..165af2c751d4 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java @@ -135,7 +135,7 @@ public class PbapServerProfile implements LocalBluetoothProfile { } public int getDrawableResource(BluetoothClass btClass) { - return R.drawable.ic_bt_cellphone; + return com.android.internal.R.drawable.ic_bt_cellphone; } protected void finalize() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java index ea2ebde3ff4c..9e9c5b92fbfe 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java @@ -196,7 +196,7 @@ final class SapProfile implements LocalBluetoothProfile { } public int getDrawableResource(BluetoothClass btClass) { - return R.drawable.ic_bt_cellphone; + return com.android.internal.R.drawable.ic_bt_cellphone; } protected void finalize() { diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java index a5c6f0c28e78..6d0e3ac4b19c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java @@ -19,7 +19,6 @@ import android.bluetooth.BluetoothClass; import android.content.Context; import android.util.Log; -import com.android.settingslib.R; import com.android.settingslib.bluetooth.CachedBluetoothDevice; /** @@ -45,7 +44,7 @@ public class BluetoothMediaDevice extends MediaDevice { @Override public int getIcon() { //TODO(b/117129183): This is not final icon for bluetooth device, just for demo. - return R.drawable.ic_bt_headphones_a2dp; + return com.android.internal.R.drawable.ic_bt_headphones_a2dp; } @Override diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java index 04f70cc676d9..99d9d1c71e0e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java @@ -20,8 +20,6 @@ import android.widget.Toast; import androidx.mediarouter.media.MediaRouter; -import com.android.settingslib.R; - /** * InfoMediaDevice extends MediaDevice to represents wifi device. */ @@ -45,7 +43,7 @@ public class InfoMediaDevice extends MediaDevice { @Override public int getIcon() { //TODO(b/121083246): This is not final icon for cast device, just for demo. - return R.drawable.ic_settings_print; + return com.android.internal.R.drawable.ic_settings_print; } @Override diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java index c808214dbcf4..01003aae6590 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java @@ -18,7 +18,6 @@ package com.android.settingslib.media; import android.content.Context; import android.util.Log; -import com.android.settingslib.R; import com.android.settingslib.bluetooth.A2dpProfile; import com.android.settingslib.bluetooth.HearingAidProfile; import com.android.settingslib.bluetooth.LocalBluetoothManager; @@ -53,7 +52,7 @@ public class PhoneMediaDevice extends MediaDevice { @Override public int getIcon() { //TODO(b/117129183): This is not final icon for phone device, just for demo. - return R.drawable.ic_bt_cellphone; + return com.android.internal.R.drawable.ic_bt_cellphone; } @Override diff --git a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java index f02044dc52e0..a106846fac26 100644 --- a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java @@ -56,7 +56,7 @@ public class FooterPreference extends Preference { } private void init() { - setIcon(R.drawable.ic_info_outline_24dp); + setIcon(com.android.internal.R.drawable.ic_info_outline_24); setKey(KEY_FOOTER); setOrder(ORDER_FOOTER); setSelectable(false); diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java index a436cb515bb8..cf7bcb282584 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java @@ -31,8 +31,6 @@ import android.graphics.PorterDuff.Mode; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; -import com.android.settingslib.R; - import org.junit.Test; import org.junit.runner.RunWith; @@ -45,7 +43,7 @@ public class UserIconDrawableTest { public void getConstantState_shouldNotBeNull() { final Bitmap b = BitmapFactory.decodeResource( InstrumentationRegistry.getTargetContext().getResources(), - R.drawable.ic_mode_edit); + com.android.internal.R.drawable.ic_mode_edit); mDrawable = new UserIconDrawable(100 /* size */).setIcon(b).bake(); assertThat(mDrawable.getConstantState()).isNotNull(); } diff --git a/packages/SettingsLib/tests/robotests/res/drawable/ic_info_outline_24dp.xml b/packages/SettingsLib/tests/robotests/res/drawable/ic_info_outline_24dp.xml deleted file mode 100644 index 3fe1e9e5ca9f..000000000000 --- a/packages/SettingsLib/tests/robotests/res/drawable/ic_info_outline_24dp.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2016 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0" - android:tint="?android:attr/textColorSecondary"> - <path - android:fillColor="#000000" - android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/> -</vector> diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java index 0eb6de9584eb..7a71551bb367 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java @@ -20,7 +20,6 @@ import static com.google.common.truth.Truth.assertThat; import android.bluetooth.BluetoothDevice; import android.graphics.drawable.Drawable; -import com.android.settingslib.R; import com.android.settingslib.graph.BluetoothDeviceLayerDrawable; import org.junit.Test; @@ -34,7 +33,7 @@ public class BluetoothUtilsTest { @Test public void testGetBluetoothDrawable_noBatteryLevel_returnSimpleDrawable() { final Drawable drawable = BluetoothUtils.getBluetoothDrawable( - RuntimeEnvironment.application, R.drawable.ic_bt_laptop, + RuntimeEnvironment.application, com.android.internal.R.drawable.ic_bt_laptop, BluetoothDevice.BATTERY_LEVEL_UNKNOWN, 1 /* iconScale */); assertThat(drawable).isNotInstanceOf(BluetoothDeviceLayerDrawable.class); @@ -43,7 +42,7 @@ public class BluetoothUtilsTest { @Test public void testGetBluetoothDrawable_hasBatteryLevel_returnLayerDrawable() { final Drawable drawable = BluetoothUtils.getBluetoothDrawable( - RuntimeEnvironment.application, R.drawable.ic_bt_laptop, + RuntimeEnvironment.application, com.android.internal.R.drawable.ic_bt_laptop, 10 /* batteryLevel */, 1 /* iconScale */); assertThat(drawable).isInstanceOf(BluetoothDeviceLayerDrawable.class); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java index b77670bd01e5..491f32dd1185 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java @@ -12,8 +12,6 @@ import static com.google.common.truth.Truth.assertThat; import android.content.pm.ActivityInfo; import android.os.Bundle; -import com.android.settingslib.R; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -31,7 +29,7 @@ public class TileTest { mActivityInfo = new ActivityInfo(); mActivityInfo.packageName = RuntimeEnvironment.application.getPackageName(); mActivityInfo.name = "abc"; - mActivityInfo.icon = R.drawable.ic_plus; + mActivityInfo.icon = com.android.internal.R.drawable.ic_plus; mActivityInfo.metaData = new Bundle(); mTile = new Tile(mActivityInfo, "category"); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java index bbb4249317f7..b379b54c605b 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java @@ -53,8 +53,6 @@ import android.provider.Settings.Global; import android.util.ArrayMap; import android.util.Pair; -import com.android.settingslib.R; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -255,7 +253,7 @@ public class TileUtilsTest { resolveInfo.activityInfo.metaData = new Bundle(oldMetadata); resolveInfo.activityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, - R.drawable.ic_bt_cellphone); + com.android.internal.R.drawable.ic_bt_cellphone); outTiles.clear(); TileUtils.getTilesForAction(mContext, UserHandle.CURRENT, IA_SETTINGS_ACTION, addedCache, null /* defaultCategory */, outTiles, false /* usePriority */); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java index 1b350cc83285..c6c2a4418357 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java @@ -31,7 +31,7 @@ import org.robolectric.RuntimeEnvironment; @RunWith(RobolectricTestRunner.class) public class BluetoothDeviceLayerDrawableTest { - private static final int RES_ID = R.drawable.ic_bt_cellphone; + private static final int RES_ID = com.android.internal.R.drawable.ic_bt_cellphone; private static final int BATTERY_LEVEL = 15; private static final float BATTERY_ICON_SCALE = 0.75f; private static final int BATTERY_ICON_PADDING_TOP = 6; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java index 97de7ef2378a..e24b52594ba6 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java @@ -74,10 +74,10 @@ public class ActionButtonsPreferenceTest { @Test public void onBindViewHolder_setIcon_shouldShowButtonByDefault() { - mPref.setButton1Icon(R.drawable.ic_plus); - mPref.setButton2Icon(R.drawable.ic_plus); - mPref.setButton3Icon(R.drawable.ic_plus); - mPref.setButton4Icon(R.drawable.ic_plus); + mPref.setButton1Icon(com.android.internal.R.drawable.ic_plus); + mPref.setButton2Icon(com.android.internal.R.drawable.ic_plus); + mPref.setButton3Icon(com.android.internal.R.drawable.ic_plus); + mPref.setButton4Icon(com.android.internal.R.drawable.ic_plus); mPref.onBindViewHolder(mHolder); @@ -126,10 +126,10 @@ public class ActionButtonsPreferenceTest { @Test public void onBindViewHolder_setVisibleIsGoneAndSetIcon_shouldNotShowButton() { - mPref.setButton1Icon(R.drawable.ic_plus).setButton1Visible(false); - mPref.setButton2Icon(R.drawable.ic_plus).setButton2Visible(false); - mPref.setButton3Icon(R.drawable.ic_plus).setButton3Visible(false); - mPref.setButton4Icon(R.drawable.ic_plus).setButton4Visible(false); + mPref.setButton1Icon(com.android.internal.R.drawable.ic_plus).setButton1Visible(false); + mPref.setButton2Icon(com.android.internal.R.drawable.ic_plus).setButton2Visible(false); + mPref.setButton3Icon(com.android.internal.R.drawable.ic_plus).setButton3Visible(false); + mPref.setButton4Icon(com.android.internal.R.drawable.ic_plus).setButton4Visible(false); mPref.onBindViewHolder(mHolder); @@ -215,7 +215,7 @@ public class ActionButtonsPreferenceTest { @Test public void onBindViewHolder_setButtonIcon_iconMustDisplayAboveText() { mPref.setButton1Text(R.string.install_other_apps); - mPref.setButton1Icon(R.drawable.ic_plus); + mPref.setButton1Icon(com.android.internal.R.drawable.ic_plus); mPref.onBindViewHolder(mHolder); final Drawable[] drawablesAroundText = diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java index 4c68c1476cff..63a958eeb5bb 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java @@ -54,7 +54,7 @@ public class AppEntitiesHeaderControllerTest { mController = AppEntitiesHeaderController.newInstance(mContext, mAppEntitiesHeaderView); mAppEntityInfo = new AppEntityInfo.Builder() - .setIcon(mContext.getDrawable(R.drawable.ic_menu)) + .setIcon(mContext.getDrawable(com.android.internal.R.drawable.ic_menu)) .setTitle(TITLE) .setSummary(SUMMARY) .setOnClickListener(v -> { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java index cf6137d47bb0..c3ea336e0a4e 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java @@ -58,7 +58,7 @@ public class BarChartPreferenceTest { mHolder = PreferenceViewHolder.createInstanceForTests(mBarChartView); mPreference = new BarChartPreference(mContext, null /* attrs */); - mIcon = mContext.getDrawable(R.drawable.ic_menu); + mIcon = mContext.getDrawable(com.android.internal.R.drawable.ic_menu); mBarView1 = mBarChartView.findViewById(R.id.bar_view1); mBarView2 = mBarChartView.findViewById(R.id.bar_view2); mBarView3 = mBarChartView.findViewById(R.id.bar_view3); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index a1aefabfc7f2..856b167b9175 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -707,17 +707,17 @@ class SettingsProtoDumpUtil { Settings.Global.GPU_DEBUG_LAYERS_GLES, GlobalSettingsProto.Gpu.DEBUG_LAYERS_GLES); dumpSetting(s, p, - Settings.Global.GUP_DEV_ALL_APPS, - GlobalSettingsProto.Gpu.GUP_DEV_ALL_APPS); + Settings.Global.GAME_DRIVER_ALL_APPS, + GlobalSettingsProto.Gpu.GAME_DRIVER_ALL_APPS); dumpSetting(s, p, - Settings.Global.GUP_DEV_OPT_IN_APPS, - GlobalSettingsProto.Gpu.GUP_DEV_OPT_IN_APPS); + Settings.Global.GAME_DRIVER_OPT_IN_APPS, + GlobalSettingsProto.Gpu.GAME_DRIVER_OPT_IN_APPS); dumpSetting(s, p, - Settings.Global.GUP_DEV_OPT_OUT_APPS, - GlobalSettingsProto.Gpu.GUP_DEV_OPT_OUT_APPS); + Settings.Global.GAME_DRIVER_OPT_OUT_APPS, + GlobalSettingsProto.Gpu.GAME_DRIVER_OPT_OUT_APPS); dumpSetting(s, p, - Settings.Global.GUP_BLACKLIST, - GlobalSettingsProto.Gpu.GUP_BLACKLIST); + Settings.Global.GAME_DRIVER_BLACKLIST, + GlobalSettingsProto.Gpu.GAME_DRIVER_BLACKLIST); dumpSetting(s, p, Settings.Global.GAME_DRIVER_WHITELIST, GlobalSettingsProto.Gpu.GAME_DRIVER_WHITELIST); diff --git a/packages/SystemUI/res/drawable/smart_reply_button_background.xml b/packages/SystemUI/res/drawable/smart_reply_button_background.xml index 31119a90a788..5652b49818fc 100644 --- a/packages/SystemUI/res/drawable/smart_reply_button_background.xml +++ b/packages/SystemUI/res/drawable/smart_reply_button_background.xml @@ -25,7 +25,7 @@ android:insetRight="0dp" android:insetBottom="8dp"> <shape android:shape="rectangle"> - <corners android:radius="8dp" /> + <corners android:radius="@dimen/smart_reply_button_corner_radius" /> <stroke android:width="@dimen/smart_reply_button_stroke_width" android:color="@color/smart_reply_button_stroke" /> <solid android:color="@color/smart_reply_button_background"/> diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml index abf9e056ed22..67e31ac7f9da 100644 --- a/packages/SystemUI/res/layout/qs_footer_impl.xml +++ b/packages/SystemUI/res/layout/qs_footer_impl.xml @@ -109,7 +109,7 @@ android:contentDescription="@string/accessibility_quick_settings_edit" android:focusable="true" android:padding="15dp" - android:src="@drawable/ic_mode_edit" + android:src="@*android:drawable/ic_mode_edit" android:tint="?android:attr/colorForeground"/> <com.android.systemui.statusbar.AlphaOptimizedFrameLayout diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index a14259eca45e..d435c67364ba 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -888,8 +888,10 @@ <dimen name="smart_reply_button_stroke_width">1dp</dimen> <dimen name="smart_reply_button_font_size">14sp</dimen> <dimen name="smart_reply_button_line_spacing_extra">6sp</dimen> <!-- Total line height 20sp. --> - <dimen name="smart_action_button_icon_size">24dp</dimen> - <dimen name="smart_action_button_icon_padding">10dp</dimen> + <!-- Corner radius = half of min_height to create rounded sides. --> + <dimen name="smart_reply_button_corner_radius">24dp</dimen> + <dimen name="smart_action_button_icon_size">18dp</dimen> + <dimen name="smart_action_button_icon_padding">8dp</dimen> <!-- A reasonable upper bound for the height of the smart reply button. The measuring code needs to start with a guess for the maximum size. Currently two-line smart reply buttons diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 190bd7ae91a2..ffa5de8de2b7 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2305,8 +2305,8 @@ <item quantity="other"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item> </plurals> - <!-- Action on Ongoing Privacy Dialog to dismiss [CHAR LIMIT=10]--> - <string name="ongoing_privacy_dialog_cancel">Cancel</string> + <!-- Action for accepting the Ongoing privacy dialog [CHAR LIMIT=10]--> + <string name="ongoing_privacy_dialog_ok">Got it</string> <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=20]--> <string name="ongoing_privacy_dialog_open_settings">View details</string> @@ -2337,6 +2337,7 @@ <item quantity="one"><xliff:g id="num_apps" example="1">%d</xliff:g> other app</item> <item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> other apps</item> </plurals> + <!-- Text for the quick setting tile for sensor privacy [CHAR LIMIT=30] --> <string name="sensor_privacy_mode">Sensors off</string> diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index b7bee30dc640..da29ab4e63d2 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -428,7 +428,7 @@ public class BubbleController { entry.key).canBubble(); boolean hasOverlayIntent = n.getNotification().getBubbleMetadata() != null && n.getNotification().getBubbleMetadata().getIntent() != null; - return hasOverlayIntent && canChannelOverlay && canAppOverlay; + return DEBUG_ENABLE_AUTO_BUBBLE && hasOverlayIntent && canChannelOverlay && canAppOverlay; } /** @@ -438,7 +438,8 @@ public class BubbleController { * message-like notification. * </p> */ - private boolean shouldAutoBubble(Context context, NotificationEntry entry) { + @VisibleForTesting + protected boolean shouldAutoBubble(Context context, NotificationEntry entry) { if (entry.isBubbleDismissed()) { return false; } @@ -465,9 +466,11 @@ public class BubbleController { Class<? extends Notification.Style> style = n.getNotification().getNotificationStyle(); boolean isMessageType = Notification.CATEGORY_MESSAGE.equals(n.getNotification().category); boolean isMessageStyle = Notification.MessagingStyle.class.equals(style); - return (((isMessageType && hasRemoteInput) || isMessageStyle) && autoBubbleMessages) + boolean shouldAutoBubble = + (((isMessageType && hasRemoteInput) || isMessageStyle) && autoBubbleMessages) || (isImportantOngoing && autoBubbleOngoing) || autoBubbleAll; + return DEBUG_ENABLE_AUTO_BUBBLE && shouldAutoBubble; } private static boolean shouldAutoBubbleMessages(Context context) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index f14495bb959d..4527f73ab118 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -40,6 +40,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricSourceType; +import android.media.AudioAttributes; import android.media.AudioManager; import android.media.SoundPool; import android.os.Bundle; @@ -750,7 +751,14 @@ public class KeyguardViewMediator extends SystemUI { mDeviceInteractive = mPM.isInteractive(); - mLockSounds = new SoundPool(1, AudioManager.STREAM_SYSTEM, 0); + mLockSounds = new SoundPool.Builder() + .setMaxStreams(1) + .setAudioAttributes( + new AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .build()) + .build(); String soundPath = Settings.Global.getString(cr, Settings.Global.LOCK_SOUND); if (soundPath != null) { mLockSoundId = mLockSounds.load(soundPath, 1); diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt index 26c6d501f2cb..db5c244d95b2 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt @@ -20,6 +20,7 @@ import android.content.Context import android.content.DialogInterface import android.content.Intent import android.content.res.ColorStateList +import android.os.UserHandle import android.util.IconDrawableFactory import android.view.Gravity import android.view.LayoutInflater @@ -48,6 +49,7 @@ class OngoingPrivacyDialog constructor( R.dimen.ongoing_appops_dialog_icon_margin) private val MAX_ITEMS = context.resources.getInteger(R.integer.ongoing_appops_dialog_max_apps) private val iconFactory = IconDrawableFactory.newInstance(context, true) + private var dismissDialog: (() -> Unit)? = null init { val a = context.theme.obtainStyledAttributes( @@ -58,8 +60,8 @@ class OngoingPrivacyDialog constructor( fun createDialog(): Dialog { val builder = AlertDialog.Builder(context).apply { - setNegativeButton(R.string.ongoing_privacy_dialog_cancel, null) - setPositiveButton(R.string.ongoing_privacy_dialog_open_settings, + setPositiveButton(R.string.ongoing_privacy_dialog_ok, null) + setNeutralButton(R.string.ongoing_privacy_dialog_open_settings, object : DialogInterface.OnClickListener { val intent = Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE).putExtra( Intent.EXTRA_DURATION_MILLIS, TimeUnit.MINUTES.toMillis(1)) @@ -72,7 +74,9 @@ class OngoingPrivacyDialog constructor( }) } builder.setView(getContentView()) - return builder.create() + val dialog = builder.create() + dismissDialog = dialog::dismiss + return dialog } fun getContentView(): View { @@ -116,6 +120,7 @@ class OngoingPrivacyDialog constructor( return contentView } + @Suppress("DEPRECATION") private fun addAppItem( itemList: LinearLayout, app: PrivacyApplication, @@ -152,6 +157,16 @@ class OngoingPrivacyDialog constructor( } else { icons.visibility = View.GONE } + item.setOnClickListener(object : View.OnClickListener { + val intent = Intent(Intent.ACTION_REVIEW_APP_PERMISSION_USAGE) + .putExtra(Intent.EXTRA_PACKAGE_NAME, app.packageName) + .putExtra(Intent.EXTRA_USER, UserHandle.getUserHandleForUid(app.uid)) + override fun onClick(v: View?) { + Dependency.get(ActivityStarter::class.java) + .postStartActivityDismissingKeyguard(intent, 0) + dismissDialog?.invoke() + } + }) itemList.addView(item) } } diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt index 9252167d02bd..dbe87d1d53c6 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt @@ -37,7 +37,7 @@ data class PrivacyItem( val application: PrivacyApplication ) -data class PrivacyApplication(val packageName: String, val context: Context) +data class PrivacyApplication(val packageName: String, val uid: Int, val context: Context) : Comparable<PrivacyApplication> { override fun compareTo(other: PrivacyApplication): Int { diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt index 2339fae2d1ee..f1c3bf299eea 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt @@ -59,7 +59,8 @@ class PrivacyItemController @Inject constructor(val context: Context) { @Suppress("DEPRECATION") private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER) private var listening = false - val systemApp = PrivacyApplication(context.getString(R.string.device_services), context) + val systemApp = + PrivacyApplication(context.getString(R.string.device_services), SYSTEM_UID, context) private val callbacks = mutableListOf<WeakReference<Callback>>() private val notifyChanges = Runnable { @@ -162,7 +163,7 @@ class PrivacyItemController @Inject constructor(val context: Context) { else -> return null } if (appOpItem.uid == SYSTEM_UID) return PrivacyItem(type, systemApp) - val app = PrivacyApplication(appOpItem.packageName, context) + val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid, context) return PrivacyItem(type, app) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java index d740033c9f48..a0f4e24d2f93 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java @@ -24,7 +24,6 @@ import android.widget.Switch; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.R; -import com.android.systemui.R.drawable; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.qs.QSHost; @@ -38,7 +37,7 @@ import javax.inject.Inject; /** Quick settings tile: Location **/ public class LocationTile extends QSTileImpl<BooleanState> { - private final Icon mIcon = ResourceIcon.get(drawable.ic_signal_location); + private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_signal_location); private final LocationController mController; private final KeyguardMonitor mKeyguard; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java index 43ce0b50e03d..de78d3376500 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java @@ -111,7 +111,7 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> protected void handleUpdateState(BooleanState state, Object arg) { state.value = mController.isActivated(); state.label = mContext.getString(R.string.quick_settings_night_display_label); - state.icon = ResourceIcon.get(R.drawable.ic_qs_night_display_on); + state.icon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_night_display_on); state.expandedAccessibilityClassName = Switch.class.getName(); state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; state.secondaryLabel = getSecondaryLabel(state.value); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java index 35b7ef42177d..5e5241910e7f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java @@ -39,7 +39,6 @@ import com.android.systemui.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.ExpandableViewState; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -221,6 +220,11 @@ public class NotificationLogger implements StateListener { } @Override + public void onEntryReinflated(NotificationEntry entry) { + mExpansionStateLogger.onEntryReinflated(entry.key); + } + + @Override public void onInflationError( StatusBarNotification notification, Exception exception) { @@ -468,6 +472,13 @@ public class NotificationLogger implements StateListener { mLoggedExpansionState.remove(key); } + @VisibleForTesting + void onEntryReinflated(String key) { + // When the notification is updated, we should consider the notification as not + // yet logged. + mLoggedExpansionState.remove(key); + } + private State getState(String key) { State state = mExpansionStates.get(key); if (state == null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index c10837165c0f..711b08e45117 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -448,6 +448,11 @@ public class NotificationPanelView extends PanelView implements false); addView(mKeyguardStatusView, index); + // Re-associate the clock container with the keyguard clock switch. + mBigClockContainer.removeAllViews(); + KeyguardClockSwitch keyguardClockSwitch = findViewById(R.id.keyguard_clock_container); + keyguardClockSwitch.setBigClockContainer(mBigClockContainer); + // Update keyguard bottom area index = indexOfChild(mKeyguardBottomArea); removeView(mKeyguardBottomArea); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java index c4f027f2abea..07c6587c56e1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java @@ -479,13 +479,20 @@ public class SmartReplyView extends ViewGroup { remeasureButtonsIfNecessary(accumulatedMeasures.mButtonPaddingHorizontal, accumulatedMeasures.mMaxChildHeight); + int buttonHeight = Math.max(getSuggestedMinimumHeight(), mPaddingTop + + accumulatedMeasures.mMaxChildHeight + mPaddingBottom); + + // Set the corner radius to half the button height to make the side of the buttons look like + // a semicircle. + for (View smartSuggestionButton : smartSuggestions) { + setCornerRadius((Button) smartSuggestionButton, ((float) buttonHeight) / 2); + } + setMeasuredDimension( resolveSize(Math.max(getSuggestedMinimumWidth(), accumulatedMeasures.mMeasuredWidth), widthMeasureSpec), - resolveSize(Math.max(getSuggestedMinimumHeight(), mPaddingTop - + accumulatedMeasures.mMaxChildHeight + mPaddingBottom), - heightMeasureSpec)); + resolveSize(buttonHeight, heightMeasureSpec)); } /** @@ -806,6 +813,23 @@ public class SmartReplyView extends ViewGroup { button.setTextColor(textColor); } + private void setCornerRadius(Button button, float radius) { + Drawable drawable = button.getBackground(); + if (drawable instanceof RippleDrawable) { + // Mutate in case other notifications are using this drawable. + drawable = drawable.mutate(); + RippleDrawable ripple = (RippleDrawable) drawable; + Drawable inset = ripple.getDrawable(0); + if (inset instanceof InsetDrawable) { + Drawable background = ((InsetDrawable) inset).getDrawable(); + if (background instanceof GradientDrawable) { + GradientDrawable gradientDrawable = (GradientDrawable) background; + gradientDrawable.setCornerRadius(radius); + } + } + } + } + private ActivityStarter getActivityStarter() { if (mActivityStarter == null) { mActivityStarter = Dependency.get(ActivityStarter.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index e80275793b28..44ff4a73a4a7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -36,6 +36,7 @@ import com.android.systemui.statusbar.NotificationTestHelper; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.StatusBarWindowController; @@ -189,5 +190,10 @@ public class BubbleControllerTest extends SysuiTestCase { StatusBarWindowController statusBarWindowController) { super(context, statusBarWindowController); } + + @Override + public boolean shouldAutoBubble(Context c, NotificationEntry entry) { + return entry.notification.getNotification().getBubbleMetadata() != null; + } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt index d3b3dae2c1cd..f163b88357cf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt @@ -27,16 +27,20 @@ import org.junit.runner.RunWith @SmallTest class PrivacyDialogBuilderTest : SysuiTestCase() { + companion object { + val TEST_UID = 1 + } + @Test fun testGenerateAppsList() { val bar2 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication( - "Bar", context)) + "Bar", TEST_UID, context)) val bar3 = PrivacyItem(Privacy.TYPE_LOCATION, PrivacyApplication( - "Bar", context)) + "Bar", TEST_UID, context)) val foo0 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication( - "Foo", context)) + "Foo", TEST_UID, context)) val baz1 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication( - "Baz", context)) + "Baz", TEST_UID, context)) val items = listOf(bar2, foo0, baz1, bar3) @@ -55,11 +59,14 @@ class PrivacyDialogBuilderTest : SysuiTestCase() { @Test fun testOrder() { // We want location to always go last, so it will go in the "+ other apps" - val appCamera = PrivacyItem(PrivacyType.TYPE_CAMERA, PrivacyApplication("Camera", context)) + val appCamera = PrivacyItem(PrivacyType.TYPE_CAMERA, + PrivacyApplication("Camera", TEST_UID, context)) val appMicrophone = - PrivacyItem(PrivacyType.TYPE_MICROPHONE, PrivacyApplication("Microphone", context)) + PrivacyItem(PrivacyType.TYPE_MICROPHONE, + PrivacyApplication("Microphone", TEST_UID, context)) val appLocation = - PrivacyItem(PrivacyType.TYPE_LOCATION, PrivacyApplication("Location", context)) + PrivacyItem(PrivacyType.TYPE_LOCATION, + PrivacyApplication("Location", TEST_UID, context)) val items = listOf(appLocation, appMicrophone, appCamera) val textBuilder = PrivacyDialogBuilder(context, items) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java index 5ff9d1490f3a..f1d900332607 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java @@ -15,10 +15,10 @@ */ package com.android.systemui.statusbar.notification.logging; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.os.RemoteException; @@ -31,6 +31,7 @@ import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.UiOffloadThread; +import com.android.systemui.statusbar.notification.stack.ExpandableViewState; import org.junit.Before; import org.junit.Test; @@ -155,6 +156,27 @@ public class ExpansionStateLoggerTest extends SysuiTestCase { NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN.toMetricsEventEnum()); } + @Test + public void testOnEntryReinflated() throws RemoteException { + mLogger.onExpansionChanged(NOTIFICATION_KEY, true, true, + NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN); + mLogger.onVisibilityChanged( + Collections.singletonList(createNotificationVisibility(NOTIFICATION_KEY, true)), + Collections.emptyList()); + waitForUiOffloadThread(); + verify(mBarService).onNotificationExpansionChanged( + NOTIFICATION_KEY, true, true, ExpandableViewState.LOCATION_UNKNOWN); + + mLogger.onEntryReinflated(NOTIFICATION_KEY); + mLogger.onVisibilityChanged( + Collections.singletonList(createNotificationVisibility(NOTIFICATION_KEY, true)), + Collections.emptyList()); + waitForUiOffloadThread(); + // onNotificationExpansionChanged is called the second time. + verify(mBarService, times(2)).onNotificationExpansionChanged( + NOTIFICATION_KEY, true, true, ExpandableViewState.LOCATION_UNKNOWN); + } + private NotificationVisibility createNotificationVisibility(String key, boolean visibility) { return createNotificationVisibility(key, visibility, NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN); diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 73fcb0150a9e..385931d24bee 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -6766,10 +6766,10 @@ message MetricsEvent { // OS: Q ZEN_CUSTOM_SETTINGS_DIALOG = 1612; - // OPEN: Settings > Developer Options > Game Update Packages + // OPEN: Settings > Developer Options > Game Driver Preferences // CATEGORY: SETTINGS // OS: Q - SETTINGS_GUP_DASHBOARD = 1613; + SETTINGS_GAME_DRIVER_DASHBOARD = 1613; // CATEGORY: The category for all actions relating to language detection logging. // OS: Q @@ -6904,6 +6904,12 @@ message MetricsEvent { // OS: Q FIELD_TEXT_CLASSIFIER_WIDGET_VERSION = 1640; + // Tagged data for NOTIFICATION_ITEM. One of the CATEGORY String constants from + // https://developer.android.com/reference/android/app/Notification . + // OS: Q + // CATEGORY: NOTIFICATION + FIELD_NOTIFICATION_CATEGORY = 1641; + // ---- End Q Constants, all Q constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 2c075dc809d0..f4ac13074903 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -275,6 +275,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ & AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) == 0) { return false; } + if (!mSecurityPolicy.checkAccessibilityAccess(this)) { + return false; + } try { mServiceInterface.onKeyEvent(keyEvent, sequenceNumber); } catch (RemoteException e) { @@ -388,6 +391,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ if (mSecurityPolicy.mWindows == null) { return null; } + if (!mSecurityPolicy.checkAccessibilityAccess(this)) { + return null; + } List<AccessibilityWindowInfo> windows = new ArrayList<>(); final int windowCount = mSecurityPolicy.mWindows.size(); for (int i = 0; i < windowCount; i++) { @@ -413,6 +419,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ if (!permissionGranted) { return null; } + if (!mSecurityPolicy.checkAccessibilityAccess(this)) { + return null; + } AccessibilityWindowInfo window = mSecurityPolicy.findA11yWindowInfoById(windowId); if (window != null) { AccessibilityWindowInfo windowClone = AccessibilityWindowInfo.obtain(window); @@ -455,6 +464,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId); } + if (!mSecurityPolicy.checkAccessibilityAccess(this)) { + return null; + } final int interrogatingPid = Binder.getCallingPid(); callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); @@ -511,6 +523,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId); } + if (!mSecurityPolicy.checkAccessibilityAccess(this)) { + return null; + } final int interrogatingPid = Binder.getCallingPid(); callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); @@ -567,6 +582,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId); } + if (!mSecurityPolicy.checkAccessibilityAccess(this)) { + return null; + } final int interrogatingPid = Binder.getCallingPid(); callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); @@ -623,6 +641,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId); } + if (!mSecurityPolicy.checkAccessibilityAccess(this)) { + return null; + } final int interrogatingPid = Binder.getCallingPid(); callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); @@ -678,6 +699,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId); } + if (!mSecurityPolicy.checkAccessibilityAccess(this)) { + return null; + } final int interrogatingPid = Binder.getCallingPid(); callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); @@ -722,6 +746,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ return false; } } + if (!mSecurityPolicy.checkAccessibilityAccess(this)) { + return false; + } boolean returnValue = mSystemSupport.performAccessibilityAction(resolvedWindowId, accessibilityNodeId, action, arguments, interactionId, callback, mFetchFlags, interrogatingTid); @@ -974,6 +1001,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ return; } + if (!mSecurityPolicy.checkAccessibilityAccess(this)) { + return; + } // Make a copy since during dispatch it is possible the event to // be modified to remove its source if the receiving service does // not have permission to access the window content. diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 305c53e1e26a..f88d5217ab2f 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -3787,6 +3787,31 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return findWindowIdLocked(token); } } + + public boolean checkAccessibilityAccess(AbstractAccessibilityServiceConnection service) { + final String packageName = service.getComponentName().getPackageName(); + final ResolveInfo resolveInfo = service.getServiceInfo().getResolveInfo(); + + if (resolveInfo == null) { + // For InteractionBridge and UiAutomation + return true; + } + + final int uid = resolveInfo.serviceInfo.applicationInfo.uid; + final long identityToken = Binder.clearCallingIdentity(); + try { + // For the caller is system, just block the data to a11y services. + if (OWN_PROCESS_ID == Binder.getCallingPid()) { + return mAppOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY, + uid, packageName) == AppOpsManager.MODE_ALLOWED; + } + + return mAppOpsManager.noteOp(AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY, + uid, packageName) == AppOpsManager.MODE_ALLOWED; + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } } /** diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 844096d9d717..6108aaa860d5 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -177,12 +177,13 @@ public final class ContentCaptureManagerService extends final class ContentCaptureManagerServiceStub extends IContentCaptureManager.Stub { @Override - public void startSession(@UserIdInt int userId, @NonNull IBinder activityToken, + public void startSession(@NonNull IBinder activityToken, @NonNull ComponentName componentName, @NonNull String sessionId, int flags, @NonNull IResultReceiver result) { Preconditions.checkNotNull(activityToken); Preconditions.checkNotNull(componentName); Preconditions.checkNotNull(sessionId); + final int userId = UserHandle.getCallingUserId(); // TODO(b/111276913): refactor getTaskIdForActivity() to also return ComponentName, // so we don't pass it on startSession (same for Autofill) @@ -199,8 +200,9 @@ public final class ContentCaptureManagerService extends } @Override - public void finishSession(@UserIdInt int userId, @NonNull String sessionId) { + public void finishSession(@NonNull String sessionId) { Preconditions.checkNotNull(sessionId); + final int userId = UserHandle.getCallingUserId(); synchronized (mLock) { final ContentCapturePerUserService service = getServiceForUserLocked(userId); @@ -209,15 +211,16 @@ public final class ContentCaptureManagerService extends } @Override - public void getReceiverServiceComponentName(@UserIdInt int userId, - IResultReceiver receiver) { + public void getServiceComponentName(@NonNull IResultReceiver result) { + final int userId = UserHandle.getCallingUserId(); ComponentName connectedServiceComponentName; synchronized (mLock) { final ContentCapturePerUserService service = getServiceForUserLocked(userId); connectedServiceComponentName = service.getServiceComponentName(); } try { - receiver.send(0, SyncResultReceiver.bundleFor(connectedServiceComponentName)); + result.send(/* resultCode= */ 0, + SyncResultReceiver.bundleFor(connectedServiceComponentName)); } catch (RemoteException e) { // Ignore exception as we need to be resilient against app behavior. Slog.w(TAG, "Unable to send service component name: " + e); @@ -225,8 +228,9 @@ public final class ContentCaptureManagerService extends } @Override - public void removeUserData(@UserIdInt int userId, @NonNull UserDataRemovalRequest request) { + public void removeUserData(@NonNull UserDataRemovalRequest request) { Preconditions.checkNotNull(request); + final int userId = UserHandle.getCallingUserId(); synchronized (mLock) { final ContentCapturePerUserService service = getServiceForUserLocked(userId); service.removeUserDataLocked(request); diff --git a/services/core/java/com/android/server/AlarmManagerInternal.java b/services/core/java/com/android/server/AlarmManagerInternal.java index dbff957a0da7..275661084aa3 100644 --- a/services/core/java/com/android/server/AlarmManagerInternal.java +++ b/services/core/java/com/android/server/AlarmManagerInternal.java @@ -17,5 +17,15 @@ package com.android.server; public interface AlarmManagerInternal { - void removeAlarmsForUid(int uid); + // Some other components in the system server need to know about + // broadcast alarms currently in flight + public interface InFlightListener { + /** There is now an alarm pending delivery to the given app */ + void broadcastAlarmPending(int recipientUid); + /** A broadcast alarm targeted to the given app has completed delivery */ + void broadcastAlarmComplete(int recipientUid); + } + + public void removeAlarmsForUid(int uid); + public void registerInFlightListener(InFlightListener callback); } diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index e3dcb7d331cf..10b532700d1b 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -197,6 +197,8 @@ class AlarmManagerService extends SystemService { PowerManager.WakeLock mWakeLock; ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<>(); ArrayList<InFlight> mInFlight = new ArrayList<>(); + private final ArrayList<AlarmManagerInternal.InFlightListener> mInFlightListeners = + new ArrayList<>(); AlarmHandler mHandler; AppWakeupHistory mAppWakeupHistory; ClockReceiver mClockReceiver; @@ -1315,6 +1317,10 @@ class AlarmManagerService extends SystemService { mAlarmType = alarm.type; } + boolean isBroadcast() { + return mPendingIntent != null && mPendingIntent.isBroadcast(); + } + @Override public String toString() { return "InFlight{" @@ -1354,6 +1360,20 @@ class AlarmManagerService extends SystemService { } } + private void notifyBroadcastAlarmPendingLocked(int uid) { + final int numListeners = mInFlightListeners.size(); + for (int i = 0; i < numListeners; i++) { + mInFlightListeners.get(i).broadcastAlarmPending(uid); + } + } + + private void notifyBroadcastAlarmCompleteLocked(int uid) { + final int numListeners = mInFlightListeners.size(); + for (int i = 0; i < numListeners; i++) { + mInFlightListeners.get(i).broadcastAlarmComplete(uid); + } + } + static final class FilterStats { final BroadcastStats mBroadcastStats; final String mTag; @@ -1976,6 +1996,13 @@ class AlarmManagerService extends SystemService { removeLocked(uid); } } + + @Override + public void registerInFlightListener(InFlightListener callback) { + synchronized (mLock) { + mInFlightListeners.add(callback); + } + } } /** @@ -4426,7 +4453,11 @@ class AlarmManagerService extends SystemService { private InFlight removeLocked(PendingIntent pi, Intent intent) { for (int i = 0; i < mInFlight.size(); i++) { - if (mInFlight.get(i).mPendingIntent == pi) { + final InFlight inflight = mInFlight.get(i); + if (inflight.mPendingIntent == pi) { + if (pi.isBroadcast()) { + notifyBroadcastAlarmCompleteLocked(inflight.mUid); + } return mInFlight.remove(i); } } @@ -4649,6 +4680,9 @@ class AlarmManagerService extends SystemService { final InFlight inflight = new InFlight(AlarmManagerService.this, alarm, nowELAPSED); mInFlight.add(inflight); mBroadcastRefCount++; + if (inflight.isBroadcast()) { + notifyBroadcastAlarmPendingLocked(alarm.uid); + } if (allowWhileIdle) { // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm. mLastAllowWhileIdleDispatch.put(alarm.creatorUid, nowELAPSED); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 14e235489b97..1519c1785070 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -1884,6 +1884,12 @@ public class ConnectivityService extends IConnectivityManager.Stub "ConnectivityService"); } + private void enforceControlAlwaysOnVpnPermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CONTROL_ALWAYS_ON_VPN, + "ConnectivityService"); + } + private void enforceNetworkStackSettingsOrSetup() { enforceAnyPermissionOf( android.Manifest.permission.NETWORK_SETTINGS, @@ -1891,6 +1897,12 @@ public class ConnectivityService extends IConnectivityManager.Stub android.Manifest.permission.NETWORK_STACK); } + private void enforceNetworkStackPermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.NETWORK_STACK, + "ConnectivityService"); + } + private boolean checkNetworkStackPermission() { return PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission( android.Manifest.permission.NETWORK_STACK); @@ -4147,8 +4159,9 @@ public class ConnectivityService extends IConnectivityManager.Stub } @Override - public boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown) { - enforceConnectivityInternalPermission(); + public boolean setAlwaysOnVpnPackage( + int userId, String packageName, boolean lockdown, List<String> lockdownWhitelist) { + enforceControlAlwaysOnVpnPermission(); enforceCrossUserPermission(userId); synchronized (mVpns) { @@ -4162,11 +4175,11 @@ public class ConnectivityService extends IConnectivityManager.Stub Slog.w(TAG, "User " + userId + " has no Vpn configuration"); return false; } - if (!vpn.setAlwaysOnPackage(packageName, lockdown)) { + if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownWhitelist)) { return false; } if (!startAlwaysOnVpn(userId)) { - vpn.setAlwaysOnPackage(null, false); + vpn.setAlwaysOnPackage(null, false, null); return false; } } @@ -4175,7 +4188,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public String getAlwaysOnVpnPackage(int userId) { - enforceConnectivityInternalPermission(); + enforceControlAlwaysOnVpnPermission(); enforceCrossUserPermission(userId); synchronized (mVpns) { @@ -4189,6 +4202,36 @@ public class ConnectivityService extends IConnectivityManager.Stub } @Override + public boolean isVpnLockdownEnabled(int userId) { + enforceControlAlwaysOnVpnPermission(); + enforceCrossUserPermission(userId); + + synchronized (mVpns) { + Vpn vpn = mVpns.get(userId); + if (vpn == null) { + Slog.w(TAG, "User " + userId + " has no Vpn configuration"); + return false; + } + return vpn.getLockdown(); + } + } + + @Override + public List<String> getVpnLockdownWhitelist(int userId) { + enforceControlAlwaysOnVpnPermission(); + enforceCrossUserPermission(userId); + + synchronized (mVpns) { + Vpn vpn = mVpns.get(userId); + if (vpn == null) { + Slog.w(TAG, "User " + userId + " has no Vpn configuration"); + return null; + } + return vpn.getLockdownWhitelist(); + } + } + + @Override public int checkMobileProvisioning(int suggestedTimeOutMs) { // TODO: Remove? Any reason to trigger a provisioning check? return -1; @@ -4417,7 +4460,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) { Slog.d(TAG, "Removing always-on VPN package " + packageName + " for user " + userId); - vpn.setAlwaysOnPackage(null, false); + vpn.setAlwaysOnPackage(null, false, null); } } } @@ -6297,7 +6340,7 @@ public class ConnectivityService extends IConnectivityManager.Stub synchronized (mVpns) { final String alwaysOnPackage = getAlwaysOnVpnPackage(userId); if (alwaysOnPackage != null) { - setAlwaysOnVpnPackage(userId, null, false); + setAlwaysOnVpnPackage(userId, null, false, null); setVpnPackageAuthorization(alwaysOnPackage, userId, false); } diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java index 4a8706e7090f..2f7929c962eb 100644 --- a/services/core/java/com/android/server/LooperStatsService.java +++ b/services/core/java/com/android/server/LooperStatsService.java @@ -55,11 +55,11 @@ public class LooperStatsService extends Binder { "debug.sys.looper_stats_enabled"; private static final int DEFAULT_SAMPLING_INTERVAL = 100; private static final int DEFAULT_ENTRIES_SIZE_CAP = 2000; - private static final boolean DEFAULT_ENABLED = false; + private static final boolean DEFAULT_ENABLED = true; private final Context mContext; private final LooperStats mStats; - private boolean mEnabled = false; + private boolean mEnabled = DEFAULT_ENABLED; private LooperStatsService(Context context, LooperStats stats) { this.mContext = context; diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index c064453360c5..b3997ef2b54f 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -47,8 +47,10 @@ import android.app.ActivityManager; import android.content.Context; import android.net.ConnectivityManager; import android.net.INetd; +import android.net.INetdUnsolicitedEventListener; import android.net.INetworkManagementEventObserver; import android.net.ITetheringStatsProvider; +import android.net.InetAddresses; import android.net.InterfaceConfiguration; import android.net.InterfaceConfigurationParcel; import android.net.IpPrefix; @@ -61,7 +63,6 @@ import android.net.RouteInfo; import android.net.TetherStatsParcel; import android.net.UidRange; import android.net.shared.NetdService; -import android.net.shared.NetworkObserverRegistry; import android.os.BatteryStats; import android.os.Binder; import android.os.Handler; @@ -206,13 +207,16 @@ public class NetworkManagementService extends INetworkManagementService.Stub private INetd mNetdService; - private NMSNetworkObserverRegistry mNetworkObserverRegistry; + private final NetdUnsolicitedEventListener mNetdUnsolicitedEventListener; private IBatteryStats mBatteryStats; private final Thread mThread; private CountDownLatch mConnectedSignal = new CountDownLatch(1); + private final RemoteCallbackList<INetworkManagementEventObserver> mObservers = + new RemoteCallbackList<>(); + private final NetworkStatsFactory mStatsFactory = new NetworkStatsFactory(); @GuardedBy("mTetheringStatsProviders") @@ -322,6 +326,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub mDaemonHandler = new Handler(FgThread.get().getLooper()); + mNetdUnsolicitedEventListener = new NetdUnsolicitedEventListener(); + // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); @@ -340,7 +346,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub mFgHandler = null; mThread = null; mServices = null; - mNetworkObserverRegistry = null; + mNetdUnsolicitedEventListener = null; } static NetworkManagementService create(Context context, String socket, SystemServices services) @@ -388,12 +394,14 @@ public class NetworkManagementService extends INetworkManagementService.Stub @Override public void registerObserver(INetworkManagementEventObserver observer) { - mNetworkObserverRegistry.registerObserver(observer); + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + mObservers.register(observer); } @Override public void unregisterObserver(INetworkManagementEventObserver observer) { - mNetworkObserverRegistry.unregisterObserver(observer); + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + mObservers.unregister(observer); } @FunctionalInterface @@ -401,101 +409,127 @@ public class NetworkManagementService extends INetworkManagementService.Stub public void sendCallback(INetworkManagementEventObserver o) throws RemoteException; } - private class NMSNetworkObserverRegistry extends NetworkObserverRegistry { - NMSNetworkObserverRegistry(Context context, Handler handler, INetd netd) - throws RemoteException { - super(context, handler, netd); + private void invokeForAllObservers(NetworkManagementEventCallback eventCallback) { + final int length = mObservers.beginBroadcast(); + try { + for (int i = 0; i < length; i++) { + try { + eventCallback.sendCallback(mObservers.getBroadcastItem(i)); + } catch (RemoteException | RuntimeException e) { + } + } + } finally { + mObservers.finishBroadcast(); } + } - /** - * Notify our observers of a change in the data activity state of the interface - */ - @Override - public void notifyInterfaceClassActivity(int type, boolean isActive, long tsNanos, - int uid, boolean fromRadio) { - final boolean isMobile = ConnectivityManager.isNetworkTypeMobile(type); - int powerState = isActive - ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH - : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; - - if (isMobile) { - if (!fromRadio) { - if (mMobileActivityFromRadio) { - // If this call is not coming from a report from the radio itself, but we - // have previously received reports from the radio, then we will take the - // power state to just be whatever the radio last reported. - powerState = mLastPowerStateFromRadio; - } - } else { - mMobileActivityFromRadio = true; + /** + * Notify our observers of an interface status change + */ + private void notifyInterfaceStatusChanged(String iface, boolean up) { + invokeForAllObservers(o -> o.interfaceStatusChanged(iface, up)); + } + + /** + * Notify our observers of an interface link state change + * (typically, an Ethernet cable has been plugged-in or unplugged). + */ + private void notifyInterfaceLinkStateChanged(String iface, boolean up) { + invokeForAllObservers(o -> o.interfaceLinkStateChanged(iface, up)); + } + + /** + * Notify our observers of an interface addition. + */ + private void notifyInterfaceAdded(String iface) { + invokeForAllObservers(o -> o.interfaceAdded(iface)); + } + + /** + * Notify our observers of an interface removal. + */ + private void notifyInterfaceRemoved(String iface) { + // netd already clears out quota and alerts for removed ifaces; update + // our sanity-checking state. + mActiveAlerts.remove(iface); + mActiveQuotas.remove(iface); + invokeForAllObservers(o -> o.interfaceRemoved(iface)); + } + + /** + * Notify our observers of a limit reached. + */ + private void notifyLimitReached(String limitName, String iface) { + invokeForAllObservers(o -> o.limitReached(limitName, iface)); + } + + /** + * Notify our observers of a change in the data activity state of the interface + */ + private void notifyInterfaceClassActivity(int type, boolean isActive, long tsNanos, + int uid, boolean fromRadio) { + final boolean isMobile = ConnectivityManager.isNetworkTypeMobile(type); + int powerState = isActive + ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH + : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; + if (isMobile) { + if (!fromRadio) { + if (mMobileActivityFromRadio) { + // If this call is not coming from a report from the radio itself, but we + // have previously received reports from the radio, then we will take the + // power state to just be whatever the radio last reported. + powerState = mLastPowerStateFromRadio; } - if (mLastPowerStateFromRadio != powerState) { - mLastPowerStateFromRadio = powerState; - try { - getBatteryStats().noteMobileRadioPowerState(powerState, tsNanos, uid); - } catch (RemoteException e) { - } + } else { + mMobileActivityFromRadio = true; + } + if (mLastPowerStateFromRadio != powerState) { + mLastPowerStateFromRadio = powerState; + try { + getBatteryStats().noteMobileRadioPowerState(powerState, tsNanos, uid); + } catch (RemoteException e) { } StatsLog.write_non_chained(StatsLog.MOBILE_RADIO_POWER_STATE_CHANGED, uid, null, powerState); } + } - if (ConnectivityManager.isNetworkTypeWifi(type)) { - if (mLastPowerStateFromWifi != powerState) { - mLastPowerStateFromWifi = powerState; - try { - getBatteryStats().noteWifiRadioPowerState(powerState, tsNanos, uid); - } catch (RemoteException e) { - } + if (ConnectivityManager.isNetworkTypeWifi(type)) { + if (mLastPowerStateFromWifi != powerState) { + mLastPowerStateFromWifi = powerState; + try { + getBatteryStats().noteWifiRadioPowerState(powerState, tsNanos, uid); + } catch (RemoteException e) { } StatsLog.write_non_chained(StatsLog.WIFI_RADIO_POWER_STATE_CHANGED, uid, null, powerState); } + } - if (!isMobile || fromRadio || !mMobileActivityFromRadio) { - // Report the change in data activity. We don't do this if this is a change - // on the mobile network, that is not coming from the radio itself, and we - // have previously seen change reports from the radio. In that case only - // the radio is the authority for the current state. - final boolean active = isActive; - super.notifyInterfaceClassActivity(type, isActive, tsNanos, uid, fromRadio); - } + if (!isMobile || fromRadio || !mMobileActivityFromRadio) { + // Report the change in data activity. We don't do this if this is a change + // on the mobile network, that is not coming from the radio itself, and we + // have previously seen change reports from the radio. In that case only + // the radio is the authority for the current state. + final boolean active = isActive; + invokeForAllObservers(o -> o.interfaceClassDataActivityChanged( + Integer.toString(type), active, tsNanos)); + } - boolean report = false; - synchronized (mIdleTimerLock) { - if (mActiveIdleTimers.isEmpty()) { - // If there are no idle timers, we are not monitoring activity, so we - // are always considered active. - isActive = true; - } - if (mNetworkActive != isActive) { - mNetworkActive = isActive; - report = isActive; - } + boolean report = false; + synchronized (mIdleTimerLock) { + if (mActiveIdleTimers.isEmpty()) { + // If there are no idle timers, we are not monitoring activity, so we + // are always considered active. + isActive = true; } - if (report) { - reportNetworkActive(); + if (mNetworkActive != isActive) { + mNetworkActive = isActive; + report = isActive; } } - - /** - * Notify our observers of an interface removal. - */ - @Override - public void notifyInterfaceRemoved(String iface) { - // netd already clears out quota and alerts for removed ifaces; update - // our sanity-checking state. - mActiveAlerts.remove(iface); - mActiveQuotas.remove(iface); - super.notifyInterfaceRemoved(iface); - } - - @Override - public void onStrictCleartextDetected(int uid, String hex) throws RemoteException { - // Don't need to post to mDaemonHandler because the only thing - // that notifyCleartextNetwork does is post to a handler - ActivityManager.getService().notifyCleartextNetwork(uid, - HexDump.hexStringToByteArray(hex)); + if (report) { + reportNetworkActive(); } } @@ -524,8 +558,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub return; } // No current code examines the interface parameter in a global alert. Just pass null. - mDaemonHandler.post(() -> mNetworkObserverRegistry.notifyLimitReached( - LIMIT_GLOBAL_ALERT, null)); + mDaemonHandler.post(() -> notifyLimitReached(LIMIT_GLOBAL_ALERT, null)); } } @@ -557,11 +590,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub private void connectNativeNetdService() { mNetdService = mServices.getNetd(); try { - mNetworkObserverRegistry = new NMSNetworkObserverRegistry( - mContext, mDaemonHandler, mNetdService); - if (DBG) Slog.d(TAG, "Registered NetworkObserverRegistry"); + mNetdService.registerUnsolicitedEventListener(mNetdUnsolicitedEventListener); + if (DBG) Slog.d(TAG, "Register unsolicited event listener"); } catch (RemoteException | ServiceSpecificException e) { - Slog.wtf(TAG, "Failed to register NetworkObserverRegistry: " + e); + Slog.e(TAG, "Failed to set Netd unsolicited event listener " + e); } } @@ -665,6 +697,118 @@ public class NetworkManagementService extends INetworkManagementService.Stub } + /** + * Notify our observers of a new or updated interface address. + */ + private void notifyAddressUpdated(String iface, LinkAddress address) { + invokeForAllObservers(o -> o.addressUpdated(iface, address)); + } + + /** + * Notify our observers of a deleted interface address. + */ + private void notifyAddressRemoved(String iface, LinkAddress address) { + invokeForAllObservers(o -> o.addressRemoved(iface, address)); + } + + /** + * Notify our observers of DNS server information received. + */ + private void notifyInterfaceDnsServerInfo(String iface, long lifetime, String[] addresses) { + invokeForAllObservers(o -> o.interfaceDnsServerInfo(iface, lifetime, addresses)); + } + + /** + * Notify our observers of a route change. + */ + private void notifyRouteChange(boolean updated, RouteInfo route) { + if (updated) { + invokeForAllObservers(o -> o.routeUpdated(route)); + } else { + invokeForAllObservers(o -> o.routeRemoved(route)); + } + } + + private class NetdUnsolicitedEventListener extends INetdUnsolicitedEventListener.Stub { + @Override + public void onInterfaceClassActivityChanged(boolean isActive, + int label, long timestamp, int uid) throws RemoteException { + final long timestampNanos; + if (timestamp <= 0) { + timestampNanos = SystemClock.elapsedRealtimeNanos(); + } else { + timestampNanos = timestamp; + } + mDaemonHandler.post(() -> + notifyInterfaceClassActivity(label, isActive, timestampNanos, uid, false)); + } + + @Override + public void onQuotaLimitReached(String alertName, String ifName) + throws RemoteException { + mDaemonHandler.post(() -> notifyLimitReached(alertName, ifName)); + } + + @Override + public void onInterfaceDnsServerInfo(String ifName, + long lifetime, String[] servers) throws RemoteException { + mDaemonHandler.post(() -> notifyInterfaceDnsServerInfo(ifName, lifetime, servers)); + } + + @Override + public void onInterfaceAddressUpdated(String addr, + String ifName, int flags, int scope) throws RemoteException { + final LinkAddress address = new LinkAddress(addr, flags, scope); + mDaemonHandler.post(() -> notifyAddressUpdated(ifName, address)); + } + + @Override + public void onInterfaceAddressRemoved(String addr, + String ifName, int flags, int scope) throws RemoteException { + final LinkAddress address = new LinkAddress(addr, flags, scope); + mDaemonHandler.post(() -> notifyAddressRemoved(ifName, address)); + } + + @Override + public void onInterfaceAdded(String ifName) throws RemoteException { + mDaemonHandler.post(() -> notifyInterfaceAdded(ifName)); + } + + @Override + public void onInterfaceRemoved(String ifName) throws RemoteException { + mDaemonHandler.post(() -> notifyInterfaceRemoved(ifName)); + } + + @Override + public void onInterfaceChanged(String ifName, boolean up) + throws RemoteException { + mDaemonHandler.post(() -> notifyInterfaceStatusChanged(ifName, up)); + } + + @Override + public void onInterfaceLinkStateChanged(String ifName, boolean up) + throws RemoteException { + mDaemonHandler.post(() -> notifyInterfaceLinkStateChanged(ifName, up)); + } + + @Override + public void onRouteChanged(boolean updated, + String route, String gateway, String ifName) throws RemoteException { + final RouteInfo processRoute = new RouteInfo(new IpPrefix(route), + ("".equals(gateway)) ? null : InetAddresses.parseNumericAddress(gateway), + ifName); + mDaemonHandler.post(() -> notifyRouteChange(updated, processRoute)); + } + + @Override + public void onStrictCleartextDetected(int uid, String hex) throws RemoteException { + // Don't need to post to mDaemonHandler because the only thing + // that notifyCleartextNetwork does is post to a handler + ActivityManager.getService().notifyCleartextNetwork(uid, + HexDump.hexStringToByteArray(hex)); + } + } + // // Netd Callback handling // @@ -713,18 +857,16 @@ public class NetworkManagementService extends INetworkManagementService.Stub throw new IllegalStateException(errorMessage); } if (cooked[2].equals("added")) { - mNetworkObserverRegistry.notifyInterfaceAdded(cooked[3]); + notifyInterfaceAdded(cooked[3]); return true; } else if (cooked[2].equals("removed")) { - mNetworkObserverRegistry.notifyInterfaceRemoved(cooked[3]); + notifyInterfaceRemoved(cooked[3]); return true; } else if (cooked[2].equals("changed") && cooked.length == 5) { - mNetworkObserverRegistry.notifyInterfaceStatusChanged( - cooked[3], cooked[4].equals("up")); + notifyInterfaceStatusChanged(cooked[3], cooked[4].equals("up")); return true; } else if (cooked[2].equals("linkstate") && cooked.length == 5) { - mNetworkObserverRegistry.notifyInterfaceLinkStateChanged( - cooked[3], cooked[4].equals("up")); + notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals("up")); return true; } throw new IllegalStateException(errorMessage); @@ -738,7 +880,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub throw new IllegalStateException(errorMessage); } if (cooked[2].equals("alert")) { - mNetworkObserverRegistry.notifyLimitReached(cooked[3], cooked[4]); + notifyLimitReached(cooked[3], cooked[4]); return true; } throw new IllegalStateException(errorMessage); @@ -764,9 +906,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub timestampNanos = SystemClock.elapsedRealtimeNanos(); } boolean isActive = cooked[2].equals("active"); - mNetworkObserverRegistry.notifyInterfaceClassActivity( - Integer.parseInt(cooked[3]), isActive, - timestampNanos, processUid, false); + notifyInterfaceClassActivity(Integer.parseInt(cooked[3]), + isActive, timestampNanos, processUid, false); return true; // break; case NetdResponseCode.InterfaceAddressChange: @@ -792,9 +933,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub } if (cooked[2].equals("updated")) { - mNetworkObserverRegistry.notifyAddressUpdated(iface, address); + notifyAddressUpdated(iface, address); } else { - mNetworkObserverRegistry.notifyAddressRemoved(iface, address); + notifyAddressRemoved(iface, address); } return true; // break; @@ -814,8 +955,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub throw new IllegalStateException(errorMessage); } String[] servers = cooked[5].split(","); - mNetworkObserverRegistry.notifyInterfaceDnsServerInfo( - cooked[3], lifetime, servers); + notifyInterfaceDnsServerInfo(cooked[3], lifetime, servers); } return true; // break; @@ -854,8 +994,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub InetAddress gateway = null; if (via != null) gateway = InetAddress.parseNumericAddress(via); RouteInfo route = new RouteInfo(new IpPrefix(cooked[3]), gateway, dev); - mNetworkObserverRegistry.notifyRouteChange( - cooked[2].equals("updated"), route); + notifyRouteChange(cooked[2].equals("updated"), route); return true; } catch (IllegalArgumentException e) {} } @@ -1318,8 +1457,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub if (ConnectivityManager.isNetworkTypeMobile(type)) { mNetworkActive = false; } - mDaemonHandler.post(() -> mNetworkObserverRegistry.notifyInterfaceClassActivity( - type, true /* isActive */, SystemClock.elapsedRealtimeNanos(), -1, false)); + mDaemonHandler.post(() -> notifyInterfaceClassActivity(type, true, + SystemClock.elapsedRealtimeNanos(), -1, false)); } } @@ -1342,9 +1481,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub throw new IllegalStateException(e); } mActiveIdleTimers.remove(iface); - mDaemonHandler.post(() -> mNetworkObserverRegistry.notifyInterfaceClassActivity( - params.type, false /* isActive */, SystemClock.elapsedRealtimeNanos(), -1, - false)); + mDaemonHandler.post(() -> notifyInterfaceClassActivity(params.type, false, + SystemClock.elapsedRealtimeNanos(), -1, false)); } } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index cec825fb9c00..cecd55a325d3 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -802,10 +802,7 @@ class StorageManagerService extends IStorageManager.Stub // For now, simply clone property when it changes DeviceConfig.addOnPropertyChangedListener(DeviceConfig.Storage.NAMESPACE, mContext.getMainExecutor(), (namespace, name, value) -> { - if (DeviceConfig.Storage.ISOLATED_STORAGE_ENABLED.equals(name)) { - Settings.Global.putString(mResolver, - Settings.Global.ISOLATED_STORAGE_REMOTE, value); - } + refreshIsolatedStorageSettings(); }); refreshIsolatedStorageSettings(); } @@ -840,6 +837,12 @@ class StorageManagerService extends IStorageManager.Stub } private void refreshIsolatedStorageSettings() { + // Always copy value from newer DeviceConfig location + Settings.Global.putString(mResolver, + Settings.Global.ISOLATED_STORAGE_REMOTE, + DeviceConfig.getProperty(DeviceConfig.Storage.NAMESPACE, + DeviceConfig.Storage.ISOLATED_STORAGE_ENABLED)); + final int local = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.ISOLATED_STORAGE_LOCAL, 0); final int remote = Settings.Global.getInt(mContext.getContentResolver(), diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index e5436178217d..10b126f17715 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -206,9 +206,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private Map<Integer, List<EmergencyNumber>> mEmergencyNumberList; - private CallQuality mCallQuality; + private CallQuality mCallQuality = new CallQuality(); - private CallAttributes mCallAttributes; + private CallAttributes mCallAttributes = new CallAttributes(new PreciseCallState(), + TelephonyManager.NETWORK_TYPE_UNKNOWN, new CallQuality()); private int[] mSrvccState; diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java index 0aaea2f62de0..e42666c6a637 100644 --- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java +++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java @@ -48,6 +48,7 @@ class ActivityManagerDebugConfig { static final boolean DEBUG_BROADCAST = DEBUG_ALL || false; static final boolean DEBUG_BROADCAST_BACKGROUND = DEBUG_BROADCAST || false; static final boolean DEBUG_BROADCAST_LIGHT = DEBUG_BROADCAST || false; + static final boolean DEBUG_BROADCAST_DEFERRAL = DEBUG_BROADCAST || false; static final boolean DEBUG_LRU = DEBUG_ALL || false; static final boolean DEBUG_MU = DEBUG_ALL || false; static final boolean DEBUG_NETWORK = DEBUG_ALL || false; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index eb643b670fcd..d9f3c02e4292 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -224,7 +224,6 @@ import android.hardware.display.DisplayManagerInternal; import android.location.LocationManager; import android.media.audiofx.AudioEffect; import android.net.Proxy; -import android.net.ProxyInfo; import android.net.Uri; import android.os.AppZygote; import android.os.BatteryStats; @@ -2090,6 +2089,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (phase == PHASE_SYSTEM_SERVICES_READY) { mService.mBatteryStatsService.systemServicesReady(); mService.mServices.systemServicesReady(); + } else if (phase == PHASE_ACTIVITY_MANAGER_READY) { + mService.startBroadcastObservers(); } } @@ -2266,12 +2267,27 @@ public class ActivityManagerService extends IActivityManager.Stub mProcessList.init(this, activeUids); mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids); + // Broadcast policy parameters + final BroadcastConstants foreConstants = new BroadcastConstants( + Settings.Global.BROADCAST_FG_CONSTANTS); + foreConstants.TIMEOUT = BROADCAST_FG_TIMEOUT; + + final BroadcastConstants backConstants = new BroadcastConstants( + Settings.Global.BROADCAST_BG_CONSTANTS); + backConstants.TIMEOUT = BROADCAST_BG_TIMEOUT; + + final BroadcastConstants offloadConstants = new BroadcastConstants( + Settings.Global.BROADCAST_OFFLOAD_CONSTANTS); + offloadConstants.TIMEOUT = BROADCAST_BG_TIMEOUT; + // by default, no "slow" policy in this queue + offloadConstants.SLOW_TIME = Integer.MAX_VALUE; + mFgBroadcastQueue = new BroadcastQueue(this, mHandler, - "foreground", BROADCAST_FG_TIMEOUT, false); + "foreground", foreConstants, false); mBgBroadcastQueue = new BroadcastQueue(this, mHandler, - "background", BROADCAST_BG_TIMEOUT, true); + "background", backConstants, true); mOffloadBroadcastQueue = new BroadcastQueue(this, mHandler, - "offload", BROADCAST_BG_TIMEOUT, true); + "offload", offloadConstants, true); mBroadcastQueues[0] = mFgBroadcastQueue; mBroadcastQueues[1] = mBgBroadcastQueue; mBroadcastQueues[2] = mOffloadBroadcastQueue; @@ -5226,6 +5242,15 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public boolean isIntentSenderABroadcast(IIntentSender pendingResult) { + if (pendingResult instanceof PendingIntentRecord) { + final PendingIntentRecord res = (PendingIntentRecord) pendingResult; + return res.key.type == ActivityManager.INTENT_SENDER_BROADCAST; + } + return false; + } + + @Override public Intent getIntentForIntentSender(IIntentSender pendingResult) { enforceCallingPermission(Manifest.permission.GET_INTENT_SENDER_INTENT, "getIntentForIntentSender()"); @@ -7211,7 +7236,6 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { mSystemProvidersInstalled = true; } - mConstants.start(mContext.getContentResolver()); mCoreSettingsObserver = new CoreSettingsObserver(this); mActivityTaskManager.installSystemProviders(); @@ -8711,6 +8735,12 @@ public class ActivityManagerService extends IActivityManager.Stub } } + private void startBroadcastObservers() { + for (BroadcastQueue queue : mBroadcastQueues) { + queue.start(mContext.getContentResolver()); + } + } + private void updateForceBackgroundCheck(boolean enabled) { synchronized (this) { if (mForceBackgroundCheck != enabled) { @@ -14909,10 +14939,7 @@ public class ActivityManagerService extends IActivityManager.Stub resultData, resultExtras, ordered, sticky, false, userId, allowBackgroundActivityStarts); - if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r - + ": prev had " + queue.mOrderedBroadcasts.size()); - if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST, - "Enqueueing broadcast " + r.intent.getAction()); + if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r); final BroadcastRecord oldRecord = replacePending ? queue.replaceOrderedBroadcastLocked(r) : null; @@ -15788,13 +15815,12 @@ public class ActivityManagerService extends IActivityManager.Stub * Returns true if things are idle enough to perform GCs. */ private final boolean canGcNowLocked() { - boolean processingBroadcasts = false; for (BroadcastQueue q : mBroadcastQueues) { - if (q.mParallelBroadcasts.size() != 0 || q.mOrderedBroadcasts.size() != 0) { - processingBroadcasts = true; + if (!q.mParallelBroadcasts.isEmpty() || !q.mDispatcher.isEmpty()) { + return false; } } - return !processingBroadcasts && mAtmInternal.canGcNow(); + return mAtmInternal.canGcNow(); } /** diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java new file mode 100644 index 000000000000..820caf12ac84 --- /dev/null +++ b/services/core/java/com/android/server/am/BroadcastConstants.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import android.content.ContentResolver; +import android.database.ContentObserver; +import android.os.Handler; +import android.provider.Settings; +import android.util.KeyValueListParser; +import android.util.Slog; +import android.util.TimeUtils; + +import java.io.PrintWriter; + +/** + * Tunable parameters for broadcast dispatch policy + */ +public class BroadcastConstants { + private static final String TAG = "BroadcastConstants"; + + // Value element names within the Settings record + static final String KEY_TIMEOUT = "bcast_timeout"; + static final String KEY_SLOW_TIME = "bcast_slow_time"; + static final String KEY_DEFERRAL = "bcast_deferral"; + static final String KEY_DEFERRAL_DECAY_FACTOR = "bcast_deferral_decay_factor"; + static final String KEY_DEFERRAL_FLOOR = "bcast_deferral_floor"; + + // All time intervals are in milliseconds + private static final long DEFAULT_TIMEOUT = 10_000; + private static final long DEFAULT_SLOW_TIME = 5_000; + private static final long DEFAULT_DEFERRAL = 5_000; + private static final float DEFAULT_DEFERRAL_DECAY_FACTOR = 0.75f; + private static final long DEFAULT_DEFERRAL_FLOOR = 0; + + // All time constants are in milliseconds + + // Timeout period for this broadcast queue + public long TIMEOUT = DEFAULT_TIMEOUT; + // Handling time above which we declare that a broadcast recipient was "slow". Any + // value <= zero is interpreted as disabling broadcast deferral policy entirely. + public long SLOW_TIME = DEFAULT_SLOW_TIME; + // How long to initially defer broadcasts, if an app is slow to handle one + public long DEFERRAL = DEFAULT_DEFERRAL; + // Decay factor for successive broadcasts' deferral time + public float DEFERRAL_DECAY_FACTOR = DEFAULT_DEFERRAL_DECAY_FACTOR; + // Minimum that the deferral time can decay to until the backlog fully clears + public long DEFERRAL_FLOOR = DEFAULT_DEFERRAL_FLOOR; + + // Settings override tracking for this instance + private String mSettingsKey; + private SettingsObserver mSettingsObserver; + private ContentResolver mResolver; + private final KeyValueListParser mParser = new KeyValueListParser(','); + + class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + @Override + public void onChange(boolean selfChange) { + updateConstants(); + } + } + + // A given constants instance is configured to observe specific keys from which + // that instance's values are drawn. + public BroadcastConstants(String settingsKey) { + mSettingsKey = settingsKey; + } + + /** + * Spin up the observer lazily, since it can only happen once the settings provider + * has been brought into service + */ + public void startObserving(Handler handler, ContentResolver resolver) { + mResolver = resolver; + + mSettingsObserver = new SettingsObserver(handler); + mResolver.registerContentObserver(Settings.Global.getUriFor(mSettingsKey), + false, mSettingsObserver); + + updateConstants(); + } + + private void updateConstants() { + synchronized (mParser) { + try { + mParser.setString(Settings.Global.getString(mResolver, mSettingsKey)); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Bad broadcast settings in key '" + mSettingsKey + "'", e); + return; + } + + // Unspecified fields retain their current value rather than revert to default + TIMEOUT = mParser.getLong(KEY_TIMEOUT, TIMEOUT); + SLOW_TIME = mParser.getLong(KEY_SLOW_TIME, SLOW_TIME); + DEFERRAL = mParser.getLong(KEY_DEFERRAL, DEFERRAL); + DEFERRAL_DECAY_FACTOR = mParser.getFloat(KEY_DEFERRAL_DECAY_FACTOR, + DEFERRAL_DECAY_FACTOR); + DEFERRAL_FLOOR = mParser.getLong(KEY_DEFERRAL_FLOOR, DEFERRAL_FLOOR); + } + } + + /** + * Standard dumpsys support; invoked from BroadcastQueue dump + */ + public void dump(PrintWriter pw) { + synchronized (mParser) { + pw.println(); + pw.print(" Broadcast parameters (key="); + pw.print(mSettingsKey); + pw.print(", observing="); + pw.print(mSettingsObserver != null); + pw.println("):"); + + pw.print(" "); pw.print(KEY_TIMEOUT); pw.print(" = "); + TimeUtils.formatDuration(TIMEOUT, pw); + pw.println(); + + pw.print(" "); pw.print(KEY_SLOW_TIME); pw.print(" = "); + TimeUtils.formatDuration(SLOW_TIME, pw); + pw.println(); + + pw.print(" "); pw.print(KEY_DEFERRAL); pw.print(" = "); + TimeUtils.formatDuration(DEFERRAL, pw); + pw.println(); + + pw.print(" "); pw.print(KEY_DEFERRAL_DECAY_FACTOR); pw.print(" = "); + pw.println(DEFERRAL_DECAY_FACTOR); + + pw.print(" "); pw.print(KEY_DEFERRAL_FLOOR); pw.print(" = "); + TimeUtils.formatDuration(DEFERRAL_FLOOR, pw); + pw.println(); + } + } +} diff --git a/services/core/java/com/android/server/am/BroadcastDispatcher.java b/services/core/java/com/android/server/am/BroadcastDispatcher.java new file mode 100644 index 000000000000..0d46379112aa --- /dev/null +++ b/services/core/java/com/android/server/am/BroadcastDispatcher.java @@ -0,0 +1,687 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_DEFERRAL; + +import android.content.Intent; +import android.os.Handler; +import android.os.SystemClock; +import android.util.Slog; +import android.util.SparseIntArray; +import android.util.proto.ProtoOutputStream; + +import com.android.server.AlarmManagerInternal; +import com.android.server.LocalServices; + +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Set; + +/** + * Manages ordered broadcast delivery, applying policy to mitigate the effects of + * slow receivers. + */ +public class BroadcastDispatcher { + private static final String TAG = "BroadcastDispatcher"; + + // Deferred broadcasts to one app; times are all uptime time base like + // other broadcast-related timekeeping + static class Deferrals { + final int uid; + long deferredAt; // when we started deferring + long deferredBy; // how long did we defer by last time? + long deferUntil; // when does the next element become deliverable? + int alarmCount; + + final ArrayList<BroadcastRecord> broadcasts; + + Deferrals(int uid, long now, long backoff, int count) { + this.uid = uid; + this.deferredAt = now; + this.deferredBy = backoff; + this.deferUntil = now + backoff; + this.alarmCount = count; + broadcasts = new ArrayList<>(); + } + + void add(BroadcastRecord br) { + broadcasts.add(br); + } + + void writeToProto(ProtoOutputStream proto, long fieldId) { + for (BroadcastRecord br : broadcasts) { + br.writeToProto(proto, fieldId); + } + } + + void dumpLocked(Dumper d) { + for (BroadcastRecord br : broadcasts) { + d.dump(br); + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(128); + sb.append("Deferrals{uid="); + sb.append(uid); + sb.append(", deferUntil="); + sb.append(deferUntil); + sb.append(", #broadcasts="); + sb.append(broadcasts.size()); + sb.append("}"); + return sb.toString(); + } + } + + // Carrying dump formatting state across multiple concatenated datasets + class Dumper { + final PrintWriter mPw; + final String mQueueName; + final String mDumpPackage; + final SimpleDateFormat mSdf; + boolean mPrinted; + boolean mNeedSep; + String mHeading; + String mLabel; + int mOrdinal; + + Dumper(PrintWriter pw, String queueName, String dumpPackage, SimpleDateFormat sdf) { + mPw = pw; + mQueueName = queueName; + mDumpPackage = dumpPackage; + mSdf = sdf; + + mPrinted = false; + mNeedSep = true; + } + + void setHeading(String heading) { + mHeading = heading; + mPrinted = false; + } + + void setLabel(String label) { + //" Active Ordered Broadcast " + mQueueName + " #" + i + ":" + mLabel = " " + label + " " + mQueueName + " #"; + mOrdinal = 0; + } + + boolean didPrint() { + return mPrinted; + } + + void dump(BroadcastRecord br) { + if (mDumpPackage == null || mDumpPackage.equals(br.callerPackage)) { + if (!mPrinted) { + if (mNeedSep) { + mPw.println(); + } + mPrinted = true; + mNeedSep = true; + mPw.println(" " + mHeading + " [" + mQueueName + "]:"); + } + mPw.println(mLabel + mOrdinal + ":"); + mOrdinal++; + + br.dump(mPw, " ", mSdf); + } + } + } + + private final Object mLock; + private final BroadcastQueue mQueue; + private final BroadcastConstants mConstants; + private final Handler mHandler; + private AlarmManagerInternal mAlarm; + + // Current alarm targets; mapping uid -> in-flight alarm count + final SparseIntArray mAlarmUids = new SparseIntArray(); + final AlarmManagerInternal.InFlightListener mAlarmListener = + new AlarmManagerInternal.InFlightListener() { + @Override + public void broadcastAlarmPending(final int recipientUid) { + synchronized (mLock) { + final int newCount = mAlarmUids.get(recipientUid, 0) + 1; + mAlarmUids.put(recipientUid, newCount); + // any deferred broadcasts to this app now get fast-tracked + final int numEntries = mDeferredBroadcasts.size(); + for (int i = 0; i < numEntries; i++) { + if (recipientUid == mDeferredBroadcasts.get(i).uid) { + Deferrals d = mDeferredBroadcasts.remove(i); + mAlarmBroadcasts.add(d); + break; + } + } + } + } + + @Override + public void broadcastAlarmComplete(final int recipientUid) { + synchronized (mLock) { + final int newCount = mAlarmUids.get(recipientUid, 0) - 1; + if (newCount >= 0) { + mAlarmUids.put(recipientUid, newCount); + } else { + Slog.wtf(TAG, "Undercount of broadcast alarms in flight for " + recipientUid); + mAlarmUids.put(recipientUid, 0); + } + + // No longer an alarm target, so resume ordinary deferral policy + if (newCount <= 0) { + final int numEntries = mAlarmBroadcasts.size(); + for (int i = 0; i < numEntries; i++) { + if (recipientUid == mAlarmBroadcasts.get(i).uid) { + Deferrals d = mAlarmBroadcasts.remove(i); + insertLocked(mDeferredBroadcasts, d); + break; + } + } + } + } + } + }; + + // Queue recheck operation used to tickle broadcast delivery when appropriate + final Runnable mScheduleRunnable = new Runnable() { + @Override + public void run() { + synchronized (mLock) { + if (DEBUG_BROADCAST_DEFERRAL) { + Slog.v(TAG, "Deferral recheck of pending broadcasts"); + } + mQueue.scheduleBroadcastsLocked(); + mRecheckScheduled = false; + } + } + }; + private boolean mRecheckScheduled = false; + + // Usual issuance-order outbound queue + private final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>(); + // General deferrals not holding up alarms + private final ArrayList<Deferrals> mDeferredBroadcasts = new ArrayList<>(); + // Deferrals that *are* holding up alarms; ordered by alarm dispatch time + private final ArrayList<Deferrals> mAlarmBroadcasts = new ArrayList<>(); + + // Next outbound broadcast, established by getNextBroadcastLocked() + private BroadcastRecord mCurrentBroadcast; + + /** + * Constructed & sharing a lock with its associated BroadcastQueue instance + */ + public BroadcastDispatcher(BroadcastQueue queue, BroadcastConstants constants, + Handler handler, Object lock) { + mQueue = queue; + mConstants = constants; + mHandler = handler; + mLock = lock; + } + + /** + * Spin up the integration with the alarm manager service; done lazily to manage + * service availability ordering during boot. + */ + public void start() { + // Set up broadcast alarm tracking + mAlarm = LocalServices.getService(AlarmManagerInternal.class); + mAlarm.registerInFlightListener(mAlarmListener); + } + + /** + * Standard contents-are-empty check + */ + public boolean isEmpty() { + synchronized (mLock) { + return mCurrentBroadcast == null + && mOrderedBroadcasts.isEmpty() + && mDeferredBroadcasts.isEmpty() + && mAlarmBroadcasts.isEmpty(); + } + } + + /** + * Not quite the traditional size() measurement; includes any in-process but + * not yet retired active outbound broadcast. + */ + public int totalUndelivered() { + synchronized (mLock) { + return mAlarmBroadcasts.size() + + mDeferredBroadcasts.size() + + mOrderedBroadcasts.size() + + (mCurrentBroadcast == null ? 0 : 1); + } + } + + // ---------------------------------- + // BroadcastQueue operation support + + void enqueueOrderedBroadcastLocked(BroadcastRecord r) { + mOrderedBroadcasts.add(r); + } + + // Returns the now-replaced broadcast record, or null if none + BroadcastRecord replaceBroadcastLocked(BroadcastRecord r, String typeForLogging) { + // Simple case, in the ordinary queue. + BroadcastRecord old = replaceBroadcastLocked(mOrderedBroadcasts, r, typeForLogging); + + // If we didn't find it, less-simple: in a deferral queue? + if (old == null) { + old = replaceDeferredBroadcastLocked(mAlarmBroadcasts, r, typeForLogging); + } + if (old == null) { + old = replaceDeferredBroadcastLocked(mDeferredBroadcasts, r, typeForLogging); + } + return old; + } + + private BroadcastRecord replaceDeferredBroadcastLocked(ArrayList<Deferrals> list, + BroadcastRecord r, String typeForLogging) { + BroadcastRecord old; + final int numEntries = list.size(); + for (int i = 0; i < numEntries; i++) { + final Deferrals d = list.get(i); + old = replaceBroadcastLocked(d.broadcasts, r, typeForLogging); + if (old != null) { + return old; + } + } + return null; + } + + private BroadcastRecord replaceBroadcastLocked(ArrayList<BroadcastRecord> list, + BroadcastRecord r, String typeForLogging) { + BroadcastRecord old; + final Intent intent = r.intent; + // Any in-flight broadcast has already been popped, and cannot be replaced. + // (This preserves existing behavior of the replacement API) + for (int i = list.size() - 1; i >= 0; i++) { + old = list.get(i); + if (old.userId == r.userId && intent.filterEquals(old.intent)) { + if (DEBUG_BROADCAST) { + Slog.v(TAG, "***** Replacing " + typeForLogging + + " [" + mQueue.mQueueName + "]: " + intent); + } + // Clone deferral state too if any + r.deferred = old.deferred; + list.set(i, r); + return old; + } + } + return null; + } + + boolean cleanupDisabledPackageReceiversLocked(final String packageName, + Set<String> filterByClasses, final int userId, final boolean doit) { + // Note: fast short circuits when 'doit' is false, as soon as we hit any + // "yes we would do something" circumstance + boolean didSomething = cleanupBroadcastListDisabledReceiversLocked(mOrderedBroadcasts, + packageName, filterByClasses, userId, doit); + if (doit || !didSomething) { + didSomething |= cleanupDeferralsListDisabledReceiversLocked(mAlarmBroadcasts, + packageName, filterByClasses, userId, doit); + } + if (doit || !didSomething) { + didSomething |= cleanupDeferralsListDisabledReceiversLocked(mDeferredBroadcasts, + packageName, filterByClasses, userId, doit); + } + if ((doit || !didSomething) && mCurrentBroadcast != null) { + didSomething |= mCurrentBroadcast.cleanupDisabledPackageReceiversLocked( + packageName, filterByClasses, userId, doit); + } + + return didSomething; + } + + private boolean cleanupDeferralsListDisabledReceiversLocked(ArrayList<Deferrals> list, + final String packageName, Set<String> filterByClasses, final int userId, + final boolean doit) { + boolean didSomething = false; + for (Deferrals d : list) { + didSomething = cleanupBroadcastListDisabledReceiversLocked(d.broadcasts, + packageName, filterByClasses, userId, doit); + if (!doit && didSomething) { + return true; + } + } + return didSomething; + } + + private boolean cleanupBroadcastListDisabledReceiversLocked(ArrayList<BroadcastRecord> list, + final String packageName, Set<String> filterByClasses, final int userId, + final boolean doit) { + boolean didSomething = false; + for (BroadcastRecord br : list) { + didSomething |= br.cleanupDisabledPackageReceiversLocked(packageName, + filterByClasses, userId, doit); + if (!doit && didSomething) { + return true; + } + } + return didSomething; + } + + /** + * Standard proto dump entry point + */ + public void writeToProto(ProtoOutputStream proto, long fieldId) { + if (mCurrentBroadcast != null) { + mCurrentBroadcast.writeToProto(proto, fieldId); + } + for (Deferrals d : mAlarmBroadcasts) { + d.writeToProto(proto, fieldId); + } + for (BroadcastRecord br : mOrderedBroadcasts) { + br.writeToProto(proto, fieldId); + } + for (Deferrals d : mDeferredBroadcasts) { + d.writeToProto(proto, fieldId); + } + } + + // ---------------------------------- + // Dispatch & deferral management + + public BroadcastRecord getActiveBroadcastLocked() { + return mCurrentBroadcast; + } + + /** + * If there is a deferred broadcast that is being sent to an alarm target, return + * that one. If there's no deferred alarm target broadcast but there is one + * that has reached the end of its deferral, return that. + * + * This stages the broadcast internally until it is retired, and returns that + * staged record if this is called repeatedly, until retireBroadcast(r) is called. + */ + public BroadcastRecord getNextBroadcastLocked(final long now) { + if (mCurrentBroadcast != null) { + return mCurrentBroadcast; + } + + BroadcastRecord next = null; + if (!mAlarmBroadcasts.isEmpty()) { + next = popLocked(mAlarmBroadcasts); + if (DEBUG_BROADCAST_DEFERRAL && next != null) { + Slog.i(TAG, "Next broadcast from alarm targets: " + next); + } + } + + if (next == null && !mDeferredBroadcasts.isEmpty()) { + for (int i = 0; i < mDeferredBroadcasts.size(); i++) { + Deferrals d = mDeferredBroadcasts.get(i); + if (now < d.deferUntil) { + // No more deferrals due + break; + } + + if (d.broadcasts.size() > 0) { + next = d.broadcasts.remove(0); + // apply deferral-interval decay policy and move this uid's + // deferred broadcasts down in the delivery queue accordingly + mDeferredBroadcasts.remove(i); // already 'd' + d.deferredBy = calculateDeferral(d.deferredBy); + d.deferUntil += d.deferredBy; + insertLocked(mDeferredBroadcasts, d); + if (DEBUG_BROADCAST_DEFERRAL) { + Slog.i(TAG, "Next broadcast from deferrals " + next + + ", deferUntil now " + d.deferUntil); + } + break; + } + } + } + + if (next == null && !mOrderedBroadcasts.isEmpty()) { + next = mOrderedBroadcasts.remove(0); + if (DEBUG_BROADCAST_DEFERRAL) { + Slog.i(TAG, "Next broadcast from main queue: " + next); + } + } + + mCurrentBroadcast = next; + return next; + } + + /** + * Called after the broadcast queue finishes processing the currently + * active broadcast (obtained by calling getNextBroadcastLocked()). + */ + public void retireBroadcastLocked(final BroadcastRecord r) { + // ERROR if 'r' is not the active broadcast + if (r != mCurrentBroadcast) { + Slog.wtf(TAG, "Retiring broadcast " + r + + " doesn't match current outgoing " + mCurrentBroadcast); + } + mCurrentBroadcast = null; + } + + /** + * Called prior to broadcast dispatch to check whether the intended + * recipient is currently subject to deferral policy. + */ + public boolean isDeferringLocked(final int uid) { + Deferrals d = findUidLocked(uid); + if (d != null && d.broadcasts.isEmpty()) { + // once we've caught up with deferred broadcasts to this uid + // and time has advanced sufficiently that we wouldn't be + // deferring newly-enqueued ones, we're back to normal policy. + if (SystemClock.uptimeMillis() >= d.deferUntil) { + if (DEBUG_BROADCAST_DEFERRAL) { + Slog.i(TAG, "No longer deferring broadcasts to uid " + d.uid); + } + removeDeferral(d); + return false; + } + } + return (d != null); + } + + /** + * Defer broadcasts for the given app. If 'br' is non-null, this also makes + * sure that broadcast record is enqueued as the next upcoming broadcast for + * the app. + */ + public void startDeferring(final int uid) { + synchronized (mLock) { + Deferrals d = findUidLocked(uid); + + // If we're not yet tracking this app, set up that bookkeeping + if (d == null) { + // Start a new deferral + final long now = SystemClock.uptimeMillis(); + d = new Deferrals(uid, + now, + mConstants.DEFERRAL, + mAlarmUids.get(uid, 0)); + if (DEBUG_BROADCAST_DEFERRAL) { + Slog.i(TAG, "Now deferring broadcasts to " + uid + + " until " + d.deferUntil); + } + // where it goes depends on whether it is coming into an alarm-related situation + if (d.alarmCount == 0) { + // common case, put it in the ordinary priority queue + insertLocked(mDeferredBroadcasts, d); + scheduleDeferralCheckLocked(true); + } else { + // alarm-related: strict order-encountered + mAlarmBroadcasts.add(d); + } + } else { + // We're already deferring, but something was slow again. Reset the + // deferral decay progression. + d.deferredBy = mConstants.DEFERRAL; + if (DEBUG_BROADCAST_DEFERRAL) { + Slog.i(TAG, "Uid " + uid + " slow again, deferral interval reset to " + + d.deferredBy); + } + } + } + } + + /** + * Key entry point when a broadcast about to be delivered is instead + * set aside for deferred delivery + */ + public void addDeferredBroadcast(final int uid, BroadcastRecord br) { + if (DEBUG_BROADCAST_DEFERRAL) { + Slog.i(TAG, "Enqueuing deferred broadcast " + br); + } + synchronized (mLock) { + Deferrals d = findUidLocked(uid); + if (d == null) { + Slog.wtf(TAG, "Adding deferred broadcast but not tracking " + uid); + } else { + if (br == null) { + Slog.wtf(TAG, "Deferring null broadcast to " + uid); + } else { + br.deferred = true; + d.add(br); + } + } + } + } + + /** + * When there are deferred broadcasts, we need to make sure to recheck the + * dispatch queue when they come due. Alarm-sensitive deferrals get dispatched + * aggressively, so we only need to use the ordinary deferrals timing to figure + * out when to recheck. + */ + public void scheduleDeferralCheckLocked(boolean force) { + if ((force || !mRecheckScheduled) && !mDeferredBroadcasts.isEmpty()) { + final Deferrals d = mDeferredBroadcasts.get(0); + if (!d.broadcasts.isEmpty()) { + mHandler.removeCallbacks(mScheduleRunnable); + mHandler.postAtTime(mScheduleRunnable, d.deferUntil); + mRecheckScheduled = true; + if (DEBUG_BROADCAST_DEFERRAL) { + Slog.i(TAG, "Scheduling deferred broadcast recheck at " + d.deferUntil); + } + } + } + } + + // ---------------------------------- + + /** + * If broadcasts to this uid are being deferred, find the deferrals record about it. + * @return null if this uid's broadcasts are not being deferred + */ + private Deferrals findUidLocked(final int uid) { + // The common case is that they it isn't also an alarm target... + Deferrals d = findUidLocked(uid, mDeferredBroadcasts); + // ...but if not there, also check alarm-prioritized deferrals + if (d == null) { + d = findUidLocked(uid, mAlarmBroadcasts); + } + return d; + } + + /** + * Remove the given deferral record from whichever queue it might be in at present + * @return true if the deferral was in fact found, false if this made no changes + */ + private boolean removeDeferral(Deferrals d) { + boolean didRemove = mDeferredBroadcasts.remove(d); + if (!didRemove) { + didRemove = mAlarmBroadcasts.remove(d); + } + return didRemove; + } + + /** + * Find the deferrals record for the given uid in the given list + */ + private static Deferrals findUidLocked(final int uid, ArrayList<Deferrals> list) { + final int numElements = list.size(); + for (int i = 0; i < numElements; i++) { + Deferrals d = list.get(i); + if (uid == d.uid) { + return d; + } + } + return null; + } + + /** + * Pop the next broadcast record from the head of the given deferrals list, + * if one exists. + */ + private static BroadcastRecord popLocked(ArrayList<Deferrals> list) { + final Deferrals d = list.get(0); + return d.broadcasts.isEmpty() ? null : d.broadcasts.remove(0); + } + + /** + * Insert the given Deferrals into the priority queue, sorted by defer-until milestone + */ + private static void insertLocked(ArrayList<Deferrals> list, Deferrals d) { + // Simple linear search is appropriate here because we expect to + // have very few entries in the deferral lists (i.e. very few badly- + // behaving apps currently facing deferral) + int i; + final int numElements = list.size(); + for (i = 0; i < numElements; i++) { + if (d.deferUntil < list.get(i).deferUntil) { + break; + } + } + list.add(i, d); + } + + /** + * Calculate a new deferral time based on the previous time. This should decay + * toward zero, though a small nonzero floor is an option. + */ + private long calculateDeferral(long previous) { + return Math.max(mConstants.DEFERRAL_FLOOR, + (long) (previous * mConstants.DEFERRAL_DECAY_FACTOR)); + } + + // ---------------------------------- + + boolean dumpLocked(PrintWriter pw, String dumpPackage, String queueName, + SimpleDateFormat sdf) { + final Dumper dumper = new Dumper(pw, queueName, dumpPackage, sdf); + boolean printed = false; + + dumper.setHeading("Active ordered broadcasts"); + dumper.setLabel("Active Ordered Broadcast"); + for (Deferrals d : mAlarmBroadcasts) { + d.dumpLocked(dumper); + } + printed |= dumper.didPrint(); + + for (BroadcastRecord br : mOrderedBroadcasts) { + dumper.dump(br); + } + printed |= dumper.didPrint(); + + dumper.setHeading("Deferred ordered broadcasts"); + dumper.setLabel("Deferred Ordered Broadcast"); + for (Deferrals d : mDeferredBroadcasts) { + d.dumpLocked(dumper); + } + printed |= dumper.didPrint(); + + return printed; + } +} diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index cdf6e0ede865..64a36ef66f12 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -24,6 +24,7 @@ import android.app.AppOpsManager; import android.app.BroadcastOptions; import android.app.PendingIntent; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.IIntentReceiver; import android.content.IIntentSender; import android.content.Intent; @@ -45,6 +46,7 @@ import android.os.Trace; import android.os.UserHandle; import android.util.EventLog; import android.util.Slog; +import android.util.SparseIntArray; import android.util.StatsLog; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; @@ -75,14 +77,15 @@ public final class BroadcastQueue { final ActivityManagerService mService; /** - * Recognizable moniker for this queue + * Behavioral parameters such as timeouts and deferral policy, tracking Settings + * for runtime configurability */ - final String mQueueName; + final BroadcastConstants mConstants; /** - * Timeout period for this queue's broadcasts + * Recognizable moniker for this queue */ - final long mTimeoutPeriod; + final String mQueueName; /** * If true, we can delay broadcasts while waiting services to finish in the previous @@ -100,13 +103,18 @@ public final class BroadcastQueue { final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>(); /** - * List of all active broadcasts that are to be executed one at a time. - * The object at the top of the list is the currently activity broadcasts; - * those after it are waiting for the top to finish. As with parallel - * broadcasts, separate background- and foreground-priority queues are - * maintained. + * Tracking of the ordered broadcast queue, including deferral policy and alarm + * prioritization. */ - final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>(); + final BroadcastDispatcher mDispatcher; + + /** + * Refcounting for completion callbacks of split/deferred broadcasts. The key + * is an opaque integer token assigned lazily when a broadcast is first split + * into multiple BroadcastRecord objects. + */ + final SparseIntArray mSplitRefcounts = new SparseIntArray(); + private int mNextToken = 0; /** * Historical data of past broadcasts, for debugging. This is a ring buffer @@ -173,7 +181,8 @@ public final class BroadcastQueue { switch (msg.what) { case BROADCAST_INTENT_MSG: { if (DEBUG_BROADCAST) Slog.v( - TAG_BROADCAST, "Received BROADCAST_INTENT_MSG"); + TAG_BROADCAST, "Received BROADCAST_INTENT_MSG [" + + mQueueName + "]"); processNextBroadcast(true); } break; case BROADCAST_TIMEOUT_MSG: { @@ -201,12 +210,19 @@ public final class BroadcastQueue { } BroadcastQueue(ActivityManagerService service, Handler handler, - String name, long timeoutPeriod, boolean allowDelayBehindServices) { + String name, BroadcastConstants constants, boolean allowDelayBehindServices) { mService = service; mHandler = new BroadcastHandler(handler.getLooper()); mQueueName = name; - mTimeoutPeriod = timeoutPeriod; mDelayBehindServices = allowDelayBehindServices; + + mConstants = constants; + mDispatcher = new BroadcastDispatcher(this, mConstants, mHandler, mService); + } + + void start(ContentResolver resolver) { + mDispatcher.start(); + mConstants.startObserving(mHandler, resolver); } @Override @@ -224,7 +240,7 @@ public final class BroadcastQueue { } public void enqueueOrderedBroadcastLocked(BroadcastRecord r) { - mOrderedBroadcasts.add(r); + mDispatcher.enqueueOrderedBroadcastLocked(r); enqueueBroadcastHelper(r); } @@ -255,7 +271,7 @@ public final class BroadcastQueue { * the old one. */ public final BroadcastRecord replaceOrderedBroadcastLocked(BroadcastRecord r) { - return replaceBroadcastLocked(mOrderedBroadcasts, r, "ORDERED"); + return mDispatcher.replaceBroadcastLocked(r, "ORDERED"); } private BroadcastRecord replaceBroadcastLocked(ArrayList<BroadcastRecord> queue, @@ -365,14 +381,17 @@ public final class BroadcastQueue { } } + // Skip the current receiver, if any, that is in flight to the given process public void skipCurrentReceiverLocked(ProcessRecord app) { BroadcastRecord r = null; - if (mOrderedBroadcasts.size() > 0) { - BroadcastRecord br = mOrderedBroadcasts.get(0); - if (br.curApp == app) { - r = br; - } + final BroadcastRecord curActive = mDispatcher.getActiveBroadcastLocked(); + if (curActive != null && curActive.curApp == app) { + // confirmed: the current active broadcast is to the given app + r = curActive; } + + // If the current active broadcast isn't this BUT we're waiting for + // mPendingBroadcast to spin up the target app, that's what we use. if (r == null && mPendingBroadcast != null && mPendingBroadcast.curApp == app) { if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "[" + mQueueName + "] skip & discard pending app " + r); @@ -404,20 +423,29 @@ public final class BroadcastQueue { } public BroadcastRecord getMatchingOrderedReceiver(IBinder receiver) { - if (mOrderedBroadcasts.size() > 0) { - final BroadcastRecord r = mOrderedBroadcasts.get(0); - if (r != null && r.receiver == receiver) { - return r; - } + BroadcastRecord br = mDispatcher.getActiveBroadcastLocked(); + if (br != null && br.receiver == receiver) { + return br; } return null; } + // > 0 only, no worry about "eventual" recycling + private int nextSplitTokenLocked() { + int next = mNextToken + 1; + if (next <= 0) { + next = 1; + } + mNextToken = next; + return next; + } + public boolean finishReceiverLocked(BroadcastRecord r, int resultCode, String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) { final int state = r.state; final ActivityInfo receiver = r.curReceiver; final long finishTime = SystemClock.uptimeMillis(); + final long elapsed = finishTime - r.receiverTime; r.state = BroadcastRecord.IDLE; if (state == BroadcastRecord.IDLE) { Slog.w(TAG, "finishReceiver [" + mQueueName + "] called but state is IDLE"); @@ -428,8 +456,22 @@ public final class BroadcastQueue { // If we're abandoning this broadcast before any receivers were actually spun up, // nextReceiver is zero; in which case time-to-process bookkeeping doesn't apply. if (r.nextReceiver > 0) { - r.duration[r.nextReceiver - 1] = finishTime - r.receiverTime; + r.duration[r.nextReceiver - 1] = elapsed; + } + + // if this receiver was slow, impose deferral policy on the app. This will kick in + // when processNextBroadcastLocked() next finds this uid as a receiver identity. + if (mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) { + if (DEBUG_BROADCAST_DEFERRAL) { + Slog.i(TAG, "Broadcast receiver was slow: " + receiver + " br=" + r); + } + if (r.curApp != null) { + mDispatcher.startDeferring(r.curApp.uid); + } else { + Slog.d(TAG, "finish receiver curApp is null? " + r); + } } + r.receiver = null; r.intent.setComponent(null); if (r.curApp != null && r.curApp.curReceivers.contains(r)) { @@ -452,9 +494,10 @@ public final class BroadcastQueue { r.resultAbort = false; } + // If we want to wait behind services *AND* we're finishing the head/ + // active broadcast on its queue if (waitForServices && r.curComponent != null && r.queue.mDelayBehindServices - && r.queue.mOrderedBroadcasts.size() > 0 - && r.queue.mOrderedBroadcasts.get(0) == r) { + && r.queue.mDispatcher.getActiveBroadcastLocked() == r) { ActivityInfo nextReceiver; if (r.nextReceiver < r.receivers.size()) { Object obj = r.receivers.get(r.nextReceiver); @@ -488,8 +531,8 @@ public final class BroadcastQueue { } public void backgroundServicesFinishedLocked(int userId) { - if (mOrderedBroadcasts.size() > 0) { - BroadcastRecord br = mOrderedBroadcasts.get(0); + BroadcastRecord br = mDispatcher.getActiveBroadcastLocked(); + if (br != null) { if (br.userId == userId && br.state == BroadcastRecord.WAITING_SERVICES) { Slog.i(TAG, "Resuming delayed broadcast"); br.curComponent = null; @@ -861,7 +904,7 @@ public final class BroadcastQueue { if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast [" + mQueueName + "]: " + mParallelBroadcasts.size() + " parallel broadcasts, " - + mOrderedBroadcasts.size() + " ordered broadcasts"); + + mDispatcher.totalUndelivered() + " ordered broadcasts"); mService.updateCpuStats(); @@ -937,8 +980,12 @@ public final class BroadcastQueue { boolean looped = false; do { - if (mOrderedBroadcasts.size() == 0) { - // No more broadcasts pending, so all done! + final long now = SystemClock.uptimeMillis(); + r = mDispatcher.getNextBroadcastLocked(now); + + if (r == null) { + // No more broadcasts are deliverable right now, so all done! + mDispatcher.scheduleDeferralCheckLocked(false); mService.scheduleAppGcsLocked(); if (looped) { // If we had finished the last ordered broadcast, then @@ -954,7 +1001,7 @@ public final class BroadcastQueue { return; } - r = mOrderedBroadcasts.get(0); + boolean forceReceive = false; // Ensure that even if something goes awry with the timeout @@ -967,9 +1014,8 @@ public final class BroadcastQueue { // significant amounts of time. int numReceivers = (r.receivers != null) ? r.receivers.size() : 0; if (mService.mProcessesReady && r.dispatchTime > 0) { - long now = SystemClock.uptimeMillis(); if ((numReceivers > 0) && - (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) { + (now > r.dispatchTime + (2 * mConstants.TIMEOUT * numReceivers))) { Slog.w(TAG, "Hung broadcast [" + mQueueName + "] discarded after timeout failure:" + " now=" + now @@ -993,27 +1039,53 @@ public final class BroadcastQueue { return; } + // Is the current broadcast is done for any reason? if (r.receivers == null || r.nextReceiver >= numReceivers || r.resultAbort || forceReceive) { - // No more receivers for this broadcast! Send the final - // result if requested... + // Send the final result if requested if (r.resultTo != null) { - try { - if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST, - "Finishing broadcast [" + mQueueName + "] " - + r.intent.getAction() + " app=" + r.callerApp); - performReceiveLocked(r.callerApp, r.resultTo, - new Intent(r.intent), r.resultCode, - r.resultData, r.resultExtras, false, false, r.userId); - // Set this to null so that the reference - // (local and remote) isn't kept in the mBroadcastHistory. - r.resultTo = null; - } catch (RemoteException e) { - r.resultTo = null; - Slog.w(TAG, "Failure [" - + mQueueName + "] sending broadcast result of " - + r.intent, e); - + boolean sendResult = true; + + // if this was part of a split/deferral complex, update the refcount and only + // send the completion when we clear all of them + if (r.splitToken != 0) { + int newCount = mSplitRefcounts.get(r.splitToken) - 1; + if (newCount == 0) { + // done! clear out this record's bookkeeping and deliver + if (DEBUG_BROADCAST_DEFERRAL) { + Slog.i(TAG, "Sending broadcast completion for split token " + + r.splitToken); + } + mSplitRefcounts.delete(r.splitToken); + } else { + // still have some split broadcast records in flight; update refcount + // and hold off on the callback + if (DEBUG_BROADCAST_DEFERRAL) { + Slog.i(TAG, "Result refcount " + newCount + " for split token " + + r.splitToken + " - not sending completion yet"); + } + sendResult = false; + mSplitRefcounts.put(r.splitToken, newCount); + } + } + if (sendResult) { + try { + if (DEBUG_BROADCAST) { + Slog.i(TAG_BROADCAST, "Finishing broadcast [" + mQueueName + "] " + + r.intent.getAction() + " app=" + r.callerApp); + } + performReceiveLocked(r.callerApp, r.resultTo, + new Intent(r.intent), r.resultCode, + r.resultData, r.resultExtras, false, false, r.userId); + // Set this to null so that the reference + // (local and remote) isn't kept in the mBroadcastHistory. + r.resultTo = null; + } catch (RemoteException e) { + r.resultTo = null; + Slog.w(TAG, "Failure [" + + mQueueName + "] sending broadcast result of " + + r.intent, e); + } } } @@ -1031,11 +1103,76 @@ public final class BroadcastQueue { mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage, r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime); } - mOrderedBroadcasts.remove(0); + mDispatcher.retireBroadcastLocked(r); r = null; looped = true; continue; } + + // Check whether the next receiver is under deferral policy, and handle that + // accordingly. If the current broadcast was already part of deferred-delivery + // tracking, we know that it must now be deliverable as-is without re-deferral. + if (!r.deferred) { + final int receiverUid = r.getReceiverUid(r.receivers.get(r.nextReceiver)); + if (mDispatcher.isDeferringLocked(receiverUid)) { + if (DEBUG_BROADCAST_DEFERRAL) { + Slog.i(TAG_BROADCAST, "Next receiver in " + r + " uid " + receiverUid + + " at " + r.nextReceiver + " is under deferral"); + } + // If this is the only (remaining) receiver in the broadcast, "splitting" + // doesn't make sense -- just defer it as-is and retire it as the + // currently active outgoing broadcast. + BroadcastRecord defer; + if (r.nextReceiver + 1 == numReceivers) { + if (DEBUG_BROADCAST_DEFERRAL) { + Slog.i(TAG, "Sole receiver of " + r + + " is under deferral; setting aside and proceeding"); + } + defer = r; + mDispatcher.retireBroadcastLocked(r); + } else { + // Nontrivial case; split out 'uid's receivers to a new broadcast record + // and defer that, then loop and pick up continuing delivery of the current + // record (now absent those receivers). + + // The split operation is guaranteed to match at least at 'nextReceiver' + defer = r.splitRecipientsLocked(receiverUid, r.nextReceiver); + if (DEBUG_BROADCAST_DEFERRAL) { + Slog.i(TAG_BROADCAST, "Post split:"); + Slog.i(TAG_BROADCAST, "Original broadcast receivers:"); + for (int i = 0; i < r.receivers.size(); i++) { + Slog.i(TAG_BROADCAST, " " + r.receivers.get(i)); + } + Slog.i(TAG_BROADCAST, "Split receivers:"); + for (int i = 0; i < defer.receivers.size(); i++) { + Slog.i(TAG_BROADCAST, " " + defer.receivers.get(i)); + } + } + // Track completion refcount as well if relevant + if (r.resultTo != null) { + int token = r.splitToken; + if (token == 0) { + // first split of this record; refcount for 'r' and 'deferred' + r.splitToken = defer.splitToken = nextSplitTokenLocked(); + mSplitRefcounts.put(r.splitToken, 2); + } else { + // new split from an already-refcounted situation; increment count + final int curCount = mSplitRefcounts.get(token); + if (DEBUG_BROADCAST_DEFERRAL) { + if (curCount == 0) { + Slog.wtf(TAG, "Split refcount is zero with token for " + r); + } + } + mSplitRefcounts.put(token, curCount + 1); + } + } + } + mDispatcher.addDeferredBroadcast(receiverUid, defer); + r = null; + looped = true; + continue; + } + } } while (r == null); // Get the next receiver... @@ -1066,7 +1203,7 @@ public final class BroadcastQueue { + mQueueName + "] " + r); } if (! mPendingBroadcastTimeoutMessage) { - long timeoutTime = r.receiverTime + mTimeoutPeriod; + long timeoutTime = r.receiverTime + mConstants.TIMEOUT; if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Submitting BROADCAST_TIMEOUT_MSG [" + mQueueName + "] for " + r + " at " + timeoutTime); @@ -1474,12 +1611,12 @@ public final class BroadcastQueue { mPendingBroadcastTimeoutMessage = false; } - if (mOrderedBroadcasts.size() == 0) { + if (mDispatcher.isEmpty() || mDispatcher.getActiveBroadcastLocked() == null) { return; } long now = SystemClock.uptimeMillis(); - BroadcastRecord r = mOrderedBroadcasts.get(0); + BroadcastRecord r = mDispatcher.getActiveBroadcastLocked(); if (fromMsg) { if (!mService.mProcessesReady) { // Only process broadcast timeouts if the system is ready. That way @@ -1488,7 +1625,7 @@ public final class BroadcastQueue { return; } - long timeoutTime = r.receiverTime + mTimeoutPeriod; + long timeoutTime = r.receiverTime + mConstants.TIMEOUT; if (timeoutTime > now) { // We can observe premature timeouts because we do not cancel and reset the // broadcast timeout message after each receiver finishes. Instead, we set up @@ -1503,16 +1640,15 @@ public final class BroadcastQueue { } } - BroadcastRecord br = mOrderedBroadcasts.get(0); - if (br.state == BroadcastRecord.WAITING_SERVICES) { + if (r.state == BroadcastRecord.WAITING_SERVICES) { // In this case the broadcast had already finished, but we had decided to wait // for started services to finish as well before going on. So if we have actually // waited long enough time timeout the broadcast, let's give up on the whole thing // and just move on to the next. - Slog.i(TAG, "Waited long enough for: " + (br.curComponent != null - ? br.curComponent.flattenToShortString() : "(null)")); - br.curComponent = null; - br.state = BroadcastRecord.IDLE; + Slog.i(TAG, "Waited long enough for: " + (r.curComponent != null + ? r.curComponent.flattenToShortString() : "(null)")); + r.curComponent = null; + r.state = BroadcastRecord.IDLE; processNextBroadcast(false); return; } @@ -1619,13 +1755,8 @@ public final class BroadcastQueue { } } - for (int i = mOrderedBroadcasts.size() - 1; i >= 0; i--) { - didSomething |= mOrderedBroadcasts.get(i).cleanupDisabledPackageReceiversLocked( - packageName, filterByClasses, userId, doit); - if (!doit && didSomething) { - return true; - } - } + didSomething |= mDispatcher.cleanupDisabledPackageReceiversLocked(packageName, + filterByClasses, userId, doit); return didSomething; } @@ -1665,7 +1796,7 @@ public final class BroadcastQueue { } final boolean isIdle() { - return mParallelBroadcasts.isEmpty() && mOrderedBroadcasts.isEmpty() + return mParallelBroadcasts.isEmpty() && mDispatcher.isEmpty() && (mPendingBroadcast == null); } @@ -1677,10 +1808,7 @@ public final class BroadcastQueue { for (int i = N - 1; i >= 0; i--) { mParallelBroadcasts.get(i).writeToProto(proto, BroadcastQueueProto.PARALLEL_BROADCASTS); } - N = mOrderedBroadcasts.size(); - for (int i = N - 1; i >= 0; i--) { - mOrderedBroadcasts.get(i).writeToProto(proto, BroadcastQueueProto.ORDERED_BROADCASTS); - } + mDispatcher.writeToProto(proto, BroadcastQueueProto.ORDERED_BROADCASTS); if (mPendingBroadcast != null) { mPendingBroadcast.writeToProto(proto, BroadcastQueueProto.PENDING_BROADCAST); } @@ -1721,7 +1849,7 @@ public final class BroadcastQueue { final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, int opti, boolean dumpAll, String dumpPackage, boolean needSep) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0 + if (!mParallelBroadcasts.isEmpty() || !mDispatcher.isEmpty() || mPendingBroadcast != null) { boolean printed = false; for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) { @@ -1740,29 +1868,12 @@ public final class BroadcastQueue { pw.println(" Active Broadcast " + mQueueName + " #" + i + ":"); br.dump(pw, " ", sdf); } - printed = false; - needSep = true; - for (int i = mOrderedBroadcasts.size() - 1; i >= 0; i--) { - BroadcastRecord br = mOrderedBroadcasts.get(i); - if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) { - continue; - } - if (!printed) { - if (needSep) { - pw.println(); - } - needSep = true; - printed = true; - pw.println(" Active ordered broadcasts [" + mQueueName + "]:"); - } - pw.println(" Active Ordered Broadcast " + mQueueName + " #" + i + ":"); - mOrderedBroadcasts.get(i).dump(pw, " ", sdf); - } + + mDispatcher.dumpLocked(pw, dumpPackage, mQueueName, sdf); + if (dumpPackage == null || (mPendingBroadcast != null && dumpPackage.equals(mPendingBroadcast.callerPackage))) { - if (needSep) { - pw.println(); - } + pw.println(); pw.println(" Pending broadcast [" + mQueueName + "]:"); if (mPendingBroadcast != null) { mPendingBroadcast.dump(pw, " ", sdf); @@ -1773,6 +1884,8 @@ public final class BroadcastQueue { } } + mConstants.dump(pw); + int i; boolean printed = false; diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index 9e799f6f14f3..d9e03f8f908f 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -36,10 +36,12 @@ import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; /** * An active intent broadcast. @@ -64,6 +66,9 @@ final class BroadcastRecord extends Binder { final int[] delivery; // delivery state of each receiver final long[] duration; // duration a receiver took to process broadcast IIntentReceiver resultTo; // who receives final result if non-null + boolean deferred; + int splitCount; // refcount for result callback, when split + int splitToken; // identifier for cross-BroadcastRecord refcount long enqueueClockTime; // the clock time the broadcast was enqueued long dispatchTime; // when dispatch started on this set of receivers long dispatchClockTime; // the clock time the dispatch started @@ -106,6 +111,9 @@ final class BroadcastRecord extends Binder { ComponentName curComponent; // the receiver class that is currently running. ActivityInfo curReceiver; // info about the receiver that is currently running. + // Private refcount-management bookkeeping; start > 0 + static AtomicInteger sNextToken = new AtomicInteger(1); + void dump(PrintWriter pw, String prefix, SimpleDateFormat sdf) { final long now = SystemClock.uptimeMillis(); @@ -304,6 +312,52 @@ final class BroadcastRecord extends Binder { allowBackgroundActivityStarts = from.allowBackgroundActivityStarts; } + /** + * Split off a new BroadcastRecord that clones this one, but contains only the + * recipient records for the current (just-finished) receiver's app, starting + * after the just-finished receiver [i.e. at r.nextReceiver]. Returns null + * if there are no matching subsequent receivers in this BroadcastRecord. + */ + BroadcastRecord splitRecipientsLocked(int slowAppUid, int startingAt) { + // Do we actually have any matching receivers down the line...? + ArrayList splitReceivers = null; + for (int i = startingAt; i < receivers.size(); ) { + Object o = receivers.get(i); + if (getReceiverUid(o) == slowAppUid) { + if (splitReceivers == null) { + splitReceivers = new ArrayList<>(); + } + splitReceivers.add(o); + receivers.remove(i); + break; + } else { + i++; + } + } + + // No later receivers in the same app, so we have no more to do + if (splitReceivers == null) { + return null; + } + + // build a new BroadcastRecord around that single-target list + BroadcastRecord split = new BroadcastRecord(queue, intent, callerApp, + callerPackage, callingPid, callingUid, callerInstantApp, resolvedType, + requiredPermissions, appOp, options, splitReceivers, resultTo, resultCode, + resultData, resultExtras, ordered, sticky, initialSticky, userId, + allowBackgroundActivityStarts); + + return split; + } + + int getReceiverUid(Object receiver) { + if (receiver instanceof BroadcastFilter) { + return ((BroadcastFilter) receiver).owningUid; + } else /* if (receiver instanceof ResolveInfo) */ { + return ((ResolveInfo) receiver).activityInfo.applicationInfo.uid; + } + } + public BroadcastRecord maybeStripForHistory() { if (!intent.canStripForHistory()) { return this; diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index 3d69aa8088f5..360d2960f61a 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -69,10 +69,11 @@ final class CoreSettingsObserver extends ContentObserver { sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS_GLES, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYER_APP, String.class); sGlobalSettingToTypeMap.put(Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, int.class); - sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_ALL_APPS, int.class); - sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_OPT_IN_APPS, String.class); - sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_OPT_OUT_APPS, String.class); - sGlobalSettingToTypeMap.put(Settings.Global.GUP_BLACKLIST, String.class); + sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_ALL_APPS, int.class); + sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_OPT_IN_APPS, String.class); + sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_OPT_OUT_APPS, String.class); + sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_BLACKLIST, String.class); + sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_WHITELIST, String.class); // add other global settings here... } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 7ede6dcd41bd..26d2d17fec7d 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -16,8 +16,8 @@ package com.android.server.appop; -import static android.app.AppOpsManager.OP_PLAY_AUDIO; import static android.app.AppOpsManager.OP_NONE; +import static android.app.AppOpsManager.OP_PLAY_AUDIO; import static android.app.AppOpsManager.UID_STATE_BACKGROUND; import static android.app.AppOpsManager.UID_STATE_CACHED; import static android.app.AppOpsManager.UID_STATE_FOREGROUND; @@ -94,9 +94,9 @@ import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.internal.util.function.pooled.PooledLambda; - import com.android.server.LocalServices; import com.android.server.LockGuard; + import libcore.util.EmptyArray; import org.xmlpull.v1.XmlPullParser; @@ -1282,6 +1282,46 @@ public class AppOpsService extends IAppOpsService.Stub { } } + /** + * Set all {@link #setMode (package) modes} for this uid to the default value. + * + * @param code The app-op + * @param uid The uid + */ + private void setAllPkgModesToDefault(int code, int uid) { + synchronized (this) { + UidState uidState = getUidStateLocked(uid, false); + if (uidState == null) { + return; + } + + ArrayMap<String, Ops> pkgOps = uidState.pkgOps; + if (pkgOps == null) { + return; + } + + int numPkgs = pkgOps.size(); + for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) { + Ops ops = pkgOps.valueAt(pkgNum); + + Op op = ops.get(code); + if (op == null) { + continue; + } + + int defaultMode = AppOpsManager.opToDefaultMode(code); + if (op.mode != defaultMode) { + Slog.w(TAG, "resetting app-op mode for " + AppOpsManager.opToName(code) + " of " + + pkgOps.keyAt(pkgNum)); + + op.mode = defaultMode; + + scheduleWriteLocked(); + } + } + } + } + @Override public void setMode(int code, int uid, String packageName, int mode) { setMode(code, uid, packageName, mode, true, false); @@ -4387,5 +4427,10 @@ public class AppOpsService extends IAppOpsService.Stub { public void setUidMode(int code, int uid, int mode) { AppOpsService.this.setUidMode(code, uid, mode); } + + @Override + public void setAllPkgModesToDefault(int code, int uid) { + AppOpsService.this.setAllPkgModesToDefault(code, uid); + } } } diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java index b71a7517ab12..5b469fe2dc7a 100644 --- a/services/core/java/com/android/server/attention/AttentionManagerService.java +++ b/services/core/java/com/android/server/attention/AttentionManagerService.java @@ -16,6 +16,10 @@ package com.android.server.attention; +import static android.provider.DeviceConfig.AttentionManagerService.NAMESPACE; +import static android.provider.DeviceConfig.AttentionManagerService.PROPERTY_COMPONENT_NAME; +import static android.provider.DeviceConfig.AttentionManagerService.PROPERTY_SERVICE_ENABLED; + import android.Manifest; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -40,6 +44,7 @@ import android.os.PowerManager; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; +import android.provider.DeviceConfig; import android.service.attention.AttentionService; import android.service.attention.AttentionService.AttentionFailureCodes; import android.service.attention.IAttentionCallback; @@ -66,6 +71,9 @@ import java.io.PrintWriter; public class AttentionManagerService extends SystemService { private static final String LOG_TAG = "AttentionManagerService"; + /** Default value in absence of {@link DeviceConfig} override. */ + private static final boolean DEFAULT_SERVICE_ENABLED = true; + /** Service will unbind if connection is not used for that amount of time. */ private static final long CONNECTION_TTL_MILLIS = 60_000; @@ -105,7 +113,7 @@ public class AttentionManagerService extends SystemService { super.onBootPhase(phase); if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { mComponentName = resolveAttentionService(mContext); - if (mComponentName != null) { + if (isAttentionServiceSupported()) { // If the service is supported we want to keep receiving the screen off events. mContext.registerReceiver(new ScreenStateReceiver(), new IntentFilter(Intent.ACTION_SCREEN_OFF)); @@ -117,7 +125,12 @@ public class AttentionManagerService extends SystemService { * Returns {@code true} if attention service is supported on this device. */ public boolean isAttentionServiceSupported() { - return mComponentName != null; + return mComponentName != null && isServiceEnabled(); + } + + private boolean isServiceEnabled() { + final String enabled = DeviceConfig.getProperty(NAMESPACE, PROPERTY_SERVICE_ENABLED); + return enabled == null ? DEFAULT_SERVICE_ENABLED : "true".equals(enabled); } /** @@ -266,8 +279,9 @@ public class AttentionManagerService extends SystemService { * system. */ private static ComponentName resolveAttentionService(Context context) { - // TODO(b/111939367): add a flag to turn on/off. - final String componentNameString = context.getString( + final String flag = DeviceConfig.getProperty(NAMESPACE, PROPERTY_COMPONENT_NAME); + + final String componentNameString = flag != null ? flag : context.getString( R.string.config_defaultAttentionService); if (TextUtils.isEmpty(componentNameString)) { diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java index 174ecfa12ee6..2791165293af 100644 --- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java @@ -279,7 +279,7 @@ public abstract class BiometricServiceBase extends SystemService } } - protected class EnrollClientImpl extends EnrollClient { + protected abstract class EnrollClientImpl extends EnrollClient { public EnrollClientImpl(Context context, DaemonWrapper daemon, long halDeviceId, IBinder token, ServiceListener listener, int userId, int groupId, diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java index 8a0f0858cedc..3ff94bce604d 100644 --- a/services/core/java/com/android/server/biometrics/EnrollClient.java +++ b/services/core/java/com/android/server/biometrics/EnrollClient.java @@ -36,6 +36,8 @@ public abstract class EnrollClient extends ClientMonitor { private final BiometricUtils mBiometricUtils; private final int[] mDisabledFeatures; + public abstract boolean shouldVibrate(); + public EnrollClient(Context context, Metrics metrics, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int userId, int groupId, @@ -62,7 +64,9 @@ public abstract class EnrollClient extends ClientMonitor { */ private boolean sendEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) { - vibrateSuccess(); + if (shouldVibrate()) { + vibrateSuccess(); + } mMetricsLogger.action(mMetrics.actionBiometricEnroll()); try { getListener().onEnrollResult(identifier, remaining); diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index a2aacdde4d9f..d4be539170fd 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -121,7 +121,12 @@ public class FaceService extends BiometricServiceBase { final boolean restricted = isRestricted(); final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, - 0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures); + 0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures) { + @Override + public boolean shouldVibrate() { + return false; + } + }; enrollInternal(client, UserHandle.getCallingUserId()); } diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java index fcf821f23ae9..f84cda03c594 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java @@ -142,7 +142,12 @@ public class FingerprintService extends BiometricServiceBase { final int groupId = userId; // default group for fingerprint enrollment final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, groupId, - cryptoToken, restricted, opPackageName, new int[0] /* disabledFeatures */); + cryptoToken, restricted, opPackageName, new int[0] /* disabledFeatures */) { + @Override + public boolean shouldVibrate() { + return true; + } + }; enrollInternal(client, userId); } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 62a1b036daa0..250884431440 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -151,7 +151,7 @@ public class Vpn { .divide(BigInteger.valueOf(100)); } // How many routes to evaluate before bailing and declaring this Vpn should provide - // the INTERNET capability. This is necessary because computing the adress space is + // the INTERNET capability. This is necessary because computing the address space is // O(n²) and this is running in the system service, so a limit is needed to alleviate // the risk of attack. // This is taken as a total of IPv4 + IPV6 routes for simplicity, but the algorithm @@ -194,6 +194,12 @@ public class Vpn { private boolean mLockdown = false; /** + * Set of packages in addition to the VPN app itself that can access the network directly when + * VPN is not connected even if {@code mLockdown} is set. + */ + private @NonNull List<String> mLockdownWhitelist = Collections.emptyList(); + + /** * List of UIDs for which networking should be blocked until VPN is ready, during brief periods * when VPN is not running. For example, during system startup or after a crash. * @see mLockdown @@ -320,9 +326,9 @@ public class Vpn { * * Used to enable/disable legacy VPN lockdown. * - * This uses the same ip rule mechanism as {@link #setAlwaysOnPackage(String, boolean)}; - * previous settings from calling that function will be replaced and saved with the - * always-on state. + * This uses the same ip rule mechanism as + * {@link #setAlwaysOnPackage(String, boolean, List<String>)}; previous settings from calling + * that function will be replaced and saved with the always-on state. * * @param lockdown whether to prevent all traffic outside of a VPN. */ @@ -419,12 +425,14 @@ public class Vpn { * * @param packageName the package to designate as always-on VPN supplier. * @param lockdown whether to prevent traffic outside of a VPN, for example while connecting. + * @param lockdownWhitelist packages to be whitelisted from lockdown. * @return {@code true} if the package has been set as always-on, {@code false} otherwise. */ - public synchronized boolean setAlwaysOnPackage(String packageName, boolean lockdown) { + public synchronized boolean setAlwaysOnPackage( + String packageName, boolean lockdown, List<String> lockdownWhitelist) { enforceControlPermissionOrInternalCaller(); - if (setAlwaysOnPackageInternal(packageName, lockdown)) { + if (setAlwaysOnPackageInternal(packageName, lockdown, lockdownWhitelist)) { saveAlwaysOnPackage(); return true; } @@ -439,15 +447,27 @@ public class Vpn { * * @param packageName the package to designate as always-on VPN supplier. * @param lockdown whether to prevent traffic outside of a VPN, for example while connecting. + * @param lockdownWhitelist packages to be whitelisted from lockdown. This is only used if + * {@code lockdown} is {@code true}. Packages must not contain commas. * @return {@code true} if the package has been set as always-on, {@code false} otherwise. */ @GuardedBy("this") - private boolean setAlwaysOnPackageInternal(String packageName, boolean lockdown) { + private boolean setAlwaysOnPackageInternal( + String packageName, boolean lockdown, List<String> lockdownWhitelist) { if (VpnConfig.LEGACY_VPN.equals(packageName)) { Log.w(TAG, "Not setting legacy VPN \"" + packageName + "\" as always-on."); return false; } + if (lockdownWhitelist != null) { + for (String pkg : lockdownWhitelist) { + if (pkg.contains(",")) { + Log.w(TAG, "Not setting always-on vpn, invalid whitelisted package: " + pkg); + return false; + } + } + } + if (packageName != null) { // Pre-authorize new always-on VPN package. if (!setPackageAuthorization(packageName, true)) { @@ -460,13 +480,18 @@ public class Vpn { } mLockdown = (mAlwaysOn && lockdown); + mLockdownWhitelist = (mLockdown && lockdownWhitelist != null) + ? Collections.unmodifiableList(new ArrayList<>(lockdownWhitelist)) + : Collections.emptyList(); + if (isCurrentPreparedPackage(packageName)) { updateAlwaysOnNotification(mNetworkInfo.getDetailedState()); + setVpnForcedLocked(mLockdown); } else { // Prepare this app. The notification will update as a side-effect of updateState(). + // It also calls setVpnForcedLocked(). prepareInternal(packageName); } - setVpnForcedLocked(mLockdown); return true; } @@ -478,7 +503,6 @@ public class Vpn { * @return the package name of the VPN controller responsible for always-on VPN, * or {@code null} if none is set or always-on VPN is controlled through * lockdown instead. - * @hide */ public synchronized String getAlwaysOnPackage() { enforceControlPermissionOrInternalCaller(); @@ -486,6 +510,13 @@ public class Vpn { } /** + * @return an immutable list of packages whitelisted from always-on VPN lockdown. + */ + public synchronized List<String> getLockdownWhitelist() { + return mLockdown ? mLockdownWhitelist : null; + } + + /** * Save the always-on package and lockdown config into Settings.Secure */ @GuardedBy("this") @@ -496,6 +527,9 @@ public class Vpn { getAlwaysOnPackage(), mUserHandle); mSystemServices.settingsSecurePutIntForUser(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN, (mAlwaysOn && mLockdown ? 1 : 0), mUserHandle); + mSystemServices.settingsSecurePutStringForUser( + Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST, + String.join(",", mLockdownWhitelist), mUserHandle); } finally { Binder.restoreCallingIdentity(token); } @@ -512,7 +546,11 @@ public class Vpn { Settings.Secure.ALWAYS_ON_VPN_APP, mUserHandle); final boolean alwaysOnLockdown = mSystemServices.settingsSecureGetIntForUser( Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN, 0 /*default*/, mUserHandle) != 0; - setAlwaysOnPackageInternal(alwaysOnPackage, alwaysOnLockdown); + final String whitelistString = mSystemServices.settingsSecureGetStringForUser( + Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST, mUserHandle); + final List<String> whitelistedPackages = TextUtils.isEmpty(whitelistString) + ? Collections.emptyList() : Arrays.asList(whitelistString.split(",")); + setAlwaysOnPackageInternal(alwaysOnPackage, alwaysOnLockdown, whitelistedPackages); } finally { Binder.restoreCallingIdentity(token); } @@ -532,7 +570,7 @@ public class Vpn { } // Remove always-on VPN if it's not supported. if (!isAlwaysOnPackageSupported(alwaysOnPackage)) { - setAlwaysOnPackage(null, false); + setAlwaysOnPackage(null, false, null); return false; } // Skip if the service is already established. This isn't bulletproof: it's not bound @@ -1249,9 +1287,10 @@ public class Vpn { } /** - * Restrict network access from all UIDs affected by this {@link Vpn}, apart from the VPN - * service app itself, to only sockets that have had {@code protect()} called on them. All - * non-VPN traffic is blocked via a {@code PROHIBIT} response from the kernel. + * Restricts network access from all UIDs affected by this {@link Vpn}, apart from the VPN + * service app itself and whitelisted packages, to only sockets that have had {@code protect()} + * called on them. All non-VPN traffic is blocked via a {@code PROHIBIT} response from the + * kernel. * * The exception for the VPN UID isn't technically necessary -- setup should use protected * sockets -- but in practice it saves apps that don't protect their sockets from breaking. @@ -1267,8 +1306,13 @@ public class Vpn { */ @GuardedBy("this") private void setVpnForcedLocked(boolean enforce) { - final List<String> exemptedPackages = - isNullOrLegacyVpn(mPackage) ? null : Collections.singletonList(mPackage); + final List<String> exemptedPackages; + if (isNullOrLegacyVpn(mPackage)) { + exemptedPackages = null; + } else { + exemptedPackages = new ArrayList<>(mLockdownWhitelist); + exemptedPackages.add(mPackage); + } final Set<UidRange> removedRanges = new ArraySet<>(mBlockedUsers); Set<UidRange> addedRanges = Collections.emptySet(); diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 95665989cc34..7414e55ed94a 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -418,10 +418,15 @@ final class LogicalDisplay { // Now add back the offset for the masked area. mTempDisplayRect.offset(maskingInsets.left, maskingInsets.top); - mTempDisplayRect.left += mDisplayOffsetX; - mTempDisplayRect.right += mDisplayOffsetX; - mTempDisplayRect.top += mDisplayOffsetY; - mTempDisplayRect.bottom += mDisplayOffsetY; + if (orientation == Surface.ROTATION_0) { + mTempDisplayRect.offset(mDisplayOffsetX, mDisplayOffsetY); + } else if (orientation == Surface.ROTATION_90) { + mTempDisplayRect.offset(mDisplayOffsetY, -mDisplayOffsetX); + } else if (orientation == Surface.ROTATION_180) { + mTempDisplayRect.offset(-mDisplayOffsetX, -mDisplayOffsetY); + } else { // Surface.ROTATION_270 + mTempDisplayRect.offset(-mDisplayOffsetY, mDisplayOffsetX); + } device.setProjectionLocked(t, orientation, mTempLayerStackRect, mTempDisplayRect); } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 4db541c29448..d20508a5e704 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -17,9 +17,6 @@ package com.android.server.inputmethod; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; -import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; -import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.inputmethod.InputMethodSystemProperty.PER_PROFILE_IME_ENABLED; import static java.lang.annotation.RetentionPolicy.SOURCE; @@ -110,7 +107,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; -import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputBinding; @@ -207,6 +204,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub static final int MSG_SET_ACTIVE = 3020; static final int MSG_SET_INTERACTIVE = 3030; static final int MSG_REPORT_FULLSCREEN_MODE = 3045; + static final int MSG_REPORT_PRE_RENDERED = 3060; + static final int MSG_APPLY_IME_VISIBILITY = 3070; static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000; @@ -481,7 +480,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub IBinder mLastImeTargetWindow; /** - * {@link WindowManager.LayoutParams#softInputMode} of {@link #mCurFocusedWindow}. + * {@link LayoutParams#softInputMode} of {@link #mCurFocusedWindow}. * * @see #mCurFocusedWindow */ @@ -2040,7 +2039,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Slog.v(TAG, "Adding window token: " + mCurToken + " for display: " + mCurTokenDisplayId); } - mIWindowManager.addWindowToken(mCurToken, TYPE_INPUT_METHOD, mCurTokenDisplayId); + mIWindowManager.addWindowToken(mCurToken, LayoutParams.TYPE_INPUT_METHOD, + mCurTokenDisplayId); } catch (RemoteException e) { } return new InputBindResult( @@ -2755,33 +2755,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Slog.e(TAG, "windowToken cannot be null."); return InputBindResult.NULL; } - final InputBindResult result = startInputOrWindowGainedFocusInternal(startInputReason, - client, windowToken, startInputFlags, softInputMode, windowFlags, attribute, - inputContext, missingMethods, unverifiedTargetSdkVersion); - if (result == null) { - // This must never happen, but just in case. - Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason=" - + InputMethodDebug.startInputReasonToString(startInputReason) - + " windowFlags=#" + Integer.toHexString(windowFlags) - + " editorInfo=" + attribute); - return InputBindResult.NULL; - } - return result; - } - - @NonNull - private InputBindResult startInputOrWindowGainedFocusInternal( - @StartInputReason int startInputReason, IInputMethodClient client, - @NonNull IBinder windowToken, @StartInputFlags int startInputFlags, - @SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo attribute, - IInputContext inputContext, @MissingMethodFlags int missingMethods, - int unverifiedTargetSdkVersion) { final int callingUserId = UserHandle.getCallingUserId(); final int userId; if (attribute != null && attribute.targetInputMethodUser != null && attribute.targetInputMethodUser.getIdentifier() != callingUserId) { mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, - "Using EditorInfo.user requires INTERACT_ACROSS_USERS_FULL."); + "Using EditorInfo.targetInputMethodUser requires INTERACT_ACROSS_USERS_FULL."); userId = attribute.targetInputMethodUser.getIdentifier(); if (!mUserManagerInternal.isUserRunning(userId)) { // There is a chance that we hit here because of race condition. Let's just return @@ -2793,219 +2772,235 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } else { userId = callingUserId; } - InputBindResult res = null; + final InputBindResult result; synchronized (mMethodMap) { - final int windowDisplayId = - mWindowManagerInternal.getDisplayIdForWindow(windowToken); final long ident = Binder.clearCallingIdentity(); try { - if (DEBUG) Slog.v(TAG, "startInputOrWindowGainedFocusInternal: reason=" - + InputMethodDebug.startInputReasonToString(startInputReason) - + " client=" + client.asBinder() - + " inputContext=" + inputContext - + " missingMethods=" - + InputConnectionInspector.getMissingMethodFlagsAsString(missingMethods) - + " attribute=" + attribute - + " startInputFlags=" - + InputMethodDebug.startInputFlagsToString(startInputFlags) - + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode) - + " windowFlags=#" + Integer.toHexString(windowFlags) - + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion); - - ClientState cs = mClients.get(client.asBinder()); - if (cs == null) { - throw new IllegalArgumentException("unknown client " - + client.asBinder()); - } - if (cs.selfReportedDisplayId != windowDisplayId) { - Slog.e(TAG, "startInputOrWindowGainedFocusInternal: display ID mismatch." - + " from client:" + cs.selfReportedDisplayId - + " from window:" + windowDisplayId); - return InputBindResult.DISPLAY_ID_MISMATCH; - } + result = startInputOrWindowGainedFocusInternalLocked(startInputReason, client, + windowToken, startInputFlags, softInputMode, windowFlags, attribute, + inputContext, missingMethods, unverifiedTargetSdkVersion, userId); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + if (result == null) { + // This must never happen, but just in case. + Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason=" + + InputMethodDebug.startInputReasonToString(startInputReason) + + " windowFlags=#" + Integer.toHexString(windowFlags) + + " editorInfo=" + attribute); + return InputBindResult.NULL; + } + return result; + } - if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, - cs.selfReportedDisplayId)) { - // Check with the window manager to make sure this client actually - // has a window with focus. If not, reject. This is thread safe - // because if the focus changes some time before or after, the - // next client receiving focus that has any interest in input will - // be calling through here after that change happens. - if (DEBUG) { - Slog.w(TAG, "Focus gain on non-focused client " + cs.client - + " (uid=" + cs.uid + " pid=" + cs.pid + ")"); - } - return InputBindResult.NOT_IME_TARGET_WINDOW; - } + @NonNull + private InputBindResult startInputOrWindowGainedFocusInternalLocked( + @StartInputReason int startInputReason, IInputMethodClient client, + @NonNull IBinder windowToken, @StartInputFlags int startInputFlags, + @SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo attribute, + IInputContext inputContext, @MissingMethodFlags int missingMethods, + int unverifiedTargetSdkVersion, @UserIdInt int userId) { + if (DEBUG) { + Slog.v(TAG, "startInputOrWindowGainedFocusInternalLocked: reason=" + + InputMethodDebug.startInputReasonToString(startInputReason) + + " client=" + client.asBinder() + + " inputContext=" + inputContext + + " missingMethods=" + + InputConnectionInspector.getMissingMethodFlagsAsString(missingMethods) + + " attribute=" + attribute + + " startInputFlags=" + + InputMethodDebug.startInputFlagsToString(startInputFlags) + + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode) + + " windowFlags=#" + Integer.toHexString(windowFlags) + + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion); + } - // cross-profile access is always allowed here to allow profile-switching. - if (!mSettings.isCurrentProfile(userId)) { - Slog.w(TAG, "A background user is requesting window. Hiding IME."); - Slog.w(TAG, "If you need to impersonate a foreground user/profile from" - + " a background user, use EditorInfo.targetInputMethodUser with" - + " INTERACT_ACROSS_USERS_FULL permission."); - hideCurrentInputLocked(0, null); - return InputBindResult.INVALID_USER; - } + final int windowDisplayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken); - if (PER_PROFILE_IME_ENABLED && userId != mSettings.getCurrentUserId()) { - switchUserLocked(userId); - } - // Master feature flag that overrides other conditions and forces IME preRendering. - if (DEBUG) { - Slog.v(TAG, "IME PreRendering MASTER flag: " - + DebugFlags.FLAG_PRE_RENDER_IME_VIEWS.value() - + ", LowRam: " + mIsLowRam); - } - // pre-rendering not supported on low-ram devices. - cs.shouldPreRenderIme = DebugFlags.FLAG_PRE_RENDER_IME_VIEWS.value() && !mIsLowRam; + final ClientState cs = mClients.get(client.asBinder()); + if (cs == null) { + throw new IllegalArgumentException("unknown client " + client.asBinder()); + } + if (cs.selfReportedDisplayId != windowDisplayId) { + Slog.e(TAG, "startInputOrWindowGainedFocusInternal: display ID mismatch." + + " from client:" + cs.selfReportedDisplayId + + " from window:" + windowDisplayId); + return InputBindResult.DISPLAY_ID_MISMATCH; + } - if (mCurFocusedWindow == windowToken) { - if (DEBUG) { - Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client - + " attribute=" + attribute + ", token = " + windowToken); + if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, + cs.selfReportedDisplayId)) { + // Check with the window manager to make sure this client actually + // has a window with focus. If not, reject. This is thread safe + // because if the focus changes some time before or after, the + // next client receiving focus that has any interest in input will + // be calling through here after that change happens. + if (DEBUG) { + Slog.w(TAG, "Focus gain on non-focused client " + cs.client + + " (uid=" + cs.uid + " pid=" + cs.pid + ")"); + } + return InputBindResult.NOT_IME_TARGET_WINDOW; + } + + // cross-profile access is always allowed here to allow profile-switching. + if (!mSettings.isCurrentProfile(userId)) { + Slog.w(TAG, "A background user is requesting window. Hiding IME."); + Slog.w(TAG, "If you need to impersonate a foreground user/profile from" + + " a background user, use EditorInfo.targetInputMethodUser with" + + " INTERACT_ACROSS_USERS_FULL permission."); + hideCurrentInputLocked(0, null); + return InputBindResult.INVALID_USER; + } + + if (PER_PROFILE_IME_ENABLED && userId != mSettings.getCurrentUserId()) { + switchUserLocked(userId); + } + // Master feature flag that overrides other conditions and forces IME preRendering. + if (DEBUG) { + Slog.v(TAG, "IME PreRendering MASTER flag: " + + DebugFlags.FLAG_PRE_RENDER_IME_VIEWS.value() + ", LowRam: " + mIsLowRam); + } + // pre-rendering not supported on low-ram devices. + cs.shouldPreRenderIme = DebugFlags.FLAG_PRE_RENDER_IME_VIEWS.value() && !mIsLowRam; + + if (mCurFocusedWindow == windowToken) { + if (DEBUG) { + Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client + + " attribute=" + attribute + ", token = " + windowToken); + } + if (attribute != null) { + return startInputUncheckedLocked(cs, inputContext, missingMethods, + attribute, startInputFlags, startInputReason); + } + return new InputBindResult( + InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY, + null, null, null, -1); + } + mCurFocusedWindow = windowToken; + mCurFocusedWindowSoftInputMode = softInputMode; + mCurFocusedWindowClient = cs; + + // Should we auto-show the IME even if the caller has not + // specified what should be done with it? + // We only do this automatically if the window can resize + // to accommodate the IME (so what the user sees will give + // them good context without input information being obscured + // by the IME) or if running on a large screen where there + // is more room for the target window + IME. + final boolean doAutoShow = + (softInputMode & LayoutParams.SOFT_INPUT_MASK_ADJUST) + == LayoutParams.SOFT_INPUT_ADJUST_RESIZE + || mRes.getConfiguration().isLayoutSizeAtLeast( + Configuration.SCREENLAYOUT_SIZE_LARGE); + final boolean isTextEditor = (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0; + + // We want to start input before showing the IME, but after closing + // it. We want to do this after closing it to help the IME disappear + // more quickly (not get stuck behind it initializing itself for the + // new focused input, even if its window wants to hide the IME). + boolean didStart = false; + + InputBindResult res = null; + switch (softInputMode & LayoutParams.SOFT_INPUT_MASK_STATE) { + case LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: + if (!isTextEditor || !doAutoShow) { + if (LayoutParams.mayUseInputMethod(windowFlags)) { + // There is no focus view, and this window will + // be behind any soft input window, so hide the + // soft input window if it is shown. + if (DEBUG) Slog.v(TAG, "Unspecified window will hide input"); + hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null); + + // If focused display changed, we should unbind current method + // to make app window in previous display relayout after Ime + // window token removed. + // Note that we can trust client's display ID as long as it matches + // to the display ID obtained from the window. + if (cs.selfReportedDisplayId != mCurTokenDisplayId) { + unbindCurrentMethodLocked(); + } } + } else if (isTextEditor && doAutoShow + && (softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { + // There is a focus view, and we are navigating forward + // into the window, so show the input window for the user. + // We only do this automatically if the window can resize + // to accommodate the IME (so what the user sees will give + // them good context without input information being obscured + // by the IME) or if running on a large screen where there + // is more room for the target window + IME. + if (DEBUG) Slog.v(TAG, "Unspecified window will show input"); if (attribute != null) { - return startInputUncheckedLocked(cs, inputContext, missingMethods, + res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute, startInputFlags, startInputReason); + didStart = true; } - return new InputBindResult( - InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY, - null, null, null, -1); + showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); } - mCurFocusedWindow = windowToken; - mCurFocusedWindowSoftInputMode = softInputMode; - mCurFocusedWindowClient = cs; - - // Should we auto-show the IME even if the caller has not - // specified what should be done with it? - // We only do this automatically if the window can resize - // to accommodate the IME (so what the user sees will give - // them good context without input information being obscured - // by the IME) or if running on a large screen where there - // is more room for the target window + IME. - final boolean doAutoShow = - (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) - == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE - || mRes.getConfiguration().isLayoutSizeAtLeast( - Configuration.SCREENLAYOUT_SIZE_LARGE); - final boolean isTextEditor = - (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0; - - // We want to start input before showing the IME, but after closing - // it. We want to do this after closing it to help the IME disappear - // more quickly (not get stuck behind it initializing itself for the - // new focused input, even if its window wants to hide the IME). - boolean didStart = false; - - switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) { - case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: - if (!isTextEditor || !doAutoShow) { - if (WindowManager.LayoutParams.mayUseInputMethod(windowFlags)) { - // There is no focus view, and this window will - // be behind any soft input window, so hide the - // soft input window if it is shown. - if (DEBUG) Slog.v(TAG, "Unspecified window will hide input"); - hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null); - - // If focused display changed, we should unbind current method - // to make app window in previous display relayout after Ime - // window token removed. - // Note that we can trust client's display ID as long as it matches - // to the display ID obtained from the window. - if (cs.selfReportedDisplayId != mCurTokenDisplayId) { - unbindCurrentMethodLocked(); - } - } - } else if (isTextEditor && doAutoShow && (softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { - // There is a focus view, and we are navigating forward - // into the window, so show the input window for the user. - // We only do this automatically if the window can resize - // to accommodate the IME (so what the user sees will give - // them good context without input information being obscured - // by the IME) or if running on a large screen where there - // is more room for the target window + IME. - if (DEBUG) Slog.v(TAG, "Unspecified window will show input"); - if (attribute != null) { - res = startInputUncheckedLocked(cs, inputContext, missingMethods, - attribute, startInputFlags, startInputReason); - didStart = true; - } - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); - } - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED: - // Do nothing. - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN: - if ((softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { - if (DEBUG) Slog.v(TAG, "Window asks to hide input going forward"); - hideCurrentInputLocked(0, null); - } - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN: - if (DEBUG) Slog.v(TAG, "Window asks to hide input"); - hideCurrentInputLocked(0, null); - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE: - if ((softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { - if (DEBUG) Slog.v(TAG, "Window asks to show input going forward"); - if (InputMethodUtils.isSoftInputModeStateVisibleAllowed( - unverifiedTargetSdkVersion, startInputFlags)) { - if (attribute != null) { - res = startInputUncheckedLocked(cs, inputContext, - missingMethods, attribute, startInputFlags, - startInputReason); - didStart = true; - } - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); - } else { - Slog.e(TAG, "SOFT_INPUT_STATE_VISIBLE is ignored because" - + " there is no focused view that also returns true from" - + " View#onCheckIsTextEditor()"); - } - } - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE: - if (DEBUG) Slog.v(TAG, "Window asks to always show input"); - if (InputMethodUtils.isSoftInputModeStateVisibleAllowed( - unverifiedTargetSdkVersion, startInputFlags)) { - if (attribute != null) { - res = startInputUncheckedLocked(cs, inputContext, missingMethods, - attribute, startInputFlags, startInputReason); - didStart = true; - } - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); - } else { - Slog.e(TAG, "SOFT_INPUT_STATE_ALWAYS_VISIBLE is ignored because" - + " there is no focused view that also returns true from" - + " View#onCheckIsTextEditor()"); - } - break; + break; + case LayoutParams.SOFT_INPUT_STATE_UNCHANGED: + // Do nothing. + break; + case LayoutParams.SOFT_INPUT_STATE_HIDDEN: + if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { + if (DEBUG) Slog.v(TAG, "Window asks to hide input going forward"); + hideCurrentInputLocked(0, null); } - - if (!didStart) { - if (attribute != null) { - if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value() - || (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) { + break; + case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN: + if (DEBUG) Slog.v(TAG, "Window asks to hide input"); + hideCurrentInputLocked(0, null); + break; + case LayoutParams.SOFT_INPUT_STATE_VISIBLE: + if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { + if (DEBUG) Slog.v(TAG, "Window asks to show input going forward"); + if (InputMethodUtils.isSoftInputModeStateVisibleAllowed( + unverifiedTargetSdkVersion, startInputFlags)) { + if (attribute != null) { res = startInputUncheckedLocked(cs, inputContext, missingMethods, - attribute, - startInputFlags, startInputReason); - } else { - res = InputBindResult.NO_EDITOR; + attribute, startInputFlags, startInputReason); + didStart = true; } + showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); } else { - res = InputBindResult.NULL_EDITOR_INFO; + Slog.e(TAG, "SOFT_INPUT_STATE_VISIBLE is ignored because" + + " there is no focused view that also returns true from" + + " View#onCheckIsTextEditor()"); } } - } finally { - Binder.restoreCallingIdentity(ident); - } + break; + case LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE: + if (DEBUG) Slog.v(TAG, "Window asks to always show input"); + if (InputMethodUtils.isSoftInputModeStateVisibleAllowed( + unverifiedTargetSdkVersion, startInputFlags)) { + if (attribute != null) { + res = startInputUncheckedLocked(cs, inputContext, missingMethods, + attribute, startInputFlags, startInputReason); + didStart = true; + } + showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); + } else { + Slog.e(TAG, "SOFT_INPUT_STATE_ALWAYS_VISIBLE is ignored because" + + " there is no focused view that also returns true from" + + " View#onCheckIsTextEditor()"); + } + break; } + if (!didStart) { + if (attribute != null) { + if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value() + || (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) { + res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute, + startInputFlags, startInputReason); + } else { + res = InputBindResult.NO_EDITOR; + } + } else { + res = InputBindResult.NULL_EDITOR_INFO; + } + } return res; } @@ -3327,6 +3322,32 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } + @BinderThread + private void reportPreRendered(IBinder token, EditorInfo info) { + synchronized (mMethodMap) { + if (!calledWithValidTokenLocked(token)) { + return; + } + if (mCurClient != null && mCurClient.client != null) { + executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO( + MSG_REPORT_PRE_RENDERED, info, mCurClient)); + } + } + } + + @BinderThread + private void applyImeVisibility(IBinder token, boolean setVisible) { + synchronized (mMethodMap) { + if (!calledWithValidTokenLocked(token)) { + return; + } + if (mCurClient != null && mCurClient.client != null) { + executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( + MSG_APPLY_IME_VISIBILITY, setVisible ? 1 : 0, mCurClient)); + } + } + } + private void setInputMethodWithSubtypeIdLocked(IBinder token, String id, int subtypeId) { if (token == null) { if (mContext.checkCallingOrSelfPermission( @@ -3580,6 +3601,32 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } return true; } + case MSG_REPORT_PRE_RENDERED: { + args = (SomeArgs) msg.obj; + final EditorInfo info = (EditorInfo) args.arg1; + final ClientState clientState = (ClientState) args.arg2; + try { + clientState.client.reportPreRendered(info); + } catch (RemoteException e) { + Slog.w(TAG, "Got RemoteException sending " + + "reportPreRendered(" + info + ") notification to pid=" + + clientState.pid + " uid=" + clientState.uid); + } + args.recycle(); + return true; + } + case MSG_APPLY_IME_VISIBILITY: { + final boolean setVisible = msg.arg1 != 0; + final ClientState clientState = (ClientState) msg.obj; + try { + clientState.client.applyImeVisibility(setVisible); + } catch (RemoteException e) { + Slog.w(TAG, "Got RemoteException sending " + + "applyImeVisibility(" + setVisible + ") notification to pid=" + + clientState.pid + " uid=" + clientState.uid); + } + return true; + } // -------------------------------------------------------------- case MSG_HARD_KEYBOARD_SWITCH_CHANGED: @@ -3921,13 +3968,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mSwitchingDialog = mDialogBuilder.create(); mSwitchingDialog.setCanceledOnTouchOutside(true); final Window w = mSwitchingDialog.getWindow(); - final WindowManager.LayoutParams attrs = w.getAttributes(); - w.setType(TYPE_INPUT_METHOD_DIALOG); + final LayoutParams attrs = w.getAttributes(); + w.setType(LayoutParams.TYPE_INPUT_METHOD_DIALOG); // Use an alternate token for the dialog for that window manager can group the token // with other IME windows based on type vs. grouping based on whichever token happens // to get selected by the system later on. attrs.token = mSwitchingDialogToken; - attrs.privateFlags |= PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + attrs.privateFlags |= LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; attrs.setTitle("Select input method"); w.setAttributes(attrs); updateSystemUiLocked(mImeWindowVis, mBackDisposition); @@ -4756,5 +4803,17 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public void notifyUserAction() { mImms.notifyUserAction(mToken); } + + @BinderThread + @Override + public void reportPreRendered(EditorInfo info) { + mImms.reportPreRendered(mToken, info); + } + + @BinderThread + @Override + public void applyImeVisibility(boolean setVisible) { + mImms.applyImeVisibility(mToken, setVisible); + } } } diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 7625aafd0907..65a078b9691a 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -508,7 +508,7 @@ public class JobSchedulerService extends com.android.server.SystemService private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f; private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f; - private static final boolean DEFAULT_USE_HEARTBEATS = true; + private static final boolean DEFAULT_USE_HEARTBEATS = false; private static final boolean DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false; private static final long DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = 10 * 60 * 1000L; // 10 minutes @@ -2040,6 +2040,7 @@ public class JobSchedulerService extends com.android.server.SystemService if (DEBUG) { Slog.d(TAG, "MSG_CHECK_JOB"); } + removeMessages(MSG_CHECK_JOB); if (mReportedActive) { // if jobs are currently being run, queue all ready jobs for execution. queueReadyJobsForExecutionLocked(); @@ -2099,7 +2100,6 @@ public class JobSchedulerService extends com.android.server.SystemService } maybeRunPendingJobsLocked(); // Don't remove JOB_EXPIRED in case one came along while processing the queue. - removeMessages(MSG_CHECK_JOB); } } } diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index 82bfa511507f..ed1a6d0beabf 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -33,6 +33,7 @@ import android.text.format.Time; import android.util.ArraySet; import android.util.Pair; import android.util.Slog; +import android.util.StatsLog; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; @@ -40,6 +41,7 @@ import com.android.server.LocalServices; import com.android.server.job.GrantedUriPermissions; import com.android.server.job.JobSchedulerInternal; import com.android.server.job.JobSchedulerService; +import com.android.server.job.JobServerProtoEnums; import com.android.server.job.JobStatusDumpProto; import com.android.server.job.JobStatusShortInfoProto; @@ -80,6 +82,28 @@ public final class JobStatus { static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24; static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1<<22; + /** + * The constraints that we want to log to statsd. + * + * Constraints that can be inferred from other atoms have been excluded to avoid logging too + * much information and to reduce redundancy: + * + * * CONSTRAINT_CHARGING can be inferred with PluggedStateChanged (Atom #32) + * * CONSTRAINT_BATTERY_NOT_LOW can be inferred with BatteryLevelChanged (Atom #30) + * * CONSTRAINT_CONNECTIVITY can be partially inferred with ConnectivityStateChanged + * (Atom #98) and BatterySaverModeStateChanged (Atom #20). + * * CONSTRAINT_DEVICE_NOT_DOZING can be mostly inferred with DeviceIdleModeStateChanged + * (Atom #21) + * * CONSTRAINT_BACKGROUND_NOT_RESTRICTED can be inferred with BatterySaverModeStateChanged + * (Atom #20) + */ + private static final int STATSD_CONSTRAINTS_TO_LOG = CONSTRAINT_CONTENT_TRIGGER + | CONSTRAINT_DEADLINE + | CONSTRAINT_IDLE + | CONSTRAINT_STORAGE_NOT_LOW + | CONSTRAINT_TIMING_DELAY + | CONSTRAINT_WITHIN_QUOTA; + // Soft override: ignore constraints like time that don't affect API availability public static final int OVERRIDE_SOFT = 1; // Full override: ignore all constraints including API-affecting like connectivity @@ -976,6 +1000,12 @@ public final class JobStatus { } satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0); mSatisfiedConstraintsOfInterest = satisfiedConstraints & CONSTRAINTS_OF_INTEREST; + if ((STATSD_CONSTRAINTS_TO_LOG & constraint) != 0) { + StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED, + sourceUid, null, getBatteryName(), getProtoConstraint(constraint), + state ? StatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__SATISFIED + : StatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__UNSATISFIED); + } return true; } @@ -1228,37 +1258,70 @@ public final class JobStatus { } } + /** Returns a {@link JobServerProtoEnums.Constraint} enum value for the given constraint. */ + private int getProtoConstraint(int constraint) { + switch (constraint) { + case CONSTRAINT_BACKGROUND_NOT_RESTRICTED: + return JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED; + case CONSTRAINT_BATTERY_NOT_LOW: + return JobServerProtoEnums.CONSTRAINT_BATTERY_NOT_LOW; + case CONSTRAINT_CHARGING: + return JobServerProtoEnums.CONSTRAINT_CHARGING; + case CONSTRAINT_CONNECTIVITY: + return JobServerProtoEnums.CONSTRAINT_CONNECTIVITY; + case CONSTRAINT_CONTENT_TRIGGER: + return JobServerProtoEnums.CONSTRAINT_CONTENT_TRIGGER; + case CONSTRAINT_DEADLINE: + return JobServerProtoEnums.CONSTRAINT_DEADLINE; + case CONSTRAINT_DEVICE_NOT_DOZING: + return JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING; + case CONSTRAINT_IDLE: + return JobServerProtoEnums.CONSTRAINT_IDLE; + case CONSTRAINT_STORAGE_NOT_LOW: + return JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW; + case CONSTRAINT_TIMING_DELAY: + return JobServerProtoEnums.CONSTRAINT_TIMING_DELAY; + case CONSTRAINT_WITHIN_QUOTA: + return JobServerProtoEnums.CONSTRAINT_WITHIN_QUOTA; + default: + return JobServerProtoEnums.CONSTRAINT_UNKNOWN; + } + } + /** Writes constraints to the given repeating proto field. */ void dumpConstraints(ProtoOutputStream proto, long fieldId, int constraints) { if ((constraints & CONSTRAINT_CHARGING) != 0) { - proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_CHARGING); + proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CHARGING); } if ((constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0) { - proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_BATTERY_NOT_LOW); + proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BATTERY_NOT_LOW); } if ((constraints & CONSTRAINT_STORAGE_NOT_LOW) != 0) { - proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_STORAGE_NOT_LOW); + proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW); } if ((constraints & CONSTRAINT_TIMING_DELAY) != 0) { - proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_TIMING_DELAY); + proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_TIMING_DELAY); } if ((constraints & CONSTRAINT_DEADLINE) != 0) { - proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_DEADLINE); + proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_DEADLINE); } if ((constraints & CONSTRAINT_IDLE) != 0) { - proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_IDLE); + proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_IDLE); } if ((constraints & CONSTRAINT_CONNECTIVITY) != 0) { - proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_CONNECTIVITY); + proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CONNECTIVITY); } if ((constraints & CONSTRAINT_CONTENT_TRIGGER) != 0) { - proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_CONTENT_TRIGGER); + proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CONTENT_TRIGGER); } if ((constraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0) { - proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_DEVICE_NOT_DOZING); + proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING); } if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) { - proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_WITHIN_QUOTA); + proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_WITHIN_QUOTA); + } + if ((constraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) { + proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED); } } diff --git a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java index dd26a29d55af..94de49e72937 100644 --- a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java @@ -43,7 +43,6 @@ import android.media.AudioSystem; import android.media.IAudioService; import android.media.IRemoteVolumeController; import android.media.Session2Token; -import android.media.session.ControllerLink; import android.media.session.IActiveSessionsListener; import android.media.session.ICallback; import android.media.session.IOnMediaKeyListener; @@ -1054,21 +1053,21 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { } @Override - public List<ControllerLink> getSessions(ComponentName componentName, int userId) { + public List<MediaSession.Token> getSessions(ComponentName componentName, int userId) { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid); - ArrayList<ControllerLink> binders = new ArrayList<>(); + ArrayList<MediaSession.Token> tokens = new ArrayList<>(); synchronized (mLock) { List<MediaSessionRecord> records = getActiveSessionsLocked(resolvedUserId); for (MediaSessionRecord record : records) { - binders.add(record.getControllerLink()); + tokens.add(record.getSessionToken()); } } - return binders; + return tokens; } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index de3f50ad8c2c..1369f7b226e2 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -121,6 +121,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.content.res.Resources; @@ -2339,18 +2340,24 @@ public class NotificationManagerService extends SystemService { public boolean areBubblesAllowedForPackage(String pkg, int uid) { enforceSystemOrSystemUIOrSamePackage(pkg, "Caller not system or systemui or same package"); - return mPreferencesHelper.areBubblessAllowed(pkg, uid); + return mPreferencesHelper.areBubblesAllowed(pkg, uid); } @Override public void setBubblesAllowed(String pkg, int uid, boolean allowed) { - checkCallerIsSystem(); - + enforceSystemOrSystemUI("Caller not system or systemui"); mPreferencesHelper.setBubblesAllowed(pkg, uid, allowed); handleSavePolicyFile(); } @Override + public boolean hasUserApprovedBubblesForPackage(String pkg, int uid) { + enforceSystemOrSystemUI("Caller not system or systemui"); + int lockedFields = mPreferencesHelper.getAppLockedFields(pkg, uid); + return (lockedFields & PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE) != 0; + } + + @Override public int getPackageImportance(String pkg) { checkCallerIsSystemOrSameApp(pkg); return mPreferencesHelper.getImportance(pkg, Binder.getCallingUid()); @@ -3859,6 +3866,24 @@ public class NotificationManagerService extends SystemService { return mLockScreenAllowSecureNotifications; } + @Override + public boolean isPackagePaused(String pkg) { + Preconditions.checkNotNull(pkg); + checkCallerIsSameApp(pkg); + + boolean isPaused; + + final PackageManagerInternal pmi = LocalServices.getService( + PackageManagerInternal.class); + int flags = pmi.getDistractingPackageRestrictions( + pkg, Binder.getCallingUserHandle().getIdentifier()); + isPaused = ((flags & PackageManager.RESTRICTION_HIDE_NOTIFICATIONS) != 0); + + isPaused |= isPackageSuspendedForUser(pkg, Binder.getCallingUid()); + + return isPaused; + } + private void verifyPrivilegedListener(INotificationListener token, UserHandle user, boolean assistantAllowed) { ManagedServiceInfo info; diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 7a21aa208dfd..3f0043cecc49 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -473,7 +473,7 @@ public class PreferencesHelper implements RankingConfig { * @param uid the uid to check if bubbles are allowed for. * @return whether bubbles are allowed. */ - public boolean areBubblessAllowed(String pkg, int uid) { + public boolean areBubblesAllowed(String pkg, int uid) { return getOrCreatePackagePreferences(pkg, uid).allowBubble; } @@ -489,7 +489,6 @@ public class PreferencesHelper implements RankingConfig { return getOrCreatePackagePreferences(packageName, uid).importance; } - /** * Returns whether the importance of the corresponding notification is user-locked and shouldn't * be adjusted by an assistant (via means of a blocking helper, for example). For the channel diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index d0ef4f1523d4..d5089cb41dd4 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -696,7 +696,7 @@ public class LauncherAppsService extends SystemService { return null; } return new LauncherApps.AppUsageLimit( - data.isGroupLimit(), data.getTotalUsageLimit(), data.getUsageRemaining()); + data.getTotalUsageLimit(), data.getUsageRemaining()); } private void ensureShortcutPermission(@NonNull String callingPackage) { diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index eab5c8f866a8..146a2f3d8433 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -28,8 +28,10 @@ import android.app.NotificationManager; import android.app.PackageDeleteObserver; import android.app.PackageInstallObserver; import android.app.admin.DevicePolicyManagerInternal; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.IntentSender; import android.content.IntentSender.SendIntentException; import android.content.pm.ApplicationInfo; @@ -138,6 +140,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements private final Callbacks mCallbacks; + private volatile boolean mBootCompleted = false; + /** * File storing persisted {@link #mSessions} metadata. */ @@ -203,9 +207,20 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mStagingManager = new StagingManager(pm); } + private void setBootCompleted() { + mBootCompleted = true; + } + public void systemReady() { mAppOps = mContext.getSystemService(AppOpsManager.class); + mContext.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + setBootCompleted(); + mContext.unregisterReceiver(this); + } + }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); synchronized (mSessions) { readSessionsLocked(); @@ -1126,8 +1141,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements public void onStagedSessionChanged(PackageInstallerSession session) { writeSessionsAsync(); - // TODO(b/118865310): don't send broadcast if system is not ready. - mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), session.userId); + if (mBootCompleted) { + mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), + session.userId); + } } public void onSessionFinished(final PackageInstallerSession session, boolean success) { diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 792b34c16551..ff6d7a888950 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -2154,6 +2154,8 @@ public class ShortcutService extends IShortcutService.Stub { public ParceledListSlice<ShortcutManager.ShareShortcutInfo> getShareTargets(String packageName, IntentFilter filter, @UserIdInt int userId) { verifyCaller(packageName, userId); + enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS, + "getShareTargets"); synchronized (mLock) { throwIfUserLockedL(userId); diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index 2455113d8874..8d64b810b407 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -16,10 +16,6 @@ package com.android.server.pm; -import com.google.android.collect.Sets; - -import com.android.internal.util.Preconditions; - import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -42,6 +38,10 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import com.android.internal.util.Preconditions; + +import com.google.android.collect.Sets; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; @@ -666,6 +666,7 @@ public class UserRestrictionsUtils { case android.provider.Settings.Secure.ALWAYS_ON_VPN_APP: case android.provider.Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN: + case android.provider.Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST: // Whitelist system uid (ConnectivityService) and root uid to change always-on vpn final int appId = UserHandle.getAppId(callingUid); if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID) { diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index 863bfd5ea391..a8be07d76b58 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -480,6 +480,10 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { final String apkPath = pkg.baseCodePath; final ApplicationInfo appInfo = pkg.applicationInfo; final String outDexFile = appInfo.dataDir + "/code_cache/compiled_view.dex"; + if (appInfo.isPrivilegedApp()) { + // Privileged apps prefer to load trusted code so they don't use compiled views. + return false; + } Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath + ") to " + outDexFile); long callingId = Binder.clearCallingIdentity(); diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 30b5e49bc3fd..3c89d7801852 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -1049,6 +1049,8 @@ public class PermissionManagerService { updatedUserIds); updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origPermissions, permissionsState, pkg, updatedUserIds); + + setAppOpsLocked(permissionsState, pkg); } // Persist the runtime permissions state for users with changes. If permissions @@ -1387,6 +1389,60 @@ public class PermissionManagerService { return updatedUserIds; } + /** + * Fix app-op modes for runtime permissions. + * + * @param permsState The state of the permissions of the package + * @param pkg The package information + */ + private void setAppOpsLocked(@NonNull PermissionsState permsState, + @NonNull PackageParser.Package pkg) { + for (int userId : UserManagerService.getInstance().getUserIds()) { + int numPerms = pkg.requestedPermissions.size(); + for (int i = 0; i < numPerms; i++) { + String permission = pkg.requestedPermissions.get(i); + + int op = permissionToOpCode(permission); + if (op == OP_NONE) { + continue; + } + + // Runtime permissions are per uid, not per package, hence per package app-op + // modes should never have been set. It is possible to set them via the shell + // though. Revert such settings during boot to get the device back into a good + // state. + LocalServices.getService(AppOpsManagerInternal.class).setAllPkgModesToDefault( + op, getUid(userId, getAppId(pkg.applicationInfo.uid))); + + // For pre-M apps the runtime permission do not store the state + if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { + continue; + } + + PermissionState state = permsState.getRuntimePermissionState(permission, userId); + if (state == null) { + continue; + } + + // Adjust app-op mods for foreground/background permissions. If an package used to + // have both fg and bg permission granted and it lost the bg permission during an + // upgrade the app-op mode should get downgraded to foreground. + if (state.isGranted()) { + BasePermission bp = mSettings.getPermission(permission); + + if (bp != null && bp.perm != null && bp.perm.info != null + && bp.perm.info.backgroundPermission != null) { + PermissionState bgState = permsState.getRuntimePermissionState( + bp.perm.info.backgroundPermission, userId); + + setAppOpMode(permission, pkg, userId, bgState != null && bgState.isGranted() + ? MODE_ALLOWED : MODE_FOREGROUND); + } + } + } + } + } + private boolean isNewPlatformPermissionForPackage(String perm, PackageParser.Package pkg) { boolean allowed = false; final int NP = PackageParser.NEW_PERMISSIONS.length; diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING index 076c94c994a5..09bacd6ed332 100644 --- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING @@ -7,6 +7,17 @@ "include-filter": "com.google.android.permission.gts.DefaultPermissionGrantPolicyTest" } ] + }, + { + "name": "CtsPermissionTestCases", + "options": [ + { + "include-filter": "android.permission.cts.BackgroundPermissionsTest" + }, + { + "include-filter": "android.permission.cts.SplitPermissionTest" + } + ] } ] }
\ No newline at end of file diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java new file mode 100644 index 000000000000..fdcafa77a378 --- /dev/null +++ b/services/core/java/com/android/server/policy/DisplayFoldController.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.policy; + +import android.content.Context; +import android.graphics.Rect; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.hardware.display.DisplayManagerInternal; +import android.os.Handler; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.view.DisplayInfo; +import android.view.IDisplayFoldListener; + +import com.android.server.DisplayThread; +import com.android.server.LocalServices; +import com.android.server.wm.WindowManagerInternal; + +/** + * Controls the behavior of foldable devices whose screen can literally bend and fold. + */ +class DisplayFoldController { + + private static final String TAG = "DisplayFoldController"; + private final WindowManagerInternal mWindowManagerInternal; + private final DisplayManagerInternal mDisplayManagerInternal; + private final int mDisplayId; + + /** The display area while device is folded. */ + private final Rect mFoldedArea; + private final Handler mHandler; + + private final DisplayInfo mNonOverrideDisplayInfo = new DisplayInfo(); + private final RemoteCallbackList<IDisplayFoldListener> mListeners = new RemoteCallbackList<>(); + private Boolean mFolded; + + DisplayFoldController(WindowManagerInternal windowManagerInternal, + DisplayManagerInternal displayManagerInternal, int displayId, Rect foldedArea, + Handler handler) { + mWindowManagerInternal = windowManagerInternal; + mDisplayManagerInternal = displayManagerInternal; + mDisplayId = displayId; + mFoldedArea = new Rect(foldedArea); + mHandler = handler; + } + + void requestDeviceFolded(boolean folded) { + mHandler.post(() -> setDeviceFolded(folded)); + } + + void setDeviceFolded(boolean folded) { + if (mFolded != null && mFolded == folded) { + return; + } + if (folded) { + mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mNonOverrideDisplayInfo); + final int dx = (mNonOverrideDisplayInfo.logicalWidth - mFoldedArea.width()) / 2 + - mFoldedArea.left; + final int dy = (mNonOverrideDisplayInfo.logicalHeight - mFoldedArea.height()) / 2 + - mFoldedArea.top; + + mWindowManagerInternal.setForcedDisplaySize(mDisplayId, mFoldedArea.width(), + mFoldedArea.height()); + mDisplayManagerInternal.setDisplayOffsets(mDisplayId, -dx, -dy); + } else { + mWindowManagerInternal.clearForcedDisplaySize(mDisplayId); + mDisplayManagerInternal.setDisplayOffsets(mDisplayId, 0, 0); + } + mFolded = folded; + + final int n = mListeners.beginBroadcast(); + for (int i = 0; i < n; i++) { + try { + mListeners.getBroadcastItem(i).onDisplayFoldChanged(mDisplayId, folded); + } catch (RemoteException e) { + // Listener died. + } + } + mListeners.finishBroadcast(); + } + + void registerDisplayFoldListener(IDisplayFoldListener listener) { + mListeners.register(listener); + if (mFolded == null) { + return; + } + mHandler.post(() -> { + try { + listener.onDisplayFoldChanged(mDisplayId, mFolded); + } catch (RemoteException e) { + // Listener died. + } + }); + } + + void unregisterDisplayFoldListener(IDisplayFoldListener listener) { + mListeners.unregister(listener); + } + + /** + * Only used for the case that persist.debug.force_foldable is set. + * This is using proximity sensor to simulate the fold state switch. + */ + static DisplayFoldController createWithProxSensor(Context context, int displayId) { + final SensorManager sensorManager = context.getSystemService(SensorManager.class); + final Sensor proxSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); + if (proxSensor == null) { + return null; + } + + final DisplayFoldController result = create(displayId); + sensorManager.registerListener(new SensorEventListener() { + @Override + public void onSensorChanged(SensorEvent event) { + result.requestDeviceFolded(event.values[0] < 1f); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // Ignore. + } + }, proxSensor, SensorManager.SENSOR_DELAY_NORMAL); + + return result; + } + + static DisplayFoldController create(int displayId) { + final DisplayManagerInternal displayService = + LocalServices.getService(DisplayManagerInternal.class); + final DisplayInfo displayInfo = new DisplayInfo(); + displayService.getNonOverrideDisplayInfo(displayId, displayInfo); + final Rect foldedArea = new Rect(0, displayInfo.logicalHeight / 2, + displayInfo.logicalWidth, displayInfo.logicalHeight); + + return new DisplayFoldController(LocalServices.getService(WindowManagerInternal.class), + displayService, displayId, foldedArea, DisplayThread.getHandler()); + } +} diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 13c4d886e7b1..0796a9c896a3 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -177,6 +177,7 @@ import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import android.view.Display; import android.view.HapticFeedbackConstants; +import android.view.IDisplayFoldListener; import android.view.IWindowManager; import android.view.InputDevice; import android.view.KeyCharacterMap; @@ -374,6 +375,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { SearchManager mSearchManager; AccessibilityManager mAccessibilityManager; BurnInProtectionHelper mBurnInProtectionHelper; + private DisplayFoldController mDisplayFoldController; AppOpsManager mAppOpsManager; private ScreenshotHelper mScreenshotHelper; private boolean mHasFeatureWatch; @@ -471,6 +473,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mLidNavigationAccessibility; boolean mLidControlsScreenLock; boolean mLidControlsSleep; + private boolean mLidControlsDisplayFold; int mShortPressOnPowerBehavior; int mLongPressOnPowerBehavior; int mVeryLongPressOnPowerBehavior; @@ -1794,6 +1797,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.bool.config_lidControlsScreenLock); mLidControlsSleep = mContext.getResources().getBoolean( com.android.internal.R.bool.config_lidControlsSleep); + mLidControlsDisplayFold = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_lidControlsDisplayFold); mAllowTheaterModeWakeFromKey = mContext.getResources().getBoolean( com.android.internal.R.bool.config_allowTheaterModeWakeFromKey); @@ -1850,6 +1855,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { readConfigurationDependentBehaviors(); + if (mLidControlsDisplayFold) { + mDisplayFoldController = DisplayFoldController.create(DEFAULT_DISPLAY); + } else if (SystemProperties.getBoolean("persist.debug.force_foldable", false)) { + mDisplayFoldController = DisplayFoldController.createWithProxSensor(context, + DEFAULT_DISPLAY); + } + mAccessibilityManager = (AccessibilityManager) context.getSystemService( Context.ACCESSIBILITY_SERVICE); @@ -3194,6 +3206,20 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override + public void registerDisplayFoldListener(IDisplayFoldListener listener) { + if (mDisplayFoldController != null) { + mDisplayFoldController.registerDisplayFoldListener(listener); + } + } + + @Override + public void unregisterDisplayFoldListener(IDisplayFoldListener listener) { + if (mDisplayFoldController != null) { + mDisplayFoldController.unregisterDisplayFoldListener(listener); + } + } + + @Override public void registerShortcutKey(long shortcutCode, IShortcutService shortcutService) throws RemoteException { synchronized (mLock) { @@ -4972,7 +4998,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { private void applyLidSwitchState() { final int lidState = mDefaultDisplayPolicy.getLidState(); - if (lidState == LID_CLOSED && mLidControlsSleep) { + if (mLidControlsDisplayFold && mDisplayFoldController != null) { + mDisplayFoldController.requestDeviceFolded(lidState == LID_CLOSED); + } else if (lidState == LID_CLOSED && mLidControlsSleep) { goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); } else if (lidState == LID_CLOSED && mLidControlsScreenLock) { diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 1d829707f180..e18cd179b113 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -78,6 +78,7 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.Display; import android.view.IApplicationToken; +import android.view.IDisplayFoldListener; import android.view.IWindowManager; import android.view.InputEventReceiver; import android.view.KeyEvent; @@ -1457,6 +1458,16 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public void requestUserActivityNotification(); /** + * Registers an IDisplayFoldListener. + */ + default void registerDisplayFoldListener(IDisplayFoldListener listener) {} + + /** + * Unregisters an IDisplayFoldListener. + */ + default void unregisterDisplayFoldListener(IDisplayFoldListener listener) {} + + /** * Updates the flag about whether AOD is showing. * * @return whether the value was changed. diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 102318262798..2d89bc7c5613 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -544,6 +544,16 @@ class ActivityMetricsLogger { mHandler.obtainMessage(MSG_CHECK_VISIBILITY, args).sendToTarget(); } + private boolean hasVisibleNonFinishingActivity(TaskRecord t) { + for (int i = t.mActivities.size() - 1; i >= 0; --i) { + final ActivityRecord r = t.mActivities.get(i); + if (r.visible && !r.finishing) { + return true; + } + } + return false; + } + private void checkVisibility(TaskRecord t, ActivityRecord r) { synchronized (mSupervisor.mService.mGlobalLock) { @@ -552,7 +562,7 @@ class ActivityMetricsLogger { // If we have an active transition that's waiting on a certain activity that will be // invisible now, we'll never get onWindowsDrawn, so abort the transition if necessary. - if (info != null && !t.isVisible()) { + if (info != null && !hasVisibleNonFinishingActivity(t)) { if (DEBUG_METRICS) Slog.i(TAG, "notifyVisibilityChanged to invisible" + " activity=" + r); logAppTransitionCancel(info); @@ -841,13 +851,11 @@ class ActivityMetricsLogger { Log.i(TAG, sb.toString()); } - void logActivityStart(Intent intent, WindowProcessController callerApp, ActivityRecord r, + void logAbortedBgActivityStart(Intent intent, WindowProcessController callerApp, int callingUid, String callingPackage, int callingUidProcState, boolean callingUidHasAnyVisibleWindow, int realCallingUid, int realCallingUidProcState, boolean realCallingUidHasAnyVisibleWindow, - int targetUid, String targetPackage, int targetUidProcState, - boolean targetUidHasAnyVisibleWindow, String targetWhitelistTag, boolean comingFromPendingIntent) { final long nowElapsed = SystemClock.elapsedRealtime(); @@ -865,13 +873,6 @@ class ActivityMetricsLogger { processStateAmToProto(realCallingUidProcState)); builder.addTaggedData(FIELD_REAL_CALLING_UID_HAS_ANY_VISIBLE_WINDOW, realCallingUidHasAnyVisibleWindow ? 1 : 0); - builder.addTaggedData(FIELD_TARGET_UID, targetUid); - builder.addTaggedData(FIELD_TARGET_PACKAGE_NAME, targetPackage); - builder.addTaggedData(FIELD_TARGET_UID_PROC_STATE, - processStateAmToProto(targetUidProcState)); - builder.addTaggedData(FIELD_TARGET_UID_HAS_ANY_VISIBLE_WINDOW, - targetUidHasAnyVisibleWindow ? 1 : 0); - builder.addTaggedData(FIELD_TARGET_WHITELIST_TAG, targetWhitelistTag); builder.addTaggedData(FIELD_COMING_FROM_PENDING_INTENT, comingFromPendingIntent ? 1 : 0); if (intent != null) { builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction()); @@ -904,35 +905,6 @@ class ActivityMetricsLogger { (nowUptime - callerApp.getWhenUnimportant())); } } - if (r != null) { - builder.addTaggedData(FIELD_TARGET_SHORT_COMPONENT_NAME, r.shortComponentName); - builder.addTaggedData(FIELD_ACTIVITY_RECORD_LAUNCH_MODE, r.info.launchMode); - builder.addTaggedData(FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY, r.info.targetActivity); - builder.addTaggedData(FIELD_ACTIVITY_RECORD_FLAGS, r.info.flags); - builder.addTaggedData(FIELD_ACTIVITY_RECORD_REAL_ACTIVITY, - r.mActivityComponent.toShortString()); - builder.addTaggedData(FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME, r.shortComponentName); - builder.addTaggedData(FIELD_ACTIVITY_RECORD_PROCESS_NAME, r.processName); - builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_FULLSCREEN, r.fullscreen ? 1 : 0); - builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY, r.noDisplay ? 1 : 0); - if (r.lastVisibleTime != 0) { - builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE, - (nowUptime - r.lastVisibleTime)); - } - if (r.resultTo != null) { - builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME, - r.resultTo.packageName); - builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME, - r.resultTo.shortComponentName); - } - builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE, r.visible ? 1 : 0); - builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD, - r.visibleIgnoringKeyguard ? 1 : 0); - if (r.lastLaunchTime != 0) { - builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH, - (nowUptime - r.lastLaunchTime)); - } - } mMetricsLogger.write(builder); } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b7c35c083174..8f39f3d6e463 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -161,6 +161,7 @@ import android.app.servertransaction.NewIntentItem; import android.app.servertransaction.PauseActivityItem; import android.app.servertransaction.PipModeChangeItem; import android.app.servertransaction.ResumeActivityItem; +import android.app.servertransaction.TopResumedActivityChangeItem; import android.app.servertransaction.WindowVisibilityItem; import android.app.usage.UsageEvents.Event; import android.content.ComponentName; @@ -692,6 +693,26 @@ final class ActivityRecord extends ConfigurationContainer { } } + void scheduleTopResumedActivityChanged(boolean onTop) { + if (!attachedToProcess()) { + if (DEBUG_CONFIGURATION) { + Slog.w(TAG, "Can't report activity position update - client not running" + + ", activityRecord=" + this); + } + return; + } + try { + if (DEBUG_CONFIGURATION) { + Slog.v(TAG, "Sending position change to " + this + ", onTop: " + onTop); + } + + mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, + TopResumedActivityChangeItem.obtain(onTop)); + } catch (RemoteException e) { + // If process died, whatever. + } + } + void updateMultiWindowMode() { if (task == null || task.getStack() == null || !attachedToProcess()) { return; @@ -3099,6 +3120,7 @@ final class ActivityRecord extends ConfigurationContainer { transaction.addCallback(callbackItem); transaction.setLifecycleStateRequest(lifecycleItem); mAtmService.getLifecycleManager().scheduleTransaction(transaction); + mRootActivityContainer.updateTopResumedActivityIfNeeded(); // Note: don't need to call pauseIfSleepingLocked() here, because the caller will only // request resume if this activity is currently resumed, which implies we aren't // sleeping. diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index c97e4e8a9bc0..549567a182bb 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -2524,6 +2524,7 @@ class ActivityStack extends ConfigurationContainer { // Protect against recursion. mInResumeTopActivity = true; result = resumeTopActivityInnerLocked(prev, options); + mRootActivityContainer.updateTopResumedActivityIfNeeded(); // When resuming the top activity, it may be necessary to pause the top activity (for // example, returning to the lock screen. We suppress the normal pause logic in diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index a83ef34f1cac..c8a150beecfa 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -840,6 +840,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // Schedule transaction. mService.getLifecycleManager().scheduleTransaction(clientTransaction); + mRootActivityContainer.updateTopResumedActivityIfNeeded(); if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0 && mService.mHasHeavyWeightFeature) { diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index b0e581134a95..95a6f71fc44b 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -749,9 +749,15 @@ class ActivityStarter { boolean abortBackgroundStart = false; if (!abort) { - abortBackgroundStart = shouldAbortBackgroundActivityStart(callingUid, callingPid, - callingPackage, realCallingUid, callerApp, originatingPendingIntent, - allowBackgroundActivityStart, intent); + try { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, + "shouldAbortBackgroundActivityStart"); + abortBackgroundStart = shouldAbortBackgroundActivityStart(callingUid, callingPid, + callingPackage, realCallingUid, callerApp, originatingPendingIntent, + allowBackgroundActivityStart, intent); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } abort |= (abortBackgroundStart && !mService.isBackgroundActivityStartsEnabled()); // TODO: remove this toast after feature development is done if (abortBackgroundStart) { @@ -903,12 +909,6 @@ class ActivityStarter { mService.onStartActivitySetDidAppSwitch(); mController.doPendingActivityLaunches(false); - // maybe log to TRON, but only if we haven't already in shouldAbortBackgroundActivityStart() - if (!abortBackgroundStart) { - maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp, r, - originatingPendingIntent, false /*abortedStart*/); - } - return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask, outActivity); } @@ -927,17 +927,31 @@ class ActivityStarter { return false; } // don't abort if the callingUid is in the foreground or is a persistent system process - final boolean isCallingUidForeground = mService.isUidForeground(callingUid); - final boolean isCallingUidPersistentSystemProcess = isUidPersistentSystemProcess( - callingUid); + final int callingUidProcState = mService.getUidStateLocked(callingUid); + final boolean callingUidHasAnyVisibleWindow = + mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(callingUid); + final boolean isCallingUidForeground = callingUidHasAnyVisibleWindow + || callingUidProcState == ActivityManager.PROCESS_STATE_TOP; + final boolean isCallingUidPersistentSystemProcess = (callingUid == Process.SYSTEM_UID) + || callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI; if (isCallingUidForeground || isCallingUidPersistentSystemProcess) { return false; } // take realCallingUid into consideration - final boolean isRealCallingUidForeground = mService.isUidForeground( - realCallingUid); - final boolean isRealCallingUidPersistentSystemProcess = isUidPersistentSystemProcess( - realCallingUid); + final int realCallingUidProcState = (callingUid == realCallingUid) + ? callingUidProcState + : mService.getUidStateLocked(realCallingUid); + final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid) + ? callingUidHasAnyVisibleWindow + : mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(realCallingUid); + final boolean isRealCallingUidForeground = (callingUid == realCallingUid) + ? isCallingUidForeground + : realCallingUidHasAnyVisibleWindow + || realCallingUidProcState == ActivityManager.PROCESS_STATE_TOP; + final boolean isRealCallingUidPersistentSystemProcess = (callingUid == realCallingUid) + ? isCallingUidPersistentSystemProcess + : (realCallingUid == Process.SYSTEM_UID) + || realCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI; if (realCallingUid != callingUid) { // don't abort if the realCallingUid is in the foreground and callingUid isn't if (isRealCallingUidForeground) { @@ -975,61 +989,14 @@ class ActivityStarter { + "; isBgStartWhitelisted: " + allowBackgroundActivityStart + "; intent: " + intent + "]"); - maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp, - null /*r*/, originatingPendingIntent, true /*abortedStart*/); - return true; - } - - /** Returns true if uid is in a persistent state. */ - private boolean isUidPersistentSystemProcess(int uid) { - return (uid == Process.SYSTEM_UID) - || (mService.getUidStateLocked(uid) <= ActivityManager.PROCESS_STATE_PERSISTENT_UI); - } - - private void maybeLogActivityStart(int callingUid, String callingPackage, int realCallingUid, - Intent intent, WindowProcessController callerApp, ActivityRecord r, - PendingIntentRecord originatingPendingIntent, boolean abortedStart) { - boolean callerAppHasForegroundActivity = - callerApp != null && callerApp.hasForegroundActivities(); - if (!mService.isActivityStartsLoggingEnabled() || callerAppHasForegroundActivity - || (!abortedStart && r == null)) { - // skip logging in this case - return; - } - - try { - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "logActivityStart"); - final int callingUidProcState = mService.getUidStateLocked(callingUid); - final boolean callingUidHasAnyVisibleWindow = - mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(callingUid); - final int realCallingUidProcState = (callingUid == realCallingUid) - ? callingUidProcState - : mService.getUidStateLocked(realCallingUid); - final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid) - ? callingUidHasAnyVisibleWindow - : mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid( - realCallingUid); - final String targetPackage = (r != null) ? r.packageName : null; - final int targetUid = (r!= null) ? ((r.appInfo != null) ? r.appInfo.uid : -1) : -1; - final int targetUidProcState = mService.getUidStateLocked(targetUid); - final boolean targetUidHasAnyVisibleWindow = (targetUid != -1) - ? mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(targetUid) - : false; - final String targetWhitelistTag = (targetUid != -1) - ? mService.getPendingTempWhitelistTagForUidLocked(targetUid) - : null; - - mSupervisor.getActivityMetricsLogger().logActivityStart(intent, callerApp, r, - callingUid, callingPackage, callingUidProcState, - callingUidHasAnyVisibleWindow, - realCallingUid, realCallingUidProcState, - realCallingUidHasAnyVisibleWindow, - targetUid, targetPackage, targetUidProcState, - targetUidHasAnyVisibleWindow, targetWhitelistTag, + // log aborted activity start to TRON + if (mService.isActivityStartsLoggingEnabled()) { + mSupervisor.getActivityMetricsLogger().logAbortedBgActivityStart(intent, callerApp, + callingUid, callingPackage, callingUidProcState, callingUidHasAnyVisibleWindow, + realCallingUid, realCallingUidProcState, realCallingUidHasAnyVisibleWindow, (originatingPendingIntent != null)); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } + return true; } /** diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index e79820338c55..66666e681e7a 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -69,7 +69,7 @@ class InsetsSourceProvider { InsetsSourceProvider(InsetsSource source, InsetsStateController stateController, DisplayContent displayContent) { - mClientVisible = InsetsState.getDefaultVisibly(source.getType()); + mClientVisible = InsetsState.getDefaultVisibility(source.getType()); mSource = source; mDisplayContent = displayContent; mStateController = stateController; @@ -153,6 +153,7 @@ class InsetsSourceProvider { return; } mAdapter = new ControlAdapter(); + setClientVisible(InsetsState.getDefaultVisibility(mSource.getType())); mWin.startAnimation(mDisplayContent.getPendingTransaction(), mAdapter, !mClientVisible /* hidden */); mControllingWin = target; @@ -219,7 +220,7 @@ class InsetsSourceProvider { public void onAnimationCancelled(SurfaceControl animationLeash) { if (mAdapter == this) { mStateController.notifyControlRevoked(mControllingWin, InsetsSourceProvider.this); - setClientVisible(InsetsState.getDefaultVisibly(mSource.getType())); + setClientVisible(InsetsState.getDefaultVisibility(mSource.getType())); mControl = null; mControllingWin = null; mAdapter = null; diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java index e95ac5c43315..624fdc2e168e 100644 --- a/services/core/java/com/android/server/wm/RootActivityContainer.java +++ b/services/core/java/com/android/server/wm/RootActivityContainer.java @@ -175,6 +175,12 @@ class RootActivityContainer extends ConfigurationContainer private ActivityDisplay mDefaultDisplay; private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>(); + /** + * Cached value of the topmost resumed activity in the system. Updated when new activity is + * resumed. + */ + private ActivityRecord mTopResumedActivity; + /** The current user */ int mCurrentUser; /** Stack id of the front stack when user switched, indexed by userId. */ @@ -1145,6 +1151,23 @@ class RootActivityContainer extends ConfigurationContainer return result; } + void updateTopResumedActivityIfNeeded() { + final ActivityRecord prevTopActivity = mTopResumedActivity; + final ActivityStack topStack = getTopDisplayFocusedStack(); + if (topStack == null || topStack.mResumedActivity == prevTopActivity) { + return; + } + // Clear previous top state + if (prevTopActivity != null) { + prevTopActivity.scheduleTopResumedActivityChanged(false /* onTop */); + } + // Update the current top activity + mTopResumedActivity = topStack.mResumedActivity; + if (mTopResumedActivity != null) { + mTopResumedActivity.scheduleTopResumedActivityChanged(true /* onTop */); + } + } + void applySleepTokens(boolean applyToStacks) { for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { // Set the sleeping state of the display. @@ -1404,6 +1427,7 @@ class RootActivityContainer extends ConfigurationContainer mActivityDisplays.remove(display); mActivityDisplays.add(position, display); } + updateTopResumedActivityIfNeeded(); } @VisibleForTesting diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index e204697e46cf..33e46f4af301 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -313,6 +313,22 @@ public abstract class WindowManagerInternal { public abstract void waitForAllWindowsDrawn(Runnable callback, long timeout); /** + * Overrides the display size. + * + * @param displayId The display to override the display size. + * @param width The width to override. + * @param height The height to override. + */ + public abstract void setForcedDisplaySize(int displayId, int width, int height); + + /** + * Recover the display size to real display size. + * + * @param displayId The display to recover the display size. + */ + public abstract void clearForcedDisplaySize(int displayId); + + /** * Adds a window token for a given window type. * * @param token The token to add. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index e6581df233ef..8373b44112fe 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -192,6 +192,7 @@ import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.Gravity; import android.view.IAppTransitionAnimationSpecsFuture; +import android.view.IDisplayFoldListener; import android.view.IDockedStackListener; import android.view.IInputFilter; import android.view.IOnKeyguardExitResult; @@ -3748,6 +3749,16 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void registerDisplayFoldListener(IDisplayFoldListener listener) { + mPolicy.registerDisplayFoldListener(listener); + } + + @Override + public void unregisterDisplayFoldListener(IDisplayFoldListener listener) { + mPolicy.unregisterDisplayFoldListener(listener); + } + + @Override public int getPreferredOptionsPanelGravity(int displayId) { synchronized (mGlobalLock) { final DisplayContent displayContent = mRoot.getDisplayContent(displayId); @@ -6937,6 +6948,16 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void setForcedDisplaySize(int displayId, int width, int height) { + WindowManagerService.this.setForcedDisplaySize(displayId, width, height); + } + + @Override + public void clearForcedDisplaySize(int displayId) { + WindowManagerService.this.clearForcedDisplaySize(displayId); + } + + @Override public void addWindowToken(IBinder token, int type, int displayId) { WindowManagerService.this.addWindowToken(token, type, displayId); } diff --git a/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java b/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java index ea3f758fb209..98bad93e09e2 100644 --- a/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java +++ b/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java @@ -170,4 +170,9 @@ public class WmDisplayCutout { public int hashCode() { return Objects.hash(mInner, mFrameSize); } + + @Override + public String toString() { + return "WmDisplayCutout{" + mInner + ", mFrameSize=" + mFrameSize + '}'; + } } diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp index 7dd30bd50c4e..0d888dc41719 100644 --- a/services/core/jni/com_android_server_security_VerityUtils.cpp +++ b/services/core/jni/com_android_server_security_VerityUtils.cpp @@ -29,12 +29,9 @@ #include <android-base/unique_fd.h> -// TODO(112037636): Always include once fsverity.h is upstreamed and backported. -#define HAS_FSVERITY 0 - -#if HAS_FSVERITY +// TODO(112037636): Always include once fsverity.h is upstreamed. +#if __has_include(<linux/fsverity.h>) #include <linux/fsverity.h> - const int kSha256Bytes = 32; #endif @@ -76,7 +73,7 @@ class JavaByteArrayHolder { }; int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) { -#if HAS_FSVERITY +#if __has_include(<linux/fsverity.h>) const char* path = env->GetStringUTFChars(filePath, nullptr); ::android::base::unique_fd rfd(open(path, O_RDONLY | O_CLOEXEC)); if (rfd.get() < 0) { @@ -89,11 +86,11 @@ int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) { #else LOG_ALWAYS_FATAL("fs-verity is used while not enabled"); return ENOSYS; -#endif // HAS_FSVERITY +#endif } int measureFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) { -#if HAS_FSVERITY +#if __has_include(<linux/fsverity.h>) auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_digest) + kSha256Bytes); fsverity_digest* data = reinterpret_cast<fsverity_digest*>(raii->getRaw()); data->digest_size = kSha256Bytes; // the only input/output parameter @@ -110,11 +107,11 @@ int measureFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) { #else LOG_ALWAYS_FATAL("fs-verity is used while not enabled"); return ENOSYS; -#endif // HAS_FSVERITY +#endif } jbyteArray constructFsveritySignedData(JNIEnv* env, jobject /* clazz */, jbyteArray digest) { -#if HAS_FSVERITY +#if __has_include(<linux/fsverity.h>) auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_digest_disk) + kSha256Bytes); fsverity_digest_disk* data = reinterpret_cast<fsverity_digest_disk*>(raii->getRaw()); @@ -132,12 +129,12 @@ jbyteArray constructFsveritySignedData(JNIEnv* env, jobject /* clazz */, jbyteAr #else LOG_ALWAYS_FATAL("fs-verity is used while not enabled"); return 0; -#endif // HAS_FSVERITY +#endif } jbyteArray constructFsverityDescriptor(JNIEnv* env, jobject /* clazz */, jlong fileSize) { -#if HAS_FSVERITY +#if __has_include(<linux/fsverity.h>) auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_descriptor)); fsverity_descriptor* desc = reinterpret_cast<fsverity_descriptor*>(raii->getRaw()); @@ -156,12 +153,12 @@ jbyteArray constructFsverityDescriptor(JNIEnv* env, jobject /* clazz */, jlong f #else LOG_ALWAYS_FATAL("fs-verity is used while not enabled"); return 0; -#endif // HAS_FSVERITY +#endif } jbyteArray constructFsverityExtension(JNIEnv* env, jobject /* clazz */, jshort extensionId, jint extensionDataSize) { -#if HAS_FSVERITY +#if __has_include(<linux/fsverity.h>) auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_extension)); fsverity_extension* ext = reinterpret_cast<fsverity_extension*>(raii->getRaw()); @@ -172,12 +169,12 @@ jbyteArray constructFsverityExtension(JNIEnv* env, jobject /* clazz */, jshort e #else LOG_ALWAYS_FATAL("fs-verity is used while not enabled"); return 0; -#endif // HAS_FSVERITY +#endif } jbyteArray constructFsverityFooter(JNIEnv* env, jobject /* clazz */, jint offsetToDescriptorHead) { -#if HAS_FSVERITY +#if __has_include(<linux/fsverity.h>) auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_footer)); fsverity_footer* footer = reinterpret_cast<fsverity_footer*>(raii->getRaw()); @@ -188,7 +185,7 @@ jbyteArray constructFsverityFooter(JNIEnv* env, jobject /* clazz */, #else LOG_ALWAYS_FATAL("fs-verity is used while not enabled"); return 0; -#endif // HAS_FSVERITY +#endif } const JNINativeMethod sMethods[] = { diff --git a/services/net/java/android/net/shared/NetworkObserverRegistry.java b/services/net/java/android/net/shared/NetworkObserverRegistry.java deleted file mode 100644 index 36945f5de2c5..000000000000 --- a/services/net/java/android/net/shared/NetworkObserverRegistry.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.net.shared; - -import static android.Manifest.permission.NETWORK_STACK; - -import android.content.Context; -import android.net.INetd; -import android.net.INetdUnsolicitedEventListener; -import android.net.INetworkManagementEventObserver; -import android.net.InetAddresses; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.RouteInfo; -import android.os.Handler; -import android.os.RemoteCallbackList; -import android.os.RemoteException; -import android.os.SystemClock; - -/** - * A class for reporting network events to clients. - * - * Implements INetdUnsolicitedEventListener and registers with netd, and relays those events to - * all INetworkManagementEventObserver objects that have registered with it. - * - * TODO: Make the notifyXyz methods protected once subclasses (e.g., the NetworkManagementService - * subclass) no longer call them directly. - * - * TODO: change from RemoteCallbackList to direct in-process callbacks. - */ -public class NetworkObserverRegistry extends INetdUnsolicitedEventListener.Stub { - - private final Context mContext; - private final Handler mDaemonHandler; - private static final String TAG = "NetworkObserverRegistry"; - - /** - * Constructs a new instance and registers it with netd. - * This method should only be called once since netd will reject multiple registrations from - * the same process. - */ - public NetworkObserverRegistry(Context context, Handler handler, INetd netd) - throws RemoteException { - mContext = context; - mDaemonHandler = handler; - netd.registerUnsolicitedEventListener(this); - } - - private final RemoteCallbackList<INetworkManagementEventObserver> mObservers = - new RemoteCallbackList<>(); - - /** - * Registers the specified observer and start sending callbacks to it. - * This method may be called on any thread. - */ - public void registerObserver(INetworkManagementEventObserver observer) { - mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG); - mObservers.register(observer); - } - - /** - * Unregisters the specified observer and stop sending callbacks to it. - * This method may be called on any thread. - */ - public void unregisterObserver(INetworkManagementEventObserver observer) { - mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG); - mObservers.unregister(observer); - } - - @FunctionalInterface - private interface NetworkManagementEventCallback { - void sendCallback(INetworkManagementEventObserver o) throws RemoteException; - } - - private void invokeForAllObservers(NetworkManagementEventCallback eventCallback) { - final int length = mObservers.beginBroadcast(); - try { - for (int i = 0; i < length; i++) { - try { - eventCallback.sendCallback(mObservers.getBroadcastItem(i)); - } catch (RemoteException | RuntimeException e) { - } - } - } finally { - mObservers.finishBroadcast(); - } - } - - /** - * Notify our observers of a change in the data activity state of the interface - */ - public void notifyInterfaceClassActivity(int type, boolean isActive, long tsNanos, - int uid, boolean fromRadio) { - invokeForAllObservers(o -> o.interfaceClassDataActivityChanged( - Integer.toString(type), isActive, tsNanos)); - } - - @Override - public void onInterfaceClassActivityChanged(boolean isActive, - int label, long timestamp, int uid) throws RemoteException { - final long timestampNanos; - if (timestamp <= 0) { - timestampNanos = SystemClock.elapsedRealtimeNanos(); - } else { - timestampNanos = timestamp; - } - mDaemonHandler.post(() -> notifyInterfaceClassActivity(label, isActive, - timestampNanos, uid, false)); - } - - /** - * Notify our observers of a limit reached. - */ - @Override - public void onQuotaLimitReached(String alertName, String ifName) throws RemoteException { - mDaemonHandler.post(() -> notifyLimitReached(alertName, ifName)); - } - - /** - * Notify our observers of a limit reached. - */ - public void notifyLimitReached(String limitName, String iface) { - invokeForAllObservers(o -> o.limitReached(limitName, iface)); - } - - @Override - public void onInterfaceDnsServerInfo(String ifName, - long lifetime, String[] servers) throws RemoteException { - mDaemonHandler.post(() -> notifyInterfaceDnsServerInfo(ifName, lifetime, servers)); - } - - /** - * Notify our observers of DNS server information received. - */ - public void notifyInterfaceDnsServerInfo(String iface, long lifetime, String[] addresses) { - invokeForAllObservers(o -> o.interfaceDnsServerInfo(iface, lifetime, addresses)); - } - - @Override - public void onInterfaceAddressUpdated(String addr, - String ifName, int flags, int scope) throws RemoteException { - final LinkAddress address = new LinkAddress(addr, flags, scope); - mDaemonHandler.post(() -> notifyAddressUpdated(ifName, address)); - } - - /** - * Notify our observers of a new or updated interface address. - */ - public void notifyAddressUpdated(String iface, LinkAddress address) { - invokeForAllObservers(o -> o.addressUpdated(iface, address)); - } - - @Override - public void onInterfaceAddressRemoved(String addr, - String ifName, int flags, int scope) throws RemoteException { - final LinkAddress address = new LinkAddress(addr, flags, scope); - mDaemonHandler.post(() -> notifyAddressRemoved(ifName, address)); - } - - /** - * Notify our observers of a deleted interface address. - */ - public void notifyAddressRemoved(String iface, LinkAddress address) { - invokeForAllObservers(o -> o.addressRemoved(iface, address)); - } - - - @Override - public void onInterfaceAdded(String ifName) throws RemoteException { - mDaemonHandler.post(() -> notifyInterfaceAdded(ifName)); - } - - /** - * Notify our observers of an interface addition. - */ - public void notifyInterfaceAdded(String iface) { - invokeForAllObservers(o -> o.interfaceAdded(iface)); - } - - @Override - public void onInterfaceRemoved(String ifName) throws RemoteException { - mDaemonHandler.post(() -> notifyInterfaceRemoved(ifName)); - } - - /** - * Notify our observers of an interface removal. - */ - public void notifyInterfaceRemoved(String iface) { - invokeForAllObservers(o -> o.interfaceRemoved(iface)); - } - - @Override - public void onInterfaceChanged(String ifName, boolean up) throws RemoteException { - mDaemonHandler.post(() -> notifyInterfaceStatusChanged(ifName, up)); - } - - /** - * Notify our observers of an interface status change - */ - public void notifyInterfaceStatusChanged(String iface, boolean up) { - invokeForAllObservers(o -> o.interfaceStatusChanged(iface, up)); - } - - @Override - public void onInterfaceLinkStateChanged(String ifName, boolean up) throws RemoteException { - mDaemonHandler.post(() -> notifyInterfaceLinkStateChanged(ifName, up)); - } - - /** - * Notify our observers of an interface link state change - * (typically, an Ethernet cable has been plugged-in or unplugged). - */ - public void notifyInterfaceLinkStateChanged(String iface, boolean up) { - invokeForAllObservers(o -> o.interfaceLinkStateChanged(iface, up)); - } - - @Override - public void onRouteChanged(boolean updated, - String route, String gateway, String ifName) throws RemoteException { - final RouteInfo processRoute = new RouteInfo(new IpPrefix(route), - ("".equals(gateway)) ? null : InetAddresses.parseNumericAddress(gateway), - ifName); - mDaemonHandler.post(() -> notifyRouteChange(updated, processRoute)); - } - - /** - * Notify our observers of a route change. - */ - public void notifyRouteChange(boolean updated, RouteInfo route) { - if (updated) { - invokeForAllObservers(o -> o.routeUpdated(route)); - } else { - invokeForAllObservers(o -> o.routeRemoved(route)); - } - } - - @Override - public void onStrictCleartextDetected(int uid, String hex) throws RemoteException { - // Don't do anything here because this is not a method of INetworkManagementEventObserver. - // Only the NMS subclass will implement this. - } -} diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index 3172afbb086f..76beb8f99c18 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -75,6 +75,7 @@ import android.app.ActivityManager; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.LauncherApps; import android.content.pm.LauncherApps.PinItemRequest; @@ -6274,6 +6275,17 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { mManager.onApplicationActive(CALLING_PACKAGE_1, USER_0); } + public void testGetShareTargets_permission() { + IntentFilter filter = new IntentFilter(); + + assertExpectException(SecurityException.class, "Missing permission", () -> + mManager.getShareTargets(filter)); + + // Has permission, now it should pass. + mCallerPermissions.add(permission.MANAGE_APP_PREDICTIONS); + mManager.getShareTargets(filter); + } + public void testDumpsys_crossProfile() { prepareCrossProfileDataSet(); dumpsysOnLogcat("test1", /* force= */ true); diff --git a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java index 5d69bbdcf0c7..4e43d002d4ae 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java @@ -964,26 +964,6 @@ public class AppTimeLimitControllerTests { assertFalse(hasAppUsageObserver(UID, OBS_ID1)); } - /** Verify app usage limit observer added correctly reports it being a group limit */ - @Test - public void testAppUsageLimitObserver_IsGroupLimit() { - addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN); - AppTimeLimitController.AppUsageLimitGroup group = getAppUsageLimitObserver(UID, OBS_ID1); - assertNotNull("Observer wasn't added", group); - assertTrue("Observer didn't correctly report being a group limit", - group.isGroupLimit()); - } - - /** Verify app usage limit observer added correctly reports it being not a group limit */ - @Test - public void testAppUsageLimitObserver_IsNotGroupLimit() { - addAppUsageLimitObserver(OBS_ID1, new String[]{PKG_PROD}, TIME_30_MIN); - AppTimeLimitController.AppUsageLimitGroup group = getAppUsageLimitObserver(UID, OBS_ID1); - assertNotNull("Observer wasn't added", group); - assertFalse("Observer didn't correctly report not being a group limit", - group.isGroupLimit()); - } - /** Verify app usage limit observer added correctly reports its total usage limit */ @Test public void testAppUsageLimitObserver_GetTotalUsageLimit() { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 9c6ab0ab9aa9..4a4fece99817 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -3595,6 +3595,22 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testUserApprovedBubblesForPackage() throws Exception { + assertFalse(mBinderService.hasUserApprovedBubblesForPackage(PKG, mUid)); + mBinderService.setBubblesAllowed(PKG, mUid, true); + assertTrue(mBinderService.hasUserApprovedBubblesForPackage(PKG, mUid)); + assertTrue(mBinderService.areBubblesAllowedForPackage(PKG, mUid)); + } + + @Test + public void testUserRejectsBubblesForPackage() throws Exception { + assertFalse(mBinderService.hasUserApprovedBubblesForPackage(PKG, mUid)); + mBinderService.setBubblesAllowed(PKG, mUid, false); + assertTrue(mBinderService.hasUserApprovedBubblesForPackage(PKG, mUid)); + assertFalse(mBinderService.areBubblesAllowedForPackage(PKG, mUid)); + } + + @Test public void testIsCallerInstantApp_primaryUser() throws Exception { ApplicationInfo info = new ApplicationInfo(); info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 24a1f8c19f12..bde9dde52c2f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -2162,20 +2162,20 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testAllowBubbles_defaults() throws Exception { - assertTrue(mHelper.areBubblessAllowed(PKG_O, UID_O)); + assertTrue(mHelper.areBubblesAllowed(PKG_O, UID_O)); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); loadStreamXml(baos, false); - assertTrue(mHelper.areBubblessAllowed(PKG_O, UID_O)); + assertTrue(mHelper.areBubblesAllowed(PKG_O, UID_O)); assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O)); } @Test public void testAllowBubbles_xml() throws Exception { mHelper.setBubblesAllowed(PKG_O, UID_O, false); - assertFalse(mHelper.areBubblessAllowed(PKG_O, UID_O)); + assertFalse(mHelper.areBubblesAllowed(PKG_O, UID_O)); assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE, mHelper.getAppLockedFields(PKG_O, UID_O)); @@ -2183,7 +2183,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); loadStreamXml(baos, false); - assertFalse(mHelper.areBubblessAllowed(PKG_O, UID_O)); + assertFalse(mHelper.areBubblesAllowed(PKG_O, UID_O)); assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE, mHelper.getAppLockedFields(PKG_O, UID_O)); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 056568a0de64..ace965b8b42c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -523,9 +523,8 @@ public class ActivityStarterTests extends ActivityTestsBase { starter.setReason("testActivityStartsLogging_noLoggingWhenDisabled").execute(); // verify logging wasn't done - verify(mActivityMetricsLogger, never()).logActivityStart(any(), any(), any(), anyInt(), - any(), anyInt(), anyBoolean(), anyInt(), anyInt(), anyBoolean(), anyInt(), any(), - anyInt(), anyBoolean(), any(), anyBoolean()); + verify(mActivityMetricsLogger, never()).logAbortedBgActivityStart(any(), any(), anyInt(), + any(), anyInt(), anyBoolean(), anyInt(), anyInt(), anyBoolean(), anyBoolean()); } /** @@ -546,10 +545,9 @@ public class ActivityStarterTests extends ActivityTestsBase { starter.setReason("testActivityStartsLogging_logsWhenEnabled").execute(); // verify the above activity start was logged - verify(mActivityMetricsLogger, times(1)).logActivityStart(any(), any(), any(), + verify(mActivityMetricsLogger, times(1)).logAbortedBgActivityStart(any(), any(), eq(FAKE_CALLING_UID), eq(FAKE_CALLING_PACKAGE), anyInt(), anyBoolean(), - eq(FAKE_REAL_CALLING_UID), anyInt(), anyBoolean(), anyInt(), - any(), anyInt(), anyBoolean(), any(), eq(false)); + eq(FAKE_REAL_CALLING_UID), anyInt(), anyBoolean(), eq(false)); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index e3bacf96a86c..67ee4ad91d91 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -395,8 +395,8 @@ public class RootActivityContainerTests extends ActivityTestsBase { /** * Tests that home activities can be started on the displays that supports system decorations. */ - // TODO (b/118206886): Will add it back once launcher's patch is merged into master. - private void testStartHomeOnAllDisplays() { + @Test + public void testStartHomeOnAllDisplays() { // Create secondary displays. final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay()); mRootActivityContainer.addChild(secondDisplay, POSITION_TOP); diff --git a/services/usage/java/com/android/server/usage/AppTimeLimitController.java b/services/usage/java/com/android/server/usage/AppTimeLimitController.java index fa472e2575f0..873ada05bc27 100644 --- a/services/usage/java/com/android/server/usage/AppTimeLimitController.java +++ b/services/usage/java/com/android/server/usage/AppTimeLimitController.java @@ -510,12 +510,9 @@ public class AppTimeLimitController { } class AppUsageLimitGroup extends UsageGroup { - private boolean mGroupLimit; - public AppUsageLimitGroup(UserData user, ObserverAppData observerApp, int observerId, String[] observed, long timeLimitMs, PendingIntent limitReachedCallback) { super(user, observerApp, observerId, observed, timeLimitMs, limitReachedCallback); - mGroupLimit = observed.length > 1; } @Override @@ -529,11 +526,6 @@ public class AppTimeLimitController { } @GuardedBy("mLock") - boolean isGroupLimit() { - return mGroupLimit; - } - - @GuardedBy("mLock") long getTotaUsageLimit() { return mTimeLimitMs; } @@ -547,14 +539,6 @@ public class AppTimeLimitController { return mTimeLimitMs - mUsageTimeMs; } } - - @Override - @GuardedBy("mLock") - void dump(PrintWriter pw) { - super.dump(pw); - pw.print(" groupLimit="); - pw.print(mGroupLimit); - } } @@ -692,7 +676,7 @@ public class AppTimeLimitController { smallestGroup = otherGroup; } } - return new UsageStatsManagerInternal.AppUsageLimitData(smallestGroup.isGroupLimit(), + return new UsageStatsManagerInternal.AppUsageLimitData( smallestGroup.getTotaUsageLimit(), smallestGroup.getUsageRemaining()); } } diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp index 7dc83c3db868..f5b4308a1b50 100644 --- a/startop/view_compiler/Android.bp +++ b/startop/view_compiler/Android.bp @@ -16,12 +16,12 @@ cc_defaults { name: "viewcompiler_defaults", + defaults: ["libdexfile_static_defaults"], header_libs: [ "libbase_headers", ], shared_libs: [ "libbase", - "libdexfile", "libz", "slicer", ], diff --git a/telephony/java/android/telephony/CallQuality.java b/telephony/java/android/telephony/CallQuality.java index b27f6b44370f..cbe622847130 100644 --- a/telephony/java/android/telephony/CallQuality.java +++ b/telephony/java/android/telephony/CallQuality.java @@ -92,6 +92,10 @@ public final class CallQuality implements Parcelable { mCodecType = in.readInt(); } + /** @hide **/ + public CallQuality() { + } + /** * Constructor. * diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index 9fee5932dadc..af324debbd57 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -701,7 +701,7 @@ public class PhoneStateListener { * @hide */ @SystemApi - public void onCallAttributesChanged(CallAttributes callAttributes) { + public void onCallAttributesChanged(@NonNull CallAttributes callAttributes) { // default implementation empty } diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index bf9bf9a1ba8f..44756307a4b3 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -1631,8 +1631,9 @@ public class ServiceState implements Parcelable { /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public static boolean bearerBitmapHasCdma(int radioTechnologyBitmap) { - return (RIL_RADIO_CDMA_TECHNOLOGY_BITMASK & radioTechnologyBitmap) != 0; + public static boolean bearerBitmapHasCdma(int networkTypeBitmask) { + return (RIL_RADIO_CDMA_TECHNOLOGY_BITMASK + & convertNetworkTypeBitmaskToBearerBitmask(networkTypeBitmask)) != 0; } /** @hide */ diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 98fc7251e9a8..80ee0a72799e 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -10134,4 +10134,26 @@ public class TelephonyManager { } return ret; } + + /** + * Broadcast intent action for network country code changes. + * + * <p> + * The {@link #EXTRA_NETWORK_COUNTRY} extra indicates the country code of the current + * network returned by {@link #getNetworkCountryIso()}. + * + * @see #EXTRA_NETWORK_COUNTRY + * @see #getNetworkCountryIso() + */ + public static final String ACTION_NETWORK_COUNTRY_CHANGED = + "android.telephony.action.NETWORK_COUNTRY_CHANGED"; + + /** + * The extra used with an {@link #ACTION_NETWORK_COUNTRY_CHANGED} to specify the + * the country code in ISO 3166 format. + * <p class="note"> + * Retrieve with {@link android.content.Intent#getStringExtra(String)}. + */ + public static final String EXTRA_NETWORK_COUNTRY = + "android.telephony.extra.NETWORK_COUNTRY"; } diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index 294c79ba57a2..3d2fe5fec14a 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -23,6 +23,7 @@ import android.annotation.SystemApi; import android.net.LinkAddress; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.data.ApnSetting.ProtocolType; import java.net.InetAddress; import java.util.ArrayList; @@ -40,7 +41,7 @@ public final class DataCallResponse implements Parcelable { private final int mSuggestedRetryTime; private final int mCid; private final int mActive; - private final String mType; + private final int mProtocolType; private final String mIfname; private final List<LinkAddress> mAddresses; private final List<InetAddress> mDnses; @@ -53,8 +54,8 @@ public final class DataCallResponse implements Parcelable { * @param suggestedRetryTime The suggested data retry time in milliseconds. * @param cid The unique id of the data connection. * @param active Data connection active status. 0 = inactive, 1 = dormant, 2 = active. - * @param type The connection protocol, should be one of the PDP_type values in TS 27.007 - * section 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP". + * @param protocolType The connection protocol, should be one of the PDP_type values in 3GPP + * TS 27.007 section 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP". * @param ifname The network interface name. * @param addresses A list of addresses with optional "/" prefix length, e.g., * "192.0.1.3" or "192.0.1.11/16 2001:db8::1/64". Typically 1 IPv4 or 1 IPv6 or @@ -71,7 +72,7 @@ public final class DataCallResponse implements Parcelable { * either not sent a value or sent an invalid value. */ public DataCallResponse(int status, int suggestedRetryTime, int cid, int active, - @Nullable String type, @Nullable String ifname, + @ProtocolType int protocolType, @Nullable String ifname, @Nullable List<LinkAddress> addresses, @Nullable List<InetAddress> dnses, @Nullable List<InetAddress> gateways, @@ -80,7 +81,7 @@ public final class DataCallResponse implements Parcelable { mSuggestedRetryTime = suggestedRetryTime; mCid = cid; mActive = active; - mType = (type == null) ? "" : type; + mProtocolType = protocolType; mIfname = (ifname == null) ? "" : ifname; mAddresses = (addresses == null) ? new ArrayList<>() : addresses; mDnses = (dnses == null) ? new ArrayList<>() : dnses; @@ -94,7 +95,7 @@ public final class DataCallResponse implements Parcelable { mSuggestedRetryTime = source.readInt(); mCid = source.readInt(); mActive = source.readInt(); - mType = source.readString(); + mProtocolType = source.readInt(); mIfname = source.readString(); mAddresses = new ArrayList<>(); source.readList(mAddresses, LinkAddress.class.getClassLoader()); @@ -128,11 +129,10 @@ public final class DataCallResponse implements Parcelable { public int getActive() { return mActive; } /** - * @return The connection protocol, should be one of the PDP_type values in TS 27.007 section - * 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP". + * @return The connection protocol type. */ - @NonNull - public String getType() { return mType; } + @ProtocolType + public int getProtocolType() { return mProtocolType; } /** * @return The network interface name. @@ -181,7 +181,7 @@ public final class DataCallResponse implements Parcelable { .append(" retry=").append(mSuggestedRetryTime) .append(" cid=").append(mCid) .append(" active=").append(mActive) - .append(" type=").append(mType) + .append(" protocolType=").append(mProtocolType) .append(" ifname=").append(mIfname) .append(" addresses=").append(mAddresses) .append(" dnses=").append(mDnses) @@ -205,7 +205,7 @@ public final class DataCallResponse implements Parcelable { && this.mSuggestedRetryTime == other.mSuggestedRetryTime && this.mCid == other.mCid && this.mActive == other.mActive - && this.mType.equals(other.mType) + && this.mProtocolType == other.mProtocolType && this.mIfname.equals(other.mIfname) && mAddresses.size() == other.mAddresses.size() && mAddresses.containsAll(other.mAddresses) @@ -220,8 +220,8 @@ public final class DataCallResponse implements Parcelable { @Override public int hashCode() { - return Objects.hash(mStatus, mSuggestedRetryTime, mCid, mActive, mType, mIfname, mAddresses, - mDnses, mGateways, mPcscfs, mMtu); + return Objects.hash(mStatus, mSuggestedRetryTime, mCid, mActive, mProtocolType, mIfname, + mAddresses, mDnses, mGateways, mPcscfs, mMtu); } @Override @@ -235,7 +235,7 @@ public final class DataCallResponse implements Parcelable { dest.writeInt(mSuggestedRetryTime); dest.writeInt(mCid); dest.writeInt(mActive); - dest.writeString(mType); + dest.writeInt(mProtocolType); dest.writeString(mIfname); dest.writeList(mAddresses); dest.writeList(mDnses); diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java index da4822cc1d14..1d196f9b2885 100644 --- a/telephony/java/android/telephony/data/DataProfile.java +++ b/telephony/java/android/telephony/data/DataProfile.java @@ -16,14 +16,23 @@ package android.telephony.data; +import static android.telephony.data.ApnSetting.ProtocolType; + +import android.annotation.IntDef; import android.annotation.SystemApi; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.TelephonyManager.NetworkTypeBitMask; +import android.telephony.data.ApnSetting.ApnType; +import android.telephony.data.ApnSetting.AuthType; import android.text.TextUtils; import com.android.internal.telephony.RILConstants; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Description of a mobile data profile used for establishing * data connections. @@ -32,24 +41,39 @@ import com.android.internal.telephony.RILConstants; */ @SystemApi public final class DataProfile implements Parcelable { - - // The types indicating the data profile is used on GSM (3GPP) or CDMA (3GPP2) network. + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"TYPE_"}, + value = { + TYPE_COMMON, + TYPE_3GPP, + TYPE_3GPP2}) + public @interface DataProfileType {} + + /** Common data profile */ public static final int TYPE_COMMON = 0; + + /** 3GPP type data profile */ public static final int TYPE_3GPP = 1; + + /** 3GPP2 type data profile */ public static final int TYPE_3GPP2 = 2; private final int mProfileId; private final String mApn; - private final String mProtocol; + @ProtocolType + private final int mProtocolType; + @AuthType private final int mAuthType; private final String mUserName; private final String mPassword; + @DataProfileType private final int mType; private final int mMaxConnsTime; @@ -60,10 +84,13 @@ public final class DataProfile implements Parcelable { private final boolean mEnabled; + @ApnType private final int mSupportedApnTypesBitmap; - private final String mRoamingProtocol; + @ProtocolType + private final int mRoamingProtocolType; + @NetworkTypeBitMask private final int mBearerBitmap; private final int mMtu; @@ -73,14 +100,14 @@ public final class DataProfile implements Parcelable { private final boolean mPreferred; /** @hide */ - public DataProfile(int profileId, String apn, String protocol, int authType, String userName, - String password, int type, int maxConnsTime, int maxConns, int waitTime, - boolean enabled, int supportedApnTypesBitmap, String roamingProtocol, - int bearerBitmap, int mtu, boolean persistent, boolean preferred) { - + public DataProfile(int profileId, String apn, @ProtocolType int protocolType, int authType, + String userName, String password, int type, int maxConnsTime, int maxConns, + int waitTime, boolean enabled, @ApnType int supportedApnTypesBitmap, + @ProtocolType int roamingProtocolType, @NetworkTypeBitMask int bearerBitmap, + int mtu, boolean persistent, boolean preferred) { this.mProfileId = profileId; this.mApn = apn; - this.mProtocol = protocol; + this.mProtocolType = protocolType; if (authType == -1) { authType = TextUtils.isEmpty(userName) ? RILConstants.SETUP_DATA_AUTH_NONE : RILConstants.SETUP_DATA_AUTH_PAP_CHAP; @@ -95,7 +122,7 @@ public final class DataProfile implements Parcelable { this.mEnabled = enabled; this.mSupportedApnTypesBitmap = supportedApnTypesBitmap; - this.mRoamingProtocol = roamingProtocol; + this.mRoamingProtocolType = roamingProtocolType; this.mBearerBitmap = bearerBitmap; this.mMtu = mtu; this.mPersistent = persistent; @@ -106,7 +133,7 @@ public final class DataProfile implements Parcelable { public DataProfile(Parcel source) { mProfileId = source.readInt(); mApn = source.readString(); - mProtocol = source.readString(); + mProtocolType = source.readInt(); mAuthType = source.readInt(); mUserName = source.readString(); mPassword = source.readString(); @@ -116,7 +143,7 @@ public final class DataProfile implements Parcelable { mWaitTime = source.readInt(); mEnabled = source.readBoolean(); mSupportedApnTypesBitmap = source.readInt(); - mRoamingProtocol = source.readString(); + mRoamingProtocolType = source.readInt(); mBearerBitmap = source.readInt(); mMtu = source.readInt(); mPersistent = source.readBoolean(); @@ -134,16 +161,14 @@ public final class DataProfile implements Parcelable { public String getApn() { return mApn; } /** - * @return The connection protocol, should be one of the PDP_type values in TS 27.007 section - * 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP". + * @return The connection protocol defined in 3GPP TS 27.007 section 10.1.1. */ - public String getProtocol() { return mProtocol; } + public @ProtocolType int getProtocol() { return mProtocolType; } /** - * @return The authentication protocol used for this PDP context - * (None: 0, PAP: 1, CHAP: 2, PAP&CHAP: 3) + * @return The authentication protocol used for this PDP context. */ - public int getAuthType() { return mAuthType; } + public @AuthType int getAuthType() { return mAuthType; } /** * @return The username for APN. Can be null. @@ -156,9 +181,9 @@ public final class DataProfile implements Parcelable { public String getPassword() { return mPassword; } /** - * @return The profile type. Could be one of TYPE_COMMON, TYPE_3GPP, or TYPE_3GPP2. + * @return The profile type. */ - public int getType() { return mType; } + public @DataProfileType int getType() { return mType; } /** * @return The period in seconds to limit the maximum connections. @@ -183,20 +208,19 @@ public final class DataProfile implements Parcelable { public boolean isEnabled() { return mEnabled; } /** - * @return The supported APN types bitmap. See RIL_ApnTypes for the value of each bit. + * @return The supported APN types bitmap. */ - public int getSupportedApnTypesBitmap() { return mSupportedApnTypesBitmap; } + public @ApnType int getSupportedApnTypesBitmap() { return mSupportedApnTypesBitmap; } /** - * @return The connection protocol on roaming network, should be one of the PDP_type values in - * TS 27.007 section 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP". + * @return The connection protocol on roaming network defined in 3GPP TS 27.007 section 10.1.1. */ - public String getRoamingProtocol() { return mRoamingProtocol; } + public @ProtocolType int getRoamingProtocol() { return mRoamingProtocolType; } /** - * @return The bearer bitmap. See RIL_RadioAccessFamily for the value of each bit. + * @return The bearer bitmap indicating the applicable networks for this data profile. */ - public int getBearerBitmap() { return mBearerBitmap; } + public @NetworkTypeBitMask int getBearerBitmap() { return mBearerBitmap; } /** * @return The maximum transmission unit (MTU) size in bytes. @@ -222,12 +246,12 @@ public final class DataProfile implements Parcelable { @Override public String toString() { - return "DataProfile=" + mProfileId + "/" + mProtocol + "/" + mAuthType + return "DataProfile=" + mProfileId + "/" + mProtocolType + "/" + mAuthType + "/" + (Build.IS_USER ? "***/***/***" : (mApn + "/" + mUserName + "/" + mPassword)) + "/" + mType + "/" + mMaxConnsTime + "/" + mMaxConns + "/" + mWaitTime + "/" + mEnabled + "/" + mSupportedApnTypesBitmap + "/" - + mRoamingProtocol + "/" + mBearerBitmap + "/" + mMtu + "/" + mPersistent + "/" + + mRoamingProtocolType + "/" + mBearerBitmap + "/" + mMtu + "/" + mPersistent + "/" + mPreferred; } @@ -242,7 +266,7 @@ public final class DataProfile implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mProfileId); dest.writeString(mApn); - dest.writeString(mProtocol); + dest.writeInt(mProtocolType); dest.writeInt(mAuthType); dest.writeString(mUserName); dest.writeString(mPassword); @@ -252,7 +276,7 @@ public final class DataProfile implements Parcelable { dest.writeInt(mWaitTime); dest.writeBoolean(mEnabled); dest.writeInt(mSupportedApnTypesBitmap); - dest.writeString(mRoamingProtocol); + dest.writeInt(mRoamingProtocolType); dest.writeInt(mBearerBitmap); dest.writeInt(mMtu); dest.writeBoolean(mPersistent); diff --git a/telephony/java/com/android/ims/internal/uce/common/StatusCode.java b/telephony/java/com/android/ims/internal/uce/common/StatusCode.java index 3921cfbbfce7..7250eee70ecb 100644 --- a/telephony/java/com/android/ims/internal/uce/common/StatusCode.java +++ b/telephony/java/com/android/ims/internal/uce/common/StatusCode.java @@ -64,6 +64,10 @@ public class StatusCode implements Parcelable { public static final int UCE_NO_CHANGE_IN_CAP = 13; /** Service is unknown. */ public static final int UCE_SERVICE_UNKNOWN = 14; + /** Service cannot support Invalid Feature Tag */ + public static final int UCE_INVALID_FEATURE_TAG = 15; + /** Service is Available */ + public static final int UCE_SERVICE_AVAILABLE = 16; private int mStatusCode = UCE_SUCCESS; diff --git a/telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl b/telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl index 43f83cd94d57..1fb8513d410a 100644 --- a/telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl +++ b/telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl @@ -66,10 +66,30 @@ interface IUceService * service the client created. * * @return optionsServiceHandle + * * @hide + * + * @deprecated This is replaced with new API createOptionsServiceForSubscription() */ int createOptionsService(IOptionsListener optionsListener, inout UceLong optionsServiceListenerHdl); + /** + * Creates a options service for Capability Discovery. + * @param optionsListener IOptionsListener object. + * @param optionsServiceListenerHdl wrapper for client's listener handle to be stored. + * @param iccId the ICC-ID derived from SubscriptionInfo for the Service requested + * + * The service will fill UceLong.mUceLong with presenceListenerHandle allocated and + * used to validate callbacks received in IPresenceListener are indeed from the + * service the client created. + * + * @return optionsServiceHandle + * + * @hide + */ + int createOptionsServiceForSubscription(IOptionsListener optionsListener, + inout UceLong optionsServiceListenerHdl, + in String iccId); /** * Destroys a Options service. @@ -89,14 +109,36 @@ interface IUceService * service the client created. * * @return presenceServiceHdl + * * @hide + * + * @deprecated This is replaced with new API createPresenceServiceForSubscription() */ int createPresenceService(IPresenceListener presenceServiceListener, inout UceLong presenceServiceListenerHdl); + /** + * Creates a presence service. + * @param presenceServiceListener IPresenceListener object + * @param presenceServiceListenerHdl wrapper for client's listener handle to be stored. + * @param iccId the ICC-ID derived from SubscriptionInfo for the Service requested + * + * The service will fill UceLong.mUceLong with presenceListenerHandle allocated and + * used to validate callbacks received in IPresenceListener are indeed from the + * service the client created. + * + * @return presenceServiceHdl + * + * @hide + */ + int createPresenceServiceForSubscription(IPresenceListener presenceServiceListener, + inout UceLong presenceServiceListenerHdl, + in String iccId); /** * Destroys a presence service. + * * @param presenceServiceHdl handle returned during createPresenceService() + * * @hide */ void destroyPresenceService(int presenceServiceHdl); @@ -105,23 +147,55 @@ interface IUceService /** * Query the UCE Service for information to know whether the is registered. + * * @return boolean, true if Registered to for network events else false. + * * @hide */ boolean getServiceStatus(); /** * Query the UCE Service for presence Service. + * * @return IPresenceService object. + * * @hide + * + * @deprecated use API getPresenceServiceForSubscription() */ IPresenceService getPresenceService(); /** + * Query the UCE Service for presence Service. + * + * @param iccId the ICC-ID derived from SubscriptionInfo for the Service requested + * + * @return IPresenceService object. + * + * @hide + */ + IPresenceService getPresenceServiceForSubscription(in String iccId); + + /** * Query the UCE Service for options service object. + * * @return IOptionsService object. + * + * @deprecated use API getOptionsServiceForSubscription() + * * @hide */ IOptionsService getOptionsService(); + /** + * Query the UCE Service for options service object. + * + * @param iccId the ICC-ID derived from SubscriptionInfo for the Service requested + * + * @return IOptionsService object. + * + * @hide + */ + IOptionsService getOptionsServiceForSubscription(in String iccId); + } diff --git a/telephony/java/com/android/ims/internal/uce/uceservice/UceServiceBase.java b/telephony/java/com/android/ims/internal/uce/uceservice/UceServiceBase.java index 3660e039582d..ceb191910427 100644 --- a/telephony/java/com/android/ims/internal/uce/uceservice/UceServiceBase.java +++ b/telephony/java/com/android/ims/internal/uce/uceservice/UceServiceBase.java @@ -56,6 +56,14 @@ public abstract class UceServiceBase { return onCreateOptionsService(optionsListener, optionsServiceListenerHdl); } + @Override + public int createOptionsServiceForSubscription(IOptionsListener optionsListener, + UceLong optionsServiceListenerHdl, + String iccId) { + return onCreateOptionsService(optionsListener, optionsServiceListenerHdl, + iccId); + } + @Override public void destroyOptionsService(int optionsServiceHandle) { @@ -70,6 +78,14 @@ public abstract class UceServiceBase { } @Override + public int createPresenceServiceForSubscription(IPresenceListener presServiceListener, + UceLong presServiceListenerHdl, + String iccId) { + return onCreatePresService(presServiceListener, presServiceListenerHdl, + iccId); + } + + @Override public void destroyPresenceService(int presServiceHdl) { onDestroyPresService(presServiceHdl); } @@ -85,9 +101,19 @@ public abstract class UceServiceBase { } @Override + public IPresenceService getPresenceServiceForSubscription(String iccId) { + return onGetPresenceService(iccId); + } + + @Override public IOptionsService getOptionsService() { return onGetOptionsService(); } + + @Override + public IOptionsService getOptionsServiceForSubscription(String iccId) { + return onGetOptionsService(iccId); + } } private UceServiceBinder mBinder; @@ -120,6 +146,13 @@ public abstract class UceServiceBase { return 0; } + protected int onCreateOptionsService(IOptionsListener optionsListener, + UceLong optionsServiceListenerHdl, + String iccId) { + //no-op + return 0; + } + protected void onDestroyOptionsService(int cdServiceHandle) { //no-op return; @@ -131,6 +164,13 @@ public abstract class UceServiceBase { return 0; } + protected int onCreatePresService(IPresenceListener presServiceListener, + UceLong presServiceListenerHdl, + String iccId) { + //no-op + return 0; + } + protected void onDestroyPresService(int presServiceHdl) { //no-op return; @@ -146,8 +186,18 @@ public abstract class UceServiceBase { return null; } + protected IPresenceService onGetPresenceService(String iccId) { + //no-op + return null; + } + protected IOptionsService onGetOptionsService () { //no-op return null; } + + protected IOptionsService onGetOptionsService (String iccId) { + //no-op + return null; + } } diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 930003462110..d9b206f49dd5 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -268,31 +268,11 @@ public interface RILConstants { int LTE_ON_CDMA_FALSE = 0; int LTE_ON_CDMA_TRUE = 1; - int CDM_TTY_MODE_DISABLED = 0; - int CDM_TTY_MODE_ENABLED = 1; - - int CDM_TTY_FULL_MODE = 1; - int CDM_TTY_HCO_MODE = 2; - int CDM_TTY_VCO_MODE = 3; - - /* Setup a packet data connection. See ril.h RIL_REQUEST_SETUP_DATA_CALL */ - int SETUP_DATA_TECH_CDMA = 0; - int SETUP_DATA_TECH_GSM = 1; - int SETUP_DATA_AUTH_NONE = 0; int SETUP_DATA_AUTH_PAP = 1; int SETUP_DATA_AUTH_CHAP = 2; int SETUP_DATA_AUTH_PAP_CHAP = 3; - String SETUP_DATA_PROTOCOL_IP = "IP"; - String SETUP_DATA_PROTOCOL_IPV6 = "IPV6"; - String SETUP_DATA_PROTOCOL_IPV4V6 = "IPV4V6"; - - /* NV config radio reset types. */ - int NV_CONFIG_RELOAD_RESET = 1; - int NV_CONFIG_ERASE_RESET = 2; - int NV_CONFIG_FACTORY_RESET = 3; - /* LCE service related constants. */ int LCE_NOT_AVAILABLE = -1; int LCE_STOPPED = 0; diff --git a/tests/ActivityTests/AndroidManifest.xml b/tests/ActivityTests/AndroidManifest.xml index b381cbfbd0a9..0b3bd70ba5c9 100644 --- a/tests/ActivityTests/AndroidManifest.xml +++ b/tests/ActivityTests/AndroidManifest.xml @@ -79,6 +79,7 @@ android:singleUser="true" android:exported="true" /> <receiver android:name="TrackTimeReceiver" /> <receiver android:name="AlarmSpamReceiver" /> + <receiver android:name="SlowReceiver" /> <activity android:name="DisableScreenshotsActivity" android:label="DisableScreenshots" android:theme="@style/DisableScreenshots"> diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java index 0f4960887a33..2581e083cc64 100644 --- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java +++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java @@ -16,21 +16,21 @@ package com.google.android.test.activity; -import java.util.ArrayList; -import java.util.List; - import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.AlarmManager; import android.app.AlertDialog; import android.app.PendingIntent; -import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentProviderClient; +import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.UserInfo; +import android.content.res.Configuration; +import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; @@ -42,21 +42,18 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; -import android.graphics.Bitmap; import android.provider.Settings; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; -import android.widget.ScrollView; -import android.widget.Toast; +import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.content.Context; -import android.content.pm.UserInfo; -import android.content.res.Configuration; -import android.util.Log; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; +import android.widget.Toast; +import java.util.ArrayList; +import java.util.List; public class ActivityTestMain extends Activity { static final String TAG = "ActivityTest"; @@ -73,8 +70,13 @@ public class ActivityTestMain extends Activity { ServiceConnection mIsolatedConnection; + static final String SLOW_RECEIVER_ACTION = "com.google.android.test.activity.SLOW_ACTION"; + static final String SLOW_RECEIVER_EXTRA = "slow_ordinal"; + static final int MSG_SPAM = 1; static final int MSG_SPAM_ALARM = 2; + static final int MSG_SLOW_RECEIVER = 3; + static final int MSG_SLOW_ALARM_RECEIVER = 4; final Handler mHandler = new Handler() { @Override @@ -100,11 +102,58 @@ public class ActivityTestMain extends Activity { mAlarm.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME, when+(30*1000), pi); scheduleSpamAlarm(30*1000); } break; + case MSG_SLOW_RECEIVER: { + // Several back to back, to illustrate dispatch policy + Intent intent = new Intent(ActivityTestMain.this, SlowReceiver.class); + intent.setAction(SLOW_RECEIVER_ACTION); + intent.putExtra(SLOW_RECEIVER_EXTRA, 1); + sendOrderedBroadcast(intent, null, mSlowReceiverCompletion, mHandler, + Activity.RESULT_OK, null, null); + intent.putExtra(SLOW_RECEIVER_EXTRA, 2); + sendOrderedBroadcast(intent, null, mSlowReceiverCompletion, mHandler, + Activity.RESULT_OK, null, null); + intent.putExtra(SLOW_RECEIVER_EXTRA, 3); + sendOrderedBroadcast(intent, null, mSlowReceiverCompletion, mHandler, + Activity.RESULT_OK, null, null); + } break; + case MSG_SLOW_ALARM_RECEIVER: { + // Several back to back, to illustrate dispatch policy + Intent intent = new Intent(ActivityTestMain.this, SlowReceiver.class); + intent.setAction(SLOW_RECEIVER_ACTION); + intent.putExtra(SLOW_RECEIVER_EXTRA, 1); + sendOrderedBroadcast(intent, null, mSlowReceiverCompletion, mHandler, + Activity.RESULT_OK, null, null); + intent.putExtra(SLOW_RECEIVER_EXTRA, 2); + sendOrderedBroadcast(intent, null, mSlowReceiverCompletion, mHandler, + Activity.RESULT_OK, null, null); + intent.putExtra(SLOW_RECEIVER_EXTRA, 3); + sendOrderedBroadcast(intent, null, mSlowReceiverCompletion, mHandler, + Activity.RESULT_OK, null, null); + + // Also send a broadcast alarm to evaluate the alarm fast-forward policy + intent.putExtra(SLOW_RECEIVER_EXTRA, 4); + PendingIntent pi = PendingIntent.getBroadcast(ActivityTestMain.this, 1, intent, 0); + AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE); + long now = SystemClock.elapsedRealtime(); + Log.i(TAG, "Setting alarm for now + 5 seconds"); + am.setExact(AlarmManager.ELAPSED_REALTIME, now + 5_000, pi); + } break; } super.handleMessage(msg); } }; + final BroadcastReceiver mSlowReceiverCompletion = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final int extra = intent.getIntExtra(SLOW_RECEIVER_EXTRA, -1); + final String msg = "Slow receiver " + extra + " completed"; + Toast.makeText(ActivityTestMain.this, msg, Toast.LENGTH_LONG) + .show(); + Log.i(TAG, msg); + } + }; + class BroadcastResultReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { @@ -387,6 +436,18 @@ public class ActivityTestMain extends Activity { return true; } }); + menu.add("Slow receiver").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { + scheduleSlowReceiver(); + return true; + } + }); + menu.add("Slow alarm receiver").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { + scheduleSlowAlarmReceiver(); + return true; + } + }); menu.add("Spam!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { scheduleSpam(false); @@ -469,6 +530,7 @@ public class ActivityTestMain extends Activity { protected void onStop() { super.onStop(); mHandler.removeMessages(MSG_SPAM_ALARM); + mHandler.removeMessages(MSG_SLOW_RECEIVER); for (ServiceConnection conn : mConnections) { unbindService(conn); } @@ -544,6 +606,16 @@ public class ActivityTestMain extends Activity { mHandler.sendMessageDelayed(msg, delay); } + void scheduleSlowReceiver() { + mHandler.removeMessages(MSG_SLOW_RECEIVER); + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SLOW_RECEIVER), 500); + } + + void scheduleSlowAlarmReceiver() { + mHandler.removeMessages(MSG_SLOW_ALARM_RECEIVER); + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SLOW_ALARM_RECEIVER), 500); + } + private View scrollWrap(View view) { ScrollView scroller = new ScrollView(this); scroller.addView(view, new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT, diff --git a/tests/ActivityTests/src/com/google/android/test/activity/SlowReceiver.java b/tests/ActivityTests/src/com/google/android/test/activity/SlowReceiver.java new file mode 100644 index 000000000000..0437a289741c --- /dev/null +++ b/tests/ActivityTests/src/com/google/android/test/activity/SlowReceiver.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.test.activity; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.SystemClock; +import android.util.Log; + +public class SlowReceiver extends BroadcastReceiver { + private static final String TAG = "SlowReceiver"; + private static final long RECEIVER_DELAY = 6_000; + + @Override + public void onReceive(Context context, Intent intent) { + final int extra = intent.getIntExtra(ActivityTestMain.SLOW_RECEIVER_EXTRA, -1); + if (extra == 1) { + Log.i(TAG, "Received broadcast 1; delaying return by " + RECEIVER_DELAY + " ms"); + long now = SystemClock.elapsedRealtime(); + final long end = now + RECEIVER_DELAY; + while (now < end) { + try { + Thread.sleep(end - now); + } catch (InterruptedException e) { } + now = SystemClock.elapsedRealtime(); + } + } else { + Log.i(TAG, "Extra parameter not 1, returning immediately"); + } + Log.i(TAG, "Returning from onReceive()"); + } +} diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index 0b74d878f069..5b17224e41e5 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -246,17 +246,17 @@ public class VpnTest { assertFalse(vpn.getLockdown()); // Set always-on without lockdown. - assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false)); + assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, Collections.emptyList())); assertTrue(vpn.getAlwaysOn()); assertFalse(vpn.getLockdown()); // Set always-on with lockdown. - assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true)); + assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.emptyList())); assertTrue(vpn.getAlwaysOn()); assertTrue(vpn.getLockdown()); // Remove always-on configuration. - assertTrue(vpn.setAlwaysOnPackage(null, false)); + assertTrue(vpn.setAlwaysOnPackage(null, false, Collections.emptyList())); assertFalse(vpn.getAlwaysOn()); assertFalse(vpn.getLockdown()); } @@ -270,11 +270,11 @@ public class VpnTest { assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]); // Set always-on without lockdown. - assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false)); + assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null)); assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]); // Set always-on with lockdown. - assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true)); + assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null)); verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] { new UidRange(user.start, user.start + PKG_UIDS[1] - 1), new UidRange(user.start + PKG_UIDS[1] + 1, user.stop) @@ -283,7 +283,7 @@ public class VpnTest { assertUnblocked(vpn, user.start + PKG_UIDS[1]); // Switch to another app. - assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true)); + assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null)); verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] { new UidRange(user.start, user.start + PKG_UIDS[1] - 1), new UidRange(user.start + PKG_UIDS[1] + 1, user.stop) @@ -297,6 +297,87 @@ public class VpnTest { } @Test + public void testLockdownWhitelist() throws Exception { + final Vpn vpn = createVpn(primaryUser.id); + final UidRange user = UidRange.createForUser(primaryUser.id); + + // Set always-on with lockdown and whitelist app PKGS[2] from lockdown. + assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.singletonList(PKGS[2]))); + verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] { + new UidRange(user.start, user.start + PKG_UIDS[1] - 1), + new UidRange(user.start + PKG_UIDS[2] + 1, user.stop) + })); + assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]); + assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]); + + // Change whitelisted app to PKGS[3]. + assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.singletonList(PKGS[3]))); + verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] { + new UidRange(user.start + PKG_UIDS[2] + 1, user.stop) + })); + verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] { + new UidRange(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1), + new UidRange(user.start + PKG_UIDS[3] + 1, user.stop) + })); + assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2]); + assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[3]); + + // Change the VPN app. + assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList(PKGS[3]))); + verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] { + new UidRange(user.start, user.start + PKG_UIDS[1] - 1), + new UidRange(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1) + })); + verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] { + new UidRange(user.start, user.start + PKG_UIDS[0] - 1), + new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1) + })); + assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]); + assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]); + + // Remove the whitelist. + assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null)); + verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] { + new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1), + new UidRange(user.start + PKG_UIDS[3] + 1, user.stop) + })); + verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] { + new UidRange(user.start + PKG_UIDS[0] + 1, user.stop), + })); + assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], + user.start + PKG_UIDS[3]); + assertUnblocked(vpn, user.start + PKG_UIDS[0]); + + // Add the whitelist. + assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList(PKGS[1]))); + verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] { + new UidRange(user.start + PKG_UIDS[0] + 1, user.stop) + })); + verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] { + new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1), + new UidRange(user.start + PKG_UIDS[1] + 1, user.stop) + })); + assertBlocked(vpn, user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]); + assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1]); + + // Try whitelisting a package with a comma, should be rejected. + assertFalse(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList("a.b,c.d"))); + + // Pass a non-existent packages in the whitelist, they (and only they) should be ignored. + // Whitelisted package should change from PGKS[1] to PKGS[2]. + assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, + Arrays.asList("com.foo.app", PKGS[2], "com.bar.app"))); + verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[]{ + new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1), + new UidRange(user.start + PKG_UIDS[1] + 1, user.stop) + })); + verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[]{ + new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[2] - 1), + new UidRange(user.start + PKG_UIDS[2] + 1, user.stop) + })); + } + + @Test public void testLockdownAddingAProfile() throws Exception { final Vpn vpn = createVpn(primaryUser.id); setMockedUsers(primaryUser); @@ -310,7 +391,7 @@ public class VpnTest { final UidRange profile = UidRange.createForUser(tempProfile.id); // Set lockdown. - assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true)); + assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null)); verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] { new UidRange(user.start, user.start + PKG_UIDS[3] - 1), new UidRange(user.start + PKG_UIDS[3] + 1, user.stop) @@ -436,7 +517,7 @@ public class VpnTest { .cancelAsUser(anyString(), anyInt(), eq(userHandle)); // Start showing a notification for disconnected once always-on. - vpn.setAlwaysOnPackage(PKGS[0], false); + vpn.setAlwaysOnPackage(PKGS[0], false, null); order.verify(mNotificationManager) .notifyAsUser(anyString(), anyInt(), any(), eq(userHandle)); @@ -450,7 +531,7 @@ public class VpnTest { .notifyAsUser(anyString(), anyInt(), any(), eq(userHandle)); // Notification should be cleared after unsetting always-on package. - vpn.setAlwaysOnPackage(null, false); + vpn.setAlwaysOnPackage(null, false, null); order.verify(mNotificationManager).cancelAsUser(anyString(), anyInt(), eq(userHandle)); } @@ -583,7 +664,9 @@ public class VpnTest { doAnswer(invocation -> { final String appName = (String) invocation.getArguments()[0]; final int userId = (int) invocation.getArguments()[1]; - return UserHandle.getUid(userId, packages.get(appName)); + Integer appId = packages.get(appName); + if (appId == null) throw new PackageManager.NameNotFoundException(appName); + return UserHandle.getUid(userId, appId); }).when(mPackageManager).getPackageUidAsUser(anyString(), anyInt()); } catch (Exception e) { } diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java index fc4cd01a5a2a..2690ee87f5b2 100644 --- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java +++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java @@ -16,8 +16,12 @@ package android.processor.view.inspector; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; @@ -102,6 +106,83 @@ final class AnnotationUtils { } /** + * Get a typed list of values for an annotation array property by name. + * + * The returned list will be empty if the value was left at the default. + * + * @param propertyName The name of the property to search for + * @param valueClass The expected class of the property value + * @param element The element the annotation is on, used for exceptions + * @param annotationMirror An annotation mirror to search for the property + * @param <T> The type of the value + * @return A list containing the requested types + */ + <T> List<T> typedArrayValuesByName( + String propertyName, + Class<T> valueClass, + Element element, + AnnotationMirror annotationMirror) { + return untypedArrayValuesByName(propertyName, element, annotationMirror) + .stream() + .map(annotationValue -> { + final Object value = annotationValue.getValue(); + + if (value == null) { + throw new ProcessingException( + "Unexpected null in array.", + element, + annotationMirror, + annotationValue); + } + + if (valueClass.isAssignableFrom(value.getClass())) { + return valueClass.cast(value); + } else { + throw new ProcessingException( + String.format( + "Expected array entry to have type %s, but got %s.", + valueClass.getCanonicalName(), + value.getClass().getCanonicalName()), + element, + annotationMirror, + annotationValue); + } + }) + .collect(Collectors.toList()); + } + + /** + * Get a list of values for an annotation array property by name. + * + * @param propertyName The name of the property to search for + * @param element The element the annotation is on, used for exceptions + * @param annotationMirror An annotation mirror to search for the property + * @return A list of annotation values, empty list if none found + */ + List<AnnotationValue> untypedArrayValuesByName( + String propertyName, + Element element, + AnnotationMirror annotationMirror) { + return typedValueByName(propertyName, List.class, element, annotationMirror) + .map(untypedValues -> { + List<AnnotationValue> typedValues = new ArrayList<>(untypedValues.size()); + + for (Object untypedValue : untypedValues) { + if (untypedValue instanceof AnnotationValue) { + typedValues.add((AnnotationValue) untypedValue); + } else { + throw new ProcessingException( + "Unable to convert array entry to AnnotationValue", + element, + annotationMirror); + } + } + + return typedValues; + }).orElseGet(Collections::emptyList); + } + + /** * Get the typed value of an annotation property by name. * * The returned optional will be empty if the value was left at the default, or if the value diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java index f1ebb87fed4d..6f588935c44c 100644 --- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java +++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java @@ -19,7 +19,9 @@ package android.processor.view.inspector; import com.squareup.javapoet.ClassName; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -92,6 +94,8 @@ public final class InspectableClassModel { private final Type mType; private boolean mAttributeIdInferrableFromR = true; private int mAttributeId = 0; + private List<IntEnumEntry> mIntEnumEntries; + private List<IntFlagEntry> mIntFlagEntries; public Property(String name, String getter, Type type) { mName = Objects.requireNonNull(name, "Name must not be null"); @@ -133,6 +137,40 @@ public final class InspectableClassModel { return mType; } + /** + * Get the mapping for an {@code int} enumeration, if present. + * + * @return A list of mapping entries, empty if absent + */ + public List<IntEnumEntry> getIntEnumEntries() { + if (mIntEnumEntries != null) { + return mIntEnumEntries; + } else { + return Collections.emptyList(); + } + } + + public void setIntEnumEntries(List<IntEnumEntry> intEnumEntries) { + mIntEnumEntries = intEnumEntries; + } + + /** + * Get the mapping of {@code int} flags, if present. + * + * @return A list of mapping entries, empty if absent + */ + public List<IntFlagEntry> getIntFlagEntries() { + if (mIntFlagEntries != null) { + return mIntFlagEntries; + } else { + return Collections.emptyList(); + } + } + + public void setIntFlagEntries(List<IntFlagEntry> intFlagEntries) { + mIntFlagEntries = intFlagEntries; + } + public enum Type { /** Primitive or boxed {@code boolean} */ BOOLEAN, @@ -181,6 +219,7 @@ public final class InspectableClassModel { * An enumeration packed into an {@code int}. * * @see android.view.inspector.IntEnumMapping + * @see IntEnumEntry */ INT_ENUM, @@ -188,8 +227,74 @@ public final class InspectableClassModel { * Non-exclusive or partially-exclusive flags packed into an {@code int}. * * @see android.view.inspector.IntFlagMapping + * @see IntFlagEntry */ INT_FLAG } } + + /** + * Model one entry in a int enum mapping. + * + * @see android.view.inspector.IntEnumMapping + */ + public static final class IntEnumEntry { + private final String mName; + private final int mValue; + + public IntEnumEntry(String name, int value) { + mName = Objects.requireNonNull(name, "Name must not be null"); + mValue = value; + } + + public String getName() { + return mName; + } + + public int getValue() { + return mValue; + } + } + + /** + * Model one entry in an int flag mapping. + * + * @see android.view.inspector.IntFlagMapping + */ + public static final class IntFlagEntry { + private final String mName; + private final int mTarget; + private final int mMask; + + public IntFlagEntry(String name, int target, int mask) { + mName = Objects.requireNonNull(name, "Name must not be null"); + mTarget = target; + mMask = mask; + } + + public IntFlagEntry(String name, int target) { + this(name, target, target); + } + + /** + * Determine if this entry has a bitmask. + * + * @return True if the bitmask and target are different, false otherwise + */ + public boolean hasMask() { + return mTarget != mMask; + } + + public String getName() { + return mName; + } + + public int getTarget() { + return mTarget; + } + + public int getMask() { + return mMask; + } + } } diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java index f666be7a2a61..42ae890640cb 100644 --- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java +++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java @@ -16,13 +16,19 @@ package android.processor.view.inspector; +import android.processor.view.inspector.InspectableClassModel.IntEnumEntry; +import android.processor.view.inspector.InspectableClassModel.IntFlagEntry; import android.processor.view.inspector.InspectableClassModel.Property; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.regex.Pattern; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; @@ -63,6 +69,7 @@ public final class InspectablePropertyProcessor implements ModelProcessor { /** * Set of android and androidx annotation qualified names for colors packed into {@code long}. + * * @see android.annotation.ColorLong */ private static final String[] COLOR_LONG_ANNOTATION_NAMES = { @@ -71,7 +78,7 @@ public final class InspectablePropertyProcessor implements ModelProcessor { /** * @param annotationQualifiedName The qualified name of the annotation to process - * @param processingEnv The processing environment from the parent processor + * @param processingEnv The processing environment from the parent processor */ public InspectablePropertyProcessor( String annotationQualifiedName, @@ -109,8 +116,8 @@ public final class InspectablePropertyProcessor implements ModelProcessor { * Check that an element is shaped like a getter. * * @param element An element that hopefully represents a getter - * @throws ProcessingException if the element isn't a getter * @return An {@link ExecutableElement} that represents a getter method. + * @throws ProcessingException if the element isn't a getter */ private ExecutableElement ensureGetter(Element element) { if (element.getKind() != ElementKind.METHOD) { @@ -144,7 +151,7 @@ public final class InspectablePropertyProcessor implements ModelProcessor { throw new ProcessingException( String.format( "Expected a getter method to take no parameters, " - + "but got %d parameters.", + + "but got %d parameters.", method.getParameters().size()), element); } @@ -167,10 +174,10 @@ public final class InspectablePropertyProcessor implements ModelProcessor { /** * Build a {@link Property} from a getter and an inspectable property annotation. * - * @param getter An element representing the getter to build from + * @param getter An element representing the getter to build from * @param annotation A mirror of an inspectable property-shaped annotation - * @throws ProcessingException If the supplied data is invalid and a property cannot be modeled * @return A property for the getter and annotation + * @throws ProcessingException If the supplied data is invalid and a property cannot be modeled */ private Property buildProperty(ExecutableElement getter, AnnotationMirror annotation) { final String name = mAnnotationUtils @@ -190,16 +197,25 @@ public final class InspectablePropertyProcessor implements ModelProcessor { .typedValueByName("attributeId", Integer.class, getter, annotation) .ifPresent(property::setAttributeId); + switch (property.getType()) { + case INT_ENUM: + property.setIntEnumEntries(processEnumMapping(getter, annotation)); + break; + case INT_FLAG: + property.setIntFlagEntries(processFlagMapping(getter, annotation)); + break; + } + return property; } /** * Determine the property type from the annotation, return type, or context clues. * - * @param getter An element representing the getter to build from + * @param getter An element representing the getter to build from * @param annotation A mirror of an inspectable property-shaped annotation * @return The resolved property type - * @throws ProcessingException If the property type cannot be resolved + * @throws ProcessingException If the property type cannot be resolved or is invalid * @see android.view.inspector.InspectableProperty#valueType() */ private Property.Type determinePropertyType( @@ -213,10 +229,62 @@ public final class InspectablePropertyProcessor implements ModelProcessor { final Property.Type returnType = convertReturnTypeToPropertyType(getter); + final boolean hasColor = hasColorAnnotation(getter); + final Optional<AnnotationValue> enumMapping = + mAnnotationUtils.valueByName("enumMapping", annotation); + final Optional<AnnotationValue> flagMapping = + mAnnotationUtils.valueByName("flagMapping", annotation); + + if (returnType != Property.Type.INT) { + enumMapping.ifPresent(value -> { + throw new ProcessingException( + String.format( + "Can only use enumMapping on int types, got %s.", + returnType.toString().toLowerCase()), + getter, + annotation, + value); + }); + flagMapping.ifPresent(value -> { + throw new ProcessingException( + String.format( + "Can only use flagMapping on int types, got %s.", + returnType.toString().toLowerCase()), + getter, + annotation, + value); + }); + } + switch (valueType) { case "INFERRED": - if (hasColorAnnotation(getter)) { + if (hasColor) { + enumMapping.ifPresent(value -> { + throw new ProcessingException( + "Cannot use enumMapping on a color type.", + getter, + annotation, + value); + }); + flagMapping.ifPresent(value -> { + throw new ProcessingException( + "Cannot use flagMapping on a color type.", + getter, + annotation, + value); + }); return Property.Type.COLOR; + } else if (enumMapping.isPresent()) { + flagMapping.ifPresent(value -> { + throw new ProcessingException( + "Cannot use flagMapping and enumMapping simultaneously.", + getter, + annotation, + value); + }); + return Property.Type.INT_ENUM; + } else if (flagMapping.isPresent()) { + return Property.Type.INT_FLAG; } else { return returnType; } @@ -235,17 +303,14 @@ public final class InspectablePropertyProcessor implements ModelProcessor { annotation); } case "GRAVITY": - if (returnType == Property.Type.INT) { - return Property.Type.GRAVITY; - } else { - throw new ProcessingException( - String.format("Gravity must be an integer, got %s", returnType), - getter, - annotation); - } + requirePackedIntToReturnInt("Gravity", returnType, getter, annotation); + return Property.Type.GRAVITY; case "INT_ENUM": + requirePackedIntToReturnInt("IntEnum", returnType, getter, annotation); + return Property.Type.INT_ENUM; case "INT_FLAG": - throw new ProcessingException("Not implemented", getter, annotation); + requirePackedIntToReturnInt("IntFlag", returnType, getter, annotation); + return Property.Type.INT_FLAG; default: throw new ProcessingException( String.format("Unknown value type enumeration value: %s", valueType), @@ -258,8 +323,8 @@ public final class InspectablePropertyProcessor implements ModelProcessor { * Get a property type from the return type of a getter. * * @param getter The getter to extract the return type of - * @throws ProcessingException If the return type is not a primitive or an object * @return The property type returned by the getter + * @throws ProcessingException If the return type is not a primitive or an object */ private Property.Type convertReturnTypeToPropertyType(ExecutableElement getter) { final TypeMirror returnType = getter.getReturnType(); @@ -295,6 +360,31 @@ public final class InspectablePropertyProcessor implements ModelProcessor { } /** + * Require that a value type packed into an integer be on a getter that returns an int. + * + * @param typeName The name of the type to use in the exception + * @param returnType The return type of the getter to check + * @param getter The getter, to use in the exception + * @param annotation The annotation, to use in the exception + * @throws ProcessingException If the return type is not an int + */ + private static void requirePackedIntToReturnInt( + String typeName, + Property.Type returnType, + ExecutableElement getter, + AnnotationMirror annotation) { + if (returnType != Property.Type.INT) { + throw new ProcessingException( + String.format( + "%s can only be defined on a method that returns int, got %s.", + typeName, + returnType.toString().toLowerCase()), + getter, + annotation); + } + } + + /** * Determine if a getter is annotated with color annotation matching its return type. * * Note that an {@code int} return value annotated with {@link android.annotation.ColorLong} is @@ -303,7 +393,6 @@ public final class InspectablePropertyProcessor implements ModelProcessor { * * @param getter The getter to query * @return True if the getter has a color annotation, false otherwise - * */ private boolean hasColorAnnotation(ExecutableElement getter) { switch (unboxType(getter.getReturnType())) { @@ -353,6 +442,117 @@ public final class InspectablePropertyProcessor implements ModelProcessor { } /** + * Build a model of an {@code int} enumeration mapping from annotation values. + * + * This method only handles the one-to-one mapping of mirrors of + * {@link android.view.inspector.InspectableProperty.EnumMap} annotations into + * {@link IntEnumEntry} objects. Further validation should be handled elsewhere + * + * @see android.view.inspector.IntEnumMapping + * @see android.view.inspector.InspectableProperty#enumMapping() + * @param getter The getter of the property, used for exceptions + * @param annotation The {@link android.view.inspector.InspectableProperty} annotation to + * extract enum mapping values from. + * @return A list of int enum entries, in the order specified in source + * @throws ProcessingException if mapping doesn't exist or is invalid + */ + private List<IntEnumEntry> processEnumMapping( + ExecutableElement getter, + AnnotationMirror annotation) { + List<AnnotationMirror> enumAnnotations = mAnnotationUtils.typedArrayValuesByName( + "enumMapping", AnnotationMirror.class, getter, annotation); + List<IntEnumEntry> enumEntries = new ArrayList<>(enumAnnotations.size()); + + if (enumAnnotations.isEmpty()) { + throw new ProcessingException( + "Encountered an empty array for enumMapping", getter, annotation); + } + + for (AnnotationMirror enumAnnotation : enumAnnotations) { + final String name = mAnnotationUtils.typedValueByName( + "name", String.class, getter, enumAnnotation) + .orElseThrow(() -> { + throw new ProcessingException( + "Name is required for @EnumMap", + getter, + enumAnnotation); + }); + + final int value = mAnnotationUtils.typedValueByName( + "value", Integer.class, getter, enumAnnotation) + .orElseThrow(() -> { + throw new ProcessingException( + "Value is required for @EnumMap", + getter, + enumAnnotation); + }); + + enumEntries.add(new IntEnumEntry(name, value)); + } + + return enumEntries; + } + + /** + * Build a model of an {@code int} flag mapping from annotation values. + * + * This method only handles the one-to-one mapping of mirrors of + * {@link android.view.inspector.InspectableProperty.FlagMap} annotations into + * {@link IntFlagEntry} objects. Further validation should be handled elsewhere + * + * @see android.view.inspector.IntFlagMapping + * @see android.view.inspector.InspectableProperty#flagMapping() + * @param getter The getter of the property, used for exceptions + * @param annotation The {@link android.view.inspector.InspectableProperty} annotation to + * extract flag mapping values from. + * @return A list of int flags entries, in the order specified in source + * @throws ProcessingException if mapping doesn't exist or is invalid + */ + private List<IntFlagEntry> processFlagMapping( + ExecutableElement getter, + AnnotationMirror annotation) { + List<AnnotationMirror> flagAnnotations = mAnnotationUtils.typedArrayValuesByName( + "flagMapping", AnnotationMirror.class, getter, annotation); + List<IntFlagEntry> flagEntries = new ArrayList<>(flagAnnotations.size()); + + if (flagAnnotations.isEmpty()) { + throw new ProcessingException( + "Encountered an empty array for flagMapping", getter, annotation); + } + + for (AnnotationMirror flagAnnotation : flagAnnotations) { + final String name = mAnnotationUtils.typedValueByName( + "name", String.class, getter, flagAnnotation) + .orElseThrow(() -> { + throw new ProcessingException( + "Name is required for @FlagMap", + getter, + flagAnnotation); + }); + + final int target = mAnnotationUtils.typedValueByName( + "target", Integer.class, getter, flagAnnotation) + .orElseThrow(() -> { + throw new ProcessingException( + "Target is required for @FlagMap", + getter, + flagAnnotation); + }); + + final Optional<Integer> mask = mAnnotationUtils.typedValueByName( + "mask", Integer.class, getter, flagAnnotation); + + if (mask.isPresent()) { + flagEntries.add(new IntFlagEntry(name, target, mask.get())); + } else { + flagEntries.add(new IntFlagEntry(name, target)); + } + } + + return flagEntries; + } + + /** * Determine if a {@link TypeMirror} is a boxed or unboxed boolean. * * @param type The type mirror to check diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java index dd4d8f54fb68..7b04645e9f44 100644 --- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java +++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java @@ -16,6 +16,8 @@ package android.processor.view.inspector; +import android.processor.view.inspector.InspectableClassModel.IntEnumEntry; +import android.processor.view.inspector.InspectableClassModel.IntFlagEntry; import android.processor.view.inspector.InspectableClassModel.Property; import com.squareup.javapoet.ClassName; @@ -69,6 +71,18 @@ public final class InspectionCompanionGenerator { "android.view.inspector", "PropertyReader"); /** + * The class name of {@link android.view.inspector.IntEnumMapping}. + */ + private static final ClassName INT_ENUM_MAPPING = ClassName.get( + "android.view.inspector", "IntEnumMapping"); + + /** + * The class name of {@link android.view.inspector.IntFlagMapping}. + */ + private static final ClassName INT_FLAG_MAPPING = ClassName.get( + "android.view.inspector", "IntFlagMapping"); + + /** * The {@code mPropertiesMapped} field. */ private static final FieldSpec M_PROPERTIES_MAPPED = FieldSpec @@ -248,13 +262,13 @@ public final class InspectionCompanionGenerator { final MethodSpec.Builder builder = MethodSpec.methodBuilder("readProperties") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) - .addParameter(model.getClassName(), "inspectable") + .addParameter(model.getClassName(), "node") .addParameter(PROPERTY_READER, "propertyReader") .addCode(generatePropertyMapInitializationCheck()); for (PropertyIdField propertyIdField : propertyIdFields) { builder.addStatement( - "propertyReader.read$L($N, inspectable.$L())", + "propertyReader.read$L($N, node.$L())", methodSuffixForPropertyType(propertyIdField.mProperty.getType()), propertyIdField.mFieldSpec, propertyIdField.mProperty.getGetter()); @@ -286,21 +300,22 @@ public final class InspectionCompanionGenerator { if (property.getAttributeId() == ID_NULL) { builder.add("$L", ID_NULL); } else { - builder.add("$L", String.format("0x%08x", property.getAttributeId())); + builder.add("$L", hexLiteral(property.getAttributeId())); } } switch (property.getType()) { case INT_ENUM: - throw new RuntimeException("IntEnumMapping generation not implemented"); + builder.add(",$W"); + builder.add(generateIntEnumMappingBuilder(property.getIntEnumEntries())); + break; case INT_FLAG: - throw new RuntimeException("IntFlagMapping generation not implemented"); - default: - builder.add(")"); + builder.add(",$W"); + builder.add(generateIntFlagMappingBuilder(property.getIntFlagEntries())); break; } - return builder.build(); + return builder.add(")").build(); } /** @@ -327,6 +342,56 @@ public final class InspectionCompanionGenerator { } /** + * Generate an invocation of {@link android.view.inspector.IntEnumMapping.Builder}. + * + * <pre> + * new IntEnumMapping.Builder() + * .addValue("ONE", 1) + * .build() + * </pre> + * + * @return A codeblock containing the an int enum mapping builder + */ + private CodeBlock generateIntEnumMappingBuilder(List<IntEnumEntry> intEnumEntries) { + final ArrayList<IntEnumEntry> sortedEntries = new ArrayList<>(intEnumEntries); + sortedEntries.sort(Comparator.comparing(IntEnumEntry::getValue)); + + final CodeBlock.Builder builder = CodeBlock.builder() + .add("new $T()$>", INT_ENUM_MAPPING.nestedClass("Builder")); + + for (IntEnumEntry entry : sortedEntries) { + builder.add("\n.addValue($S, $L)", entry.getName(), entry.getValue()); + } + + return builder.add("\n.build()$<").build(); + } + + private CodeBlock generateIntFlagMappingBuilder(List<IntFlagEntry> intFlagEntries) { + final ArrayList<IntFlagEntry> sortedEntries = new ArrayList<>(intFlagEntries); + sortedEntries.sort(Comparator.comparing(IntFlagEntry::getName)); + + final CodeBlock.Builder builder = CodeBlock.builder() + .add("new $T()$>", INT_FLAG_MAPPING.nestedClass("Builder")); + + for (IntFlagEntry entry : sortedEntries) { + if (entry.hasMask()) { + builder.add( + "\n.addFlag($S, $L, $L)", + entry.getName(), + hexLiteral(entry.getTarget()), + hexLiteral(entry.getMask())); + } else { + builder.add( + "\n.addFlag($S, $L)", + entry.getName(), + hexLiteral(entry.getTarget())); + } + } + + return builder.add("\n.build()$<").build(); + } + + /** * Generate the final class name for the inspection companion from the model's class name. * * The generated class is added to the same package as the source class. If the class in the @@ -385,6 +450,10 @@ public final class InspectionCompanionGenerator { } } + private static String hexLiteral(int value) { + return String.format("0x%08x", value); + } + /** * Value class that holds a {@link Property} and a {@link FieldSpec} for that property. */ diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java index 455f5b08e49e..01d94307f871 100644 --- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java +++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java @@ -32,6 +32,7 @@ import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; +import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; @@ -118,6 +119,12 @@ public final class PlatformInspectableProcessor extends AbstractProcessor { break; } + final Set<Modifier> classModifiers = classElement.get().getModifiers(); + + if (classModifiers.contains(Modifier.PRIVATE)) { + fail("Enclosing class cannot be private", element); + } + final InspectableClassModel model = modelMap.computeIfAbsent( classElement.get().getQualifiedName().toString(), k -> new InspectableClassModel(ClassName.get(classElement.get()))); diff --git a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java index b0775dc77f45..f6d8bb0939db 100644 --- a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java +++ b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java @@ -16,11 +16,13 @@ package android.processor.view.inspector; -import android.processor.view.inspector.InspectableClassModel.Property; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.fail; +import android.processor.view.inspector.InspectableClassModel.IntEnumEntry; +import android.processor.view.inspector.InspectableClassModel.IntFlagEntry; +import android.processor.view.inspector.InspectableClassModel.Property; import com.google.common.base.Charsets; import com.google.common.io.Resources; @@ -31,6 +33,7 @@ import org.junit.Test; import java.io.IOException; import java.net.URL; +import java.util.Arrays; import java.util.Optional; /** @@ -40,7 +43,7 @@ public class InspectionCompanionGeneratorTest { private static final String RESOURCE_PATH_TEMPLATE = "android/processor/view/inspector/InspectionCompanionGeneratorTest/%s.java.txt"; private static final ClassName TEST_CLASS_NAME = - ClassName.get("com.android.inspectable", "TestInspectable"); + ClassName.get("com.android.node", "TestNode"); private InspectableClassModel mModel; private InspectionCompanionGenerator mGenerator; @@ -59,7 +62,7 @@ public class InspectionCompanionGeneratorTest { @Test public void testNestedClass() { mModel = new InspectableClassModel( - ClassName.get("com.android.inspectable", "Outer", "Inner")); + ClassName.get("com.android.node", "Outer", "Inner")); assertGeneratedFileEquals("NestedClass"); } @@ -105,6 +108,42 @@ public class InspectionCompanionGeneratorTest { assertGeneratedFileEquals("SuppliedAttributeId"); } + @Test + public void testIntEnum() { + final Property property = new Property( + "intEnumProperty", + "getIntEnumProperty", + Property.Type.INT_ENUM); + + property.setIntEnumEntries(Arrays.asList( + new IntEnumEntry("THREE", 3), + new IntEnumEntry("TWO", 2), + new IntEnumEntry("ONE", 1))); + + mModel.putProperty(property); + + assertGeneratedFileEquals("IntEnum"); + } + + @Test + public void testIntFlag() { + final Property property = new Property( + "intFlag", + "getIntFlag", + Property.Type.INT_FLAG); + + property.setAttributeIdInferrableFromR(false); + property.setIntFlagEntries(Arrays.asList( + new IntFlagEntry("TURBO", 0x1, 0x3), + new IntFlagEntry("OVERDRIVE", 0x2, 0x3), + new IntFlagEntry("WARP", 0x4) + )); + + mModel.putProperty(property); + + assertGeneratedFileEquals("IntFlag"); + } + private Property addProperty(String name, String getter, Property.Type type) { final Property property = new Property(name, getter, type); mModel.putProperty(property); diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntEnum.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntEnum.java.txt new file mode 100644 index 000000000000..764aa8bfbd63 --- /dev/null +++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntEnum.java.txt @@ -0,0 +1,45 @@ +package com.android.node; + +import android.R; +import android.view.inspector.InspectionCompanion; +import android.view.inspector.IntEnumMapping; +import android.view.inspector.PropertyMapper; +import android.view.inspector.PropertyReader; +import java.lang.Override; + +/** + * Inspection companion for {@link TestNode}. + * + * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator} + * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}. + */ +public final class TestNode$$InspectionCompanion implements InspectionCompanion<TestNode> { + /** + * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped. + */ + private boolean mPropertiesMapped = false; + + /** + * Property ID of {@code intEnumProperty}. + */ + private int mIntEnumPropertyId; + + @Override + public void mapProperties(PropertyMapper propertyMapper) { + mIntEnumPropertyId = propertyMapper.mapIntEnum("intEnumProperty", R.attr.intEnumProperty, + new IntEnumMapping.Builder() + .addValue("ONE", 1) + .addValue("TWO", 2) + .addValue("THREE", 3) + .build()); + mPropertiesMapped = true; + } + + @Override + public void readProperties(TestNode node, PropertyReader propertyReader) { + if (!mPropertiesMapped) { + throw new InspectionCompanion.UninitializedPropertyMapException(); + } + propertyReader.readIntEnum(mIntEnumPropertyId, node.getIntEnumProperty()); + } +} diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntFlag.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntFlag.java.txt new file mode 100644 index 000000000000..75f281314965 --- /dev/null +++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntFlag.java.txt @@ -0,0 +1,43 @@ +package com.android.node; + +import android.view.inspector.InspectionCompanion; +import android.view.inspector.IntFlagMapping; +import android.view.inspector.PropertyMapper; +import android.view.inspector.PropertyReader; +import java.lang.Override; + +/** + * Inspection companion for {@link TestNode}. + * + * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator} + * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}. + */ +public final class TestNode$$InspectionCompanion implements InspectionCompanion<TestNode> { + /** + * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped. + */ + private boolean mPropertiesMapped = false; + + /** + * Property ID of {@code intFlag}. + */ + private int mIntFlagId; + + @Override + public void mapProperties(PropertyMapper propertyMapper) { + mIntFlagId = propertyMapper.mapIntFlag("intFlag", 0, new IntFlagMapping.Builder() + .addFlag("OVERDRIVE", 0x00000002, 0x00000003) + .addFlag("TURBO", 0x00000001, 0x00000003) + .addFlag("WARP", 0x00000004) + .build()); + mPropertiesMapped = true; + } + + @Override + public void readProperties(TestNode node, PropertyReader propertyReader) { + if (!mPropertiesMapped) { + throw new InspectionCompanion.UninitializedPropertyMapException(); + } + propertyReader.readIntFlag(mIntFlagId, node.getIntFlag()); + } +} diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt index 2fc242c6cf4c..0cac462fba51 100644 --- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt +++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt @@ -1,4 +1,4 @@ -package com.android.inspectable; +package com.android.node; import android.view.inspector.InspectionCompanion; import android.view.inspector.PropertyMapper; @@ -23,7 +23,7 @@ public final class Outer$Inner$$InspectionCompanion implements InspectionCompani } @Override - public void readProperties(Outer.Inner inspectable, PropertyReader propertyReader) { + public void readProperties(Outer.Inner node, PropertyReader propertyReader) { if (!mPropertiesMapped) { throw new InspectionCompanion.UninitializedPropertyMapException(); } diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt index 23d0f7807aa5..ce0f867d5332 100644 --- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt +++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt @@ -1,4 +1,4 @@ -package com.android.inspectable; +package com.android.node; import android.view.inspector.InspectionCompanion; import android.view.inspector.PropertyMapper; @@ -6,12 +6,12 @@ import android.view.inspector.PropertyReader; import java.lang.Override; /** - * Inspection companion for {@link TestInspectable}. + * Inspection companion for {@link TestNode}. * * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator} * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}. */ -public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> { +public final class TestNode$$InspectionCompanion implements InspectionCompanion<TestNode> { /** * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped. */ @@ -29,10 +29,10 @@ public final class TestInspectable$$InspectionCompanion implements InspectionCom } @Override - public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) { + public void readProperties(TestNode node, PropertyReader propertyReader) { if (!mPropertiesMapped) { throw new InspectionCompanion.UninitializedPropertyMapException(); } - propertyReader.readInt(mNoAttributePropertyId, inspectable.getNoAttributeProperty()); + propertyReader.readInt(mNoAttributePropertyId, node.getNoAttributeProperty()); } } diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt index 11425482ce94..f7357fece2bf 100644 --- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt +++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt @@ -1,4 +1,4 @@ -package com.android.inspectable; +package com.android.node; import android.view.inspector.InspectionCompanion; import android.view.inspector.PropertyMapper; @@ -7,12 +7,12 @@ import java.lang.Override; import java.lang.String; /** - * Inspection companion for {@link TestInspectable}. + * Inspection companion for {@link TestNode}. * * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator} * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}. */ -public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> { +public final class TestNode$$InspectionCompanion implements InspectionCompanion<TestNode> { /** * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped. */ @@ -24,7 +24,7 @@ public final class TestInspectable$$InspectionCompanion implements InspectionCom } @Override - public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) { + public void readProperties(TestNode node, PropertyReader propertyReader) { if (!mPropertiesMapped) { throw new InspectionCompanion.UninitializedPropertyMapException(); } diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt index 57eb08041131..dfc1bce8afcc 100644 --- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt +++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt @@ -1,4 +1,4 @@ -package com.android.inspectable; +package com.android.node; import android.R; import android.view.inspector.InspectionCompanion; @@ -7,12 +7,12 @@ import android.view.inspector.PropertyReader; import java.lang.Override; /** - * Inspection companion for {@link TestInspectable}. + * Inspection companion for {@link TestNode}. * * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator} * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}. */ -public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> { +public final class TestNode$$InspectionCompanion implements InspectionCompanion<TestNode> { /** * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped. */ @@ -90,20 +90,20 @@ public final class TestInspectable$$InspectionCompanion implements InspectionCom } @Override - public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) { + public void readProperties(TestNode node, PropertyReader propertyReader) { if (!mPropertiesMapped) { throw new InspectionCompanion.UninitializedPropertyMapException(); } - propertyReader.readBoolean(mBooleanId, inspectable.getBoolean()); - propertyReader.readByte(mByteId, inspectable.getByte()); - propertyReader.readChar(mCharId, inspectable.getChar()); - propertyReader.readColor(mColorId, inspectable.getColor()); - propertyReader.readDouble(mDoubleId, inspectable.getDouble()); - propertyReader.readFloat(mFloatId, inspectable.getFloat()); - propertyReader.readGravity(mGravityId, inspectable.getGravity()); - propertyReader.readInt(mIntId, inspectable.getInt()); - propertyReader.readLong(mLongId, inspectable.getLong()); - propertyReader.readObject(mObjectId, inspectable.getObject()); - propertyReader.readShort(mShortId, inspectable.getShort()); + propertyReader.readBoolean(mBooleanId, node.getBoolean()); + propertyReader.readByte(mByteId, node.getByte()); + propertyReader.readChar(mCharId, node.getChar()); + propertyReader.readColor(mColorId, node.getColor()); + propertyReader.readDouble(mDoubleId, node.getDouble()); + propertyReader.readFloat(mFloatId, node.getFloat()); + propertyReader.readGravity(mGravityId, node.getGravity()); + propertyReader.readInt(mIntId, node.getInt()); + propertyReader.readLong(mLongId, node.getLong()); + propertyReader.readObject(mObjectId, node.getObject()); + propertyReader.readShort(mShortId, node.getShort()); } } diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt index 6b6ce2157481..d72cdd533205 100644 --- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt +++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt @@ -1,4 +1,4 @@ -package com.android.inspectable; +package com.android.node; import android.view.inspector.InspectionCompanion; import android.view.inspector.PropertyMapper; @@ -6,12 +6,12 @@ import android.view.inspector.PropertyReader; import java.lang.Override; /** - * Inspection companion for {@link TestInspectable}. + * Inspection companion for {@link TestNode}. * * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator} * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}. */ -public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> { +public final class TestNode$$InspectionCompanion implements InspectionCompanion<TestNode> { /** * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped. */ @@ -30,10 +30,10 @@ public final class TestInspectable$$InspectionCompanion implements InspectionCom } @Override - public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) { + public void readProperties(TestNode node, PropertyReader propertyReader) { if (!mPropertiesMapped) { throw new InspectionCompanion.UninitializedPropertyMapException(); } - propertyReader.readInt(mSuppliedAttributePropertyId, inspectable.getSuppliedAttributeProperty()); + propertyReader.readInt(mSuppliedAttributePropertyId, node.getSuppliedAttributeProperty()); } } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 066823931832..e5733edd9803 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1221,7 +1221,6 @@ public class WifiManager { * @param scanResults a list of scanResult that represents the BSSID * @return List that consists of {@link WifiConfiguration} and corresponding scanResults per * network type({@link #PASSPOINT_HOME_NETWORK} and {@link #PASSPOINT_ROAMING_NETWORK}). - * @throws UnsupportedOperationException if Passpoint is not enabled on the device. * @hide */ @SystemApi @@ -1264,7 +1263,6 @@ public class WifiManager { * * @param scanResults a list of ScanResult * @return Map that consists {@link OsuProvider} and a list of matching {@link ScanResult} - * @throws UnsupportedOperationException if Passpoint is not enabled on the device. * @hide */ @SystemApi @@ -1291,7 +1289,6 @@ public class WifiManager { * * @param osuProviders a set of {@link OsuProvider} * @return Map that consists of {@link OsuProvider} and matching {@link PasspointConfiguration}. - * @throws UnsupportedOperationException if Passpoint is not enabled on the device. * @hide */ @SystemApi @@ -1720,8 +1717,8 @@ public class WifiManager { * FQDN, the new configuration will replace the existing configuration. * * @param config The Passpoint configuration to be added - * @throws IllegalArgumentException if configuration is invalid - * @throws UnsupportedOperationException if Passpoint is not enabled on the device. + * @throws IllegalArgumentException if configuration is invalid or Passpoint is not enabled on + * the device. */ public void addOrUpdatePasspointConfiguration(PasspointConfiguration config) { try { @@ -1737,8 +1734,8 @@ public class WifiManager { * Remove the Passpoint configuration identified by its FQDN (Fully Qualified Domain Name). * * @param fqdn The FQDN of the Passpoint configuration to be removed - * @throws IllegalArgumentException if no configuration is associated with the given FQDN. - * @throws UnsupportedOperationException if Passpoint is not enabled on the device. + * @throws IllegalArgumentException if no configuration is associated with the given FQDN or + * Passpoint is not enabled on the device. * @deprecated This is no longer supported. */ @Deprecated @@ -1762,7 +1759,6 @@ public class WifiManager { * An empty list will be returned when no configurations are installed. * * @return A list of {@link PasspointConfiguration} - * @throws UnsupportedOperationException if Passpoint is not enabled on the device. * @deprecated This is no longer supported. */ @Deprecated |