Merge "Add ACQUIRED_START message"
diff --git a/Android.bp b/Android.bp
index 9584767..afdcadd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -562,6 +562,7 @@
"telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl",
"telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl",
"telephony/java/android/telephony/ICellInfoCallback.aidl",
+ "telephony/java/android/telephony/IFinancialSmsCallback.aidl",
"telephony/java/android/telephony/INetworkService.aidl",
"telephony/java/android/telephony/INetworkServiceCallback.aidl",
"telephony/java/com/android/ims/internal/IImsCallSession.aidl",
@@ -827,19 +828,6 @@
}
// 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
@@ -920,6 +908,31 @@
api_dir: "aidl/networkstack",
}
+filegroup {
+ name: "framework-networkstack-shared-srcs",
+ srcs: [
+ // TODO: remove these annotations as soon as we can use andoid.support.annotations.*
+ "core/java/android/annotation/NonNull.java",
+ "core/java/android/annotation/Nullable.java",
+ "core/java/android/annotation/IntDef.java",
+ "core/java/android/annotation/IntRange.java",
+ "core/java/android/annotation/UnsupportedAppUsage.java",
+ "core/java/android/net/DhcpResults.java",
+ "core/java/android/util/LocalLog.java",
+ "core/java/com/android/internal/annotations/VisibleForTesting.java",
+ "core/java/com/android/internal/util/HexDump.java",
+ "core/java/com/android/internal/util/IndentingPrintWriter.java",
+ "core/java/com/android/internal/util/IState.java",
+ "core/java/com/android/internal/util/MessageUtils.java",
+ "core/java/com/android/internal/util/Preconditions.java",
+ "core/java/com/android/internal/util/RingBufferIndices.java",
+ "core/java/com/android/internal/util/State.java",
+ "core/java/com/android/internal/util/StateMachine.java",
+ "core/java/com/android/internal/util/WakeupMessage.java",
+ "core/java/android/net/shared/*.java",
+ ]
+}
+
// Build ext.jar
// ============================================================
java_library {
diff --git a/api/current.txt b/api/current.txt
index 8ad59ff..83b4bb1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3802,6 +3802,7 @@
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 @@
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 @@
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 @@
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;
@@ -23523,6 +23527,8 @@
method public int setBufferSizeInFrames(int);
method public int setLoopPoints(int, int, int);
method public int setNotificationMarkerPosition(int);
+ method public void setOffloadDelayPadding(int, int);
+ method public void setOffloadEndOfStream();
method public int setPlaybackHeadPosition(int);
method public void setPlaybackParams(@NonNull android.media.PlaybackParams);
method public void setPlaybackPositionUpdateListener(android.media.AudioTrack.OnPlaybackPositionUpdateListener);
@@ -25151,6 +25157,8 @@
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
@@ -27679,6 +27687,7 @@
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";
@@ -34406,8 +34415,12 @@
}
public abstract class FileObserver {
- ctor public FileObserver(String);
- ctor public FileObserver(String, int);
+ ctor @Deprecated public FileObserver(String);
+ ctor public FileObserver(@NonNull java.io.File);
+ ctor public FileObserver(@NonNull java.util.List<java.io.File>);
+ ctor @Deprecated public FileObserver(String, int);
+ ctor public FileObserver(@NonNull java.io.File, int);
+ ctor public FileObserver(@NonNull java.util.List<java.io.File>, int);
method protected void finalize();
method public abstract void onEvent(int, @Nullable String);
method public void startWatching();
@@ -38643,6 +38656,7 @@
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";
@@ -38676,6 +38690,7 @@
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";
@@ -41406,9 +41421,9 @@
method public int getUser();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
+ field public static final String KEY_CONTEXTUAL_ACTIONS = "key_contextual_actions";
field public static final String KEY_IMPORTANCE = "key_importance";
- field public static final String KEY_SMART_ACTIONS = "key_smart_actions";
- field public static final String KEY_SMART_REPLIES = "key_smart_replies";
+ field public static final String KEY_TEXT_REPLIES = "key_text_replies";
field public static final String KEY_USER_SENTIMENT = "key_user_sentiment";
}
@@ -44670,6 +44685,7 @@
method public static android.telephony.SmsManager getDefault();
method public static int getDefaultSmsSubscriptionId();
method public static android.telephony.SmsManager getSmsManagerForSubscriptionId(int);
+ method @RequiresPermission(android.Manifest.permission.SMS_FINANCIAL_TRANSACTIONS) public void getSmsMessagesForFinancialApp(android.os.Bundle, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.SmsManager.FinancialSmsCallback);
method public int getSubscriptionId();
method public void injectSmsPdu(byte[], String, android.app.PendingIntent);
method public void sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
@@ -44732,6 +44748,11 @@
field public static final int STATUS_ON_ICC_UNSENT = 7; // 0x7
}
+ public abstract static class SmsManager.FinancialSmsCallback {
+ ctor public SmsManager.FinancialSmsCallback();
+ method public abstract void onFinancialSmsMessages(android.database.CursorWindow);
+ }
+
public class SmsMessage {
method public static int[] calculateLength(CharSequence, boolean);
method public static int[] calculateLength(String, boolean);
@@ -45001,6 +45022,7 @@
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";
@@ -45039,6 +45061,7 @@
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";
@@ -55934,10 +55957,10 @@
method @NonNull public android.widget.Magnifier.Builder setCornerRadius(@Px @FloatRange(from=0) float);
method @NonNull public android.widget.Magnifier.Builder setDefaultSourceToMagnifierOffset(@Px int, @Px int);
method @NonNull public android.widget.Magnifier.Builder setElevation(@Px @FloatRange(from=0) float);
+ method @NonNull public android.widget.Magnifier.Builder setInitialZoom(@FloatRange(from=0.0f) float);
method @NonNull public android.widget.Magnifier.Builder setOverlay(@Nullable android.graphics.drawable.Drawable);
method @NonNull public android.widget.Magnifier.Builder setSize(@Px @IntRange(from=0) int, @Px @IntRange(from=0) int);
method @NonNull public android.widget.Magnifier.Builder setSourceBounds(int, int, int, int);
- method @NonNull public android.widget.Magnifier.Builder setZoom(@FloatRange(from=0.0f) float);
}
public class MediaController extends android.widget.FrameLayout {
diff --git a/api/system-current.txt b/api/system-current.txt
index 9b03461..e0af359 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1267,6 +1267,7 @@
field public static final String CONTEXTHUB_SERVICE = "contexthub";
field public static final String EUICC_CARD_SERVICE = "euicc_card";
field public static final String HDMI_CONTROL_SERVICE = "hdmi_control";
+ field public static final String NETD_SERVICE = "netd";
field public static final String NETWORK_SCORE_SERVICE = "network_score";
field public static final String OEM_LOCK_SERVICE = "oem_lock";
field public static final String PERMISSION_SERVICE = "permission";
@@ -1699,12 +1700,14 @@
method public int describeContents();
method public java.util.List<android.content.rollback.PackageRollbackInfo> getPackages();
method public int getRollbackId();
+ method public int getSessionId();
+ method public boolean isStaged();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.content.rollback.RollbackInfo> CREATOR;
}
public final class RollbackManager {
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void commitRollback(@NonNull android.content.rollback.RollbackInfo, @NonNull android.content.IntentSender);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void commitRollback(int, @NonNull android.content.IntentSender);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void expireRollbackForPackage(@NonNull String);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public java.util.List<android.content.rollback.RollbackInfo> getAvailableRollbacks();
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) @NonNull public java.util.List<android.content.rollback.RollbackInfo> getRecentlyCommittedRollbacks();
@@ -4102,25 +4105,10 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void removeAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
}
- public final class IpSecTransform implements java.lang.AutoCloseable {
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "android.permission.PACKET_KEEPALIVE_OFFLOAD"}) public void startNattKeepalive(@NonNull android.net.IpSecTransform.NattKeepaliveCallback, int, @NonNull android.os.Handler) throws java.io.IOException;
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "android.permission.PACKET_KEEPALIVE_OFFLOAD"}) public void stopNattKeepalive();
- }
-
public static class IpSecTransform.Builder {
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecTransform buildTunnelModeTransform(@NonNull java.net.InetAddress, @NonNull android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
}
- public static class IpSecTransform.NattKeepaliveCallback {
- ctor public IpSecTransform.NattKeepaliveCallback();
- method public void onError(int);
- method public void onStarted();
- method public void onStopped();
- field public static final int ERROR_HARDWARE_ERROR = 3; // 0x3
- field public static final int ERROR_HARDWARE_UNSUPPORTED = 2; // 0x2
- field public static final int ERROR_INVALID_NETWORK = 1; // 0x1
- }
-
public class LinkAddress implements android.os.Parcelable {
ctor public LinkAddress(java.net.InetAddress, int, int, int);
ctor public LinkAddress(java.net.InetAddress, int);
@@ -5120,6 +5108,36 @@
method public Object onTransactStarted(android.os.IBinder, int);
}
+ public class BugreportManager {
+ method @RequiresPermission(android.Manifest.permission.DUMP) public void cancelBugreport();
+ method @RequiresPermission(android.Manifest.permission.DUMP) public void startBugreport(@NonNull android.os.ParcelFileDescriptor, @Nullable android.os.ParcelFileDescriptor, @NonNull android.os.BugreportParams, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback);
+ }
+
+ public abstract static class BugreportManager.BugreportCallback {
+ ctor public BugreportManager.BugreportCallback();
+ method public void onError(int);
+ method public void onFinished();
+ method public void onProgress(float);
+ field public static final int BUGREPORT_ERROR_INVALID_INPUT = 1; // 0x1
+ field public static final int BUGREPORT_ERROR_RUNTIME = 2; // 0x2
+ field public static final int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4; // 0x4
+ field public static final int BUGREPORT_ERROR_USER_DENIED_CONSENT = 3; // 0x3
+ }
+
+ public final class BugreportParams {
+ ctor public BugreportParams(@android.os.BugreportParams.BugreportMode int);
+ method public int getMode();
+ field public static final int BUGREPORT_MODE_FULL = 0; // 0x0
+ field public static final int BUGREPORT_MODE_INTERACTIVE = 1; // 0x1
+ field public static final int BUGREPORT_MODE_REMOTE = 2; // 0x2
+ field public static final int BUGREPORT_MODE_TELEPHONY = 4; // 0x4
+ field public static final int BUGREPORT_MODE_WEAR = 3; // 0x3
+ field public static final int BUGREPORT_MODE_WIFI = 5; // 0x5
+ }
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef(prefix={"BUGREPORT_MODE_"}, value={android.os.BugreportParams.BUGREPORT_MODE_FULL, android.os.BugreportParams.BUGREPORT_MODE_INTERACTIVE, android.os.BugreportParams.BUGREPORT_MODE_REMOTE, android.os.BugreportParams.BUGREPORT_MODE_WEAR, android.os.BugreportParams.BUGREPORT_MODE_TELEPHONY, android.os.BugreportParams.BUGREPORT_MODE_WIFI}) public static @interface BugreportParams.BugreportMode {
+ }
+
public static class Build.VERSION {
field public static final String PREVIEW_SDK_FINGERPRINT;
}
@@ -5725,6 +5743,12 @@
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";
@@ -5733,8 +5757,8 @@
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 {
@@ -7909,7 +7933,7 @@
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();
@@ -7920,9 +7944,9 @@
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;
}
@@ -7936,8 +7960,8 @@
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/cmds/statsd/OWNERS b/cmds/statsd/OWNERS
index 1315750..deebd4e 100644
--- a/cmds/statsd/OWNERS
+++ b/cmds/statsd/OWNERS
@@ -1,6 +1,7 @@
bookatz@google.com
cjyu@google.com
dwchen@google.com
+gaillard@google.com
jinyithu@google.com
joeo@google.com
kwekua@google.com
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 8e56bef..812a2f2 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -24,15 +24,18 @@
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 @@
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 @@
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 @@
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 @@
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/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 92f47e7..e77e212 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1808,6 +1808,29 @@
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 ee3d27c..03a09ee 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -449,6 +449,14 @@
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 @@
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 @@
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 @@
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 @@
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/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index b556033..da45054 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -78,7 +78,7 @@
/**
* 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 @@
* @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 07dbb6b..70badfa 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -105,6 +105,16 @@
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/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index fb65da1..412d7f4 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -277,6 +277,7 @@
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 199c133..9ea39f7 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -94,6 +94,7 @@
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 c4b4b40..621f134 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1094,6 +1094,22 @@
}
/**
+ * 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 75d95b2..55014eb 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -1113,6 +1113,19 @@
/**
* @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/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 2dc225a..ee13164 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -102,6 +102,7 @@
import android.net.IEthernetManager;
import android.net.IIpMemoryStore;
import android.net.IIpSecService;
+import android.net.INetd;
import android.net.INetworkPolicyManager;
import android.net.IpMemoryStore;
import android.net.IpSecManager;
@@ -328,6 +329,14 @@
return new ConnectivityManager(context, service);
}});
+ registerService(Context.NETD_SERVICE, INetd.class, new StaticServiceFetcher<INetd>() {
+ @Override
+ public INetd createService() throws ServiceNotFoundException {
+ return INetd.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.NETD_SERVICE));
+ }
+ });
+
registerService(Context.NETWORK_STACK_SERVICE, NetworkStack.class,
new StaticServiceFetcher<NetworkStack>() {
@Override
diff --git a/core/java/android/app/role/IRoleManager.aidl b/core/java/android/app/role/IRoleManager.aidl
index 2964fbc..cf62e8d 100644
--- a/core/java/android/app/role/IRoleManager.aidl
+++ b/core/java/android/app/role/IRoleManager.aidl
@@ -18,6 +18,8 @@
import android.app.role.IOnRoleHoldersChangedListener;
import android.app.role.IRoleManagerCallback;
+import android.os.Bundle;
+import android.telephony.IFinancialSmsCallback;
/**
* @hide
@@ -52,4 +54,8 @@
List<String> getHeldRolesFromController(in String packageName);
String getDefaultSmsPackage(int userId);
+ /**
+ * Get filtered SMS messages for financial app.
+ */
+ void getSmsMessagesForFinancialApp(in String callingPkg, in Bundle params, in IFinancialSmsCallback callback);
}
diff --git a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
new file mode 100644
index 0000000..4064a02
--- /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 3d3c03a..43ce521 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -287,19 +287,14 @@
/** 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/Context.java b/core/java/android/content/Context.java
index 280f1ac..87f9e46 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3637,6 +3637,16 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.net.INetd} for communicating with the network stack
+ * @hide
+ * @see #getSystemService(String)
+ * @hide
+ */
+ @SystemApi
+ public static final String NETD_SERVICE = "netd";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link NetworkStack} for communicating with the network stack
* @hide
* @see #getSystemService(String)
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index b7366f1..e897b91 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -18,7 +18,6 @@
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 @@
}
/**
- * @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 89630e1..36bb5d4 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -1658,35 +1658,23 @@
* 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 @@
}
private AppUsageLimit(Parcel source) {
- mGroupLimit = source.readBoolean();
mTotalUsageLimit = source.readLong();
mUsageRemaining = source.readLong();
}
@@ -1730,7 +1717,6 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeBoolean(mGroupLimit);
dest.writeLong(mTotalUsageLimit);
dest.writeLong(mUsageRemaining);
}
diff --git a/core/java/android/content/rollback/IRollbackManager.aidl b/core/java/android/content/rollback/IRollbackManager.aidl
index 63d75a0..226d76a 100644
--- a/core/java/android/content/rollback/IRollbackManager.aidl
+++ b/core/java/android/content/rollback/IRollbackManager.aidl
@@ -26,7 +26,7 @@
ParceledListSlice getAvailableRollbacks();
ParceledListSlice getRecentlyExecutedRollbacks();
- void executeRollback(in RollbackInfo rollback, String callerPackageName,
+ void commitRollback(int rollbackId, String callerPackageName,
in IntentSender statusReceiver);
// Exposed for use from the system server only. Callback from the package
diff --git a/core/java/android/content/rollback/RollbackInfo.java b/core/java/android/content/rollback/RollbackInfo.java
index 8532b5a..812f995 100644
--- a/core/java/android/content/rollback/RollbackInfo.java
+++ b/core/java/android/content/rollback/RollbackInfo.java
@@ -17,6 +17,7 @@
package android.content.rollback;
import android.annotation.SystemApi;
+import android.content.pm.PackageInstaller;
import android.os.Parcel;
import android.os.Parcelable;
@@ -38,9 +39,6 @@
private final List<PackageRollbackInfo> mPackages;
- // TODO: Add a flag to indicate if reboot is required, when rollback of
- // staged installs is supported.
-
/** @hide */
public RollbackInfo(int rollbackId, List<PackageRollbackInfo> packages) {
this.mRollbackId = rollbackId;
@@ -66,6 +64,24 @@
return mPackages;
}
+ /**
+ * Returns true if this rollback requires reboot to take effect after
+ * being committed.
+ */
+ public boolean isStaged() {
+ // TODO: Support rollback of staged installs.
+ return false;
+ }
+
+ /**
+ * Returns the session ID for the committed rollback for staged rollbacks.
+ * Only applicable for rollbacks that have been committed.
+ */
+ public int getSessionId() {
+ // TODO: Support rollback of staged installs.
+ return PackageInstaller.SessionInfo.INVALID_ID;
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/content/rollback/RollbackManager.java b/core/java/android/content/rollback/RollbackManager.java
index 2566ac5..f8abcfc 100644
--- a/core/java/android/content/rollback/RollbackManager.java
+++ b/core/java/android/content/rollback/RollbackManager.java
@@ -102,16 +102,15 @@
* <p>
* TODO: Specify the returns status codes.
*
- * @param rollback to commit
+ * @param rollbackId ID of the rollback to commit
* @param statusReceiver where to deliver the results
* @throws SecurityException if the caller does not have the
* MANAGE_ROLLBACKS permission.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
- public void commitRollback(@NonNull RollbackInfo rollback,
- @NonNull IntentSender statusReceiver) {
+ public void commitRollback(int rollbackId, @NonNull IntentSender statusReceiver) {
try {
- mBinder.executeRollback(rollback, mCallerPackageName, statusReceiver);
+ mBinder.commitRollback(rollbackId, mCallerPackageName, statusReceiver);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index d2c0e7b..5d4928c 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -182,6 +182,38 @@
}
/**
+ * 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 @@
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/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 9758308..425b956 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -3721,6 +3721,59 @@
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 c527ab4..7877a4d 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -1119,6 +1119,8 @@
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 @@
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 @@
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 @@
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 dd052a8..a22e008 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -95,13 +95,17 @@
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 @@
StreamConfiguration[] depthConfigurations,
StreamConfigurationDuration[] depthMinFrameDurations,
StreamConfigurationDuration[] depthStallDurations,
+ StreamConfiguration[] dynamicDepthConfigurations,
+ StreamConfigurationDuration[] dynamicDepthMinFrameDurations,
+ StreamConfigurationDuration[] dynamicDepthStallDurations,
HighSpeedVideoConfiguration[] highSpeedVideoConfigurations,
ReprocessFormatsMap inputOutputFormatsMap,
boolean listHighResolution,
@@ -163,6 +170,19 @@
"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 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
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 @@
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 @@
* <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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
int size = formatsMap.size();
if (output) {
size += mDepthOutputFormats.size();
+ size += mDynamicDepthOutputFormats.size();
}
return size;
@@ -1486,10 +1565,11 @@
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 @@
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 @@
(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 @@
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 @@
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 5b3ad77..333cfbd 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -597,6 +597,7 @@
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 @@
? 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 @@
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 @@
}
}
+ /**
+ * 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 @@
// 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/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 23c8aa3..e519fdf 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -257,7 +257,6 @@
*
* @hide
*/
- @SystemApi
public static class NattKeepaliveCallback {
/** The specified {@code Network} is not connected. */
public static final int ERROR_INVALID_NETWORK = 1;
@@ -288,7 +287,6 @@
*
* @hide
*/
- @SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.MANAGE_IPSEC_TUNNELS,
android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
@@ -331,7 +329,6 @@
*
* @hide
*/
- @SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.MANAGE_IPSEC_TUNNELS,
android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index c996d01..39db4fe 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -21,8 +21,10 @@
import android.annotation.NonNull;
import android.annotation.UnsupportedAppUsage;
+import android.net.shared.Inet4AddressUtils;
import android.os.Build;
import android.os.Parcel;
+import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
import android.util.Pair;
@@ -39,8 +41,6 @@
import java.util.Locale;
import java.util.TreeSet;
-import android.system.ErrnoException;
-
/**
* Native methods for managing network interfaces.
*
@@ -177,119 +177,37 @@
FileDescriptor fd) throws IOException;
/**
- * @see #intToInet4AddressHTL(int)
- * @deprecated Use either {@link #intToInet4AddressHTH(int)}
- * or {@link #intToInet4AddressHTL(int)}
+ * @see Inet4AddressUtils#intToInet4AddressHTL(int)
+ * @deprecated Use either {@link Inet4AddressUtils#intToInet4AddressHTH(int)}
+ * or {@link Inet4AddressUtils#intToInet4AddressHTL(int)}
*/
@Deprecated
@UnsupportedAppUsage
public static InetAddress intToInetAddress(int hostAddress) {
- return intToInet4AddressHTL(hostAddress);
+ return Inet4AddressUtils.intToInet4AddressHTL(hostAddress);
}
/**
- * Convert a IPv4 address from an integer to an InetAddress (0x04030201 -> 1.2.3.4)
- *
- * <p>This method uses the higher-order int bytes as the lower-order IPv4 address bytes,
- * which is an unusual convention. Consider {@link #intToInet4AddressHTH(int)} instead.
- * @param hostAddress an int coding for an IPv4 address, where higher-order int byte is
- * lower-order IPv4 address byte
- */
- public static Inet4Address intToInet4AddressHTL(int hostAddress) {
- return intToInet4AddressHTH(Integer.reverseBytes(hostAddress));
- }
-
- /**
- * Convert a IPv4 address from an integer to an InetAddress (0x01020304 -> 1.2.3.4)
- * @param hostAddress an int coding for an IPv4 address
- */
- public static Inet4Address intToInet4AddressHTH(int hostAddress) {
- byte[] addressBytes = { (byte) (0xff & (hostAddress >> 24)),
- (byte) (0xff & (hostAddress >> 16)),
- (byte) (0xff & (hostAddress >> 8)),
- (byte) (0xff & hostAddress) };
-
- try {
- return (Inet4Address) InetAddress.getByAddress(addressBytes);
- } catch (UnknownHostException e) {
- throw new AssertionError();
- }
- }
-
- /**
- * @see #inet4AddressToIntHTL(Inet4Address)
- * @deprecated Use either {@link #inet4AddressToIntHTH(Inet4Address)}
- * or {@link #inet4AddressToIntHTL(Inet4Address)}
+ * @see Inet4AddressUtils#inet4AddressToIntHTL(Inet4Address)
+ * @deprecated Use either {@link Inet4AddressUtils#inet4AddressToIntHTH(Inet4Address)}
+ * or {@link Inet4AddressUtils#inet4AddressToIntHTL(Inet4Address)}
*/
@Deprecated
public static int inetAddressToInt(Inet4Address inetAddr)
throws IllegalArgumentException {
- return inet4AddressToIntHTL(inetAddr);
+ return Inet4AddressUtils.inet4AddressToIntHTL(inetAddr);
}
/**
- * Convert an IPv4 address from an InetAddress to an integer (1.2.3.4 -> 0x01020304)
- *
- * <p>This conversion can help order IP addresses: considering the ordering
- * 192.0.2.1 < 192.0.2.2 < ..., resulting ints will follow that ordering if read as unsigned
- * integers with {@link Integer#toUnsignedLong}.
- * @param inetAddr is an InetAddress corresponding to the IPv4 address
- * @return the IP address as integer
- */
- public static int inet4AddressToIntHTH(Inet4Address inetAddr)
- throws IllegalArgumentException {
- byte [] addr = inetAddr.getAddress();
- return ((addr[0] & 0xff) << 24) | ((addr[1] & 0xff) << 16)
- | ((addr[2] & 0xff) << 8) | (addr[3] & 0xff);
- }
-
- /**
- * Convert a IPv4 address from an InetAddress to an integer (1.2.3.4 -> 0x04030201)
- *
- * <p>This method stores the higher-order IPv4 address bytes in the lower-order int bytes,
- * which is an unusual convention. Consider {@link #inet4AddressToIntHTH(Inet4Address)} instead.
- * @param inetAddr is an InetAddress corresponding to the IPv4 address
- * @return the IP address as integer
- */
- public static int inet4AddressToIntHTL(Inet4Address inetAddr) {
- return Integer.reverseBytes(inet4AddressToIntHTH(inetAddr));
- }
-
- /**
- * @see #prefixLengthToV4NetmaskIntHTL(int)
- * @deprecated Use either {@link #prefixLengthToV4NetmaskIntHTH(int)}
- * or {@link #prefixLengthToV4NetmaskIntHTL(int)}
+ * @see Inet4AddressUtils#prefixLengthToV4NetmaskIntHTL(int)
+ * @deprecated Use either {@link Inet4AddressUtils#prefixLengthToV4NetmaskIntHTH(int)}
+ * or {@link Inet4AddressUtils#prefixLengthToV4NetmaskIntHTL(int)}
*/
@Deprecated
@UnsupportedAppUsage
public static int prefixLengthToNetmaskInt(int prefixLength)
throws IllegalArgumentException {
- return prefixLengthToV4NetmaskIntHTL(prefixLength);
- }
-
- /**
- * Convert a network prefix length to an IPv4 netmask integer (prefixLength 17 -> 0xffff8000)
- * @return the IPv4 netmask as an integer
- */
- public static int prefixLengthToV4NetmaskIntHTH(int prefixLength)
- throws IllegalArgumentException {
- if (prefixLength < 0 || prefixLength > 32) {
- throw new IllegalArgumentException("Invalid prefix length (0 <= prefix <= 32)");
- }
- // (int)a << b is equivalent to a << (b & 0x1f): can't shift by 32 (-1 << 32 == -1)
- return prefixLength == 0 ? 0 : 0xffffffff << (32 - prefixLength);
- }
-
- /**
- * Convert a network prefix length to an IPv4 netmask integer (prefixLength 17 -> 0x0080ffff).
- *
- * <p>This method stores the higher-order IPv4 address bytes in the lower-order int bytes,
- * which is an unusual convention. Consider {@link #prefixLengthToV4NetmaskIntHTH(int)} instead.
- * @return the IPv4 netmask as an integer
- */
- public static int prefixLengthToV4NetmaskIntHTL(int prefixLength)
- throws IllegalArgumentException {
- return Integer.reverseBytes(prefixLengthToV4NetmaskIntHTH(prefixLength));
+ return Inet4AddressUtils.prefixLengthToV4NetmaskIntHTL(prefixLength);
}
/**
@@ -307,17 +225,13 @@
* @return the network prefix length
* @throws IllegalArgumentException the specified netmask was not contiguous.
* @hide
+ * @deprecated use {@link Inet4AddressUtils#netmaskToPrefixLength(Inet4Address)}
*/
@UnsupportedAppUsage
+ @Deprecated
public static int netmaskToPrefixLength(Inet4Address netmask) {
- // inetAddressToInt returns an int in *network* byte order.
- int i = Integer.reverseBytes(inetAddressToInt(netmask));
- int prefixLength = Integer.bitCount(i);
- int trailingZeros = Integer.numberOfTrailingZeros(i);
- if (trailingZeros != 32 - prefixLength) {
- throw new IllegalArgumentException("Non-contiguous netmask: " + Integer.toHexString(i));
- }
- return prefixLength;
+ // This is only here because some apps seem to be using it (@UnsupportedAppUsage).
+ return Inet4AddressUtils.netmaskToPrefixLength(netmask);
}
@@ -408,16 +322,8 @@
*/
@UnsupportedAppUsage
public static int getImplicitNetmask(Inet4Address address) {
- int firstByte = address.getAddress()[0] & 0xff; // Convert to an unsigned value.
- if (firstByte < 128) {
- return 8;
- } else if (firstByte < 192) {
- return 16;
- } else if (firstByte < 224) {
- return 24;
- } else {
- return 32; // Will likely not end well for other reasons.
- }
+ // Only here because it seems to be used by apps
+ return Inet4AddressUtils.getImplicitNetmask(address);
}
/**
@@ -445,28 +351,6 @@
}
/**
- * Get a prefix mask as Inet4Address for a given prefix length.
- *
- * <p>For example 20 -> 255.255.240.0
- */
- public static Inet4Address getPrefixMaskAsInet4Address(int prefixLength)
- throws IllegalArgumentException {
- return intToInet4AddressHTH(prefixLengthToV4NetmaskIntHTH(prefixLength));
- }
-
- /**
- * Get the broadcast address for a given prefix.
- *
- * <p>For example 192.168.0.1/24 -> 192.168.0.255
- */
- public static Inet4Address getBroadcastAddress(Inet4Address addr, int prefixLength)
- throws IllegalArgumentException {
- final int intBroadcastAddr = inet4AddressToIntHTH(addr)
- | ~prefixLengthToV4NetmaskIntHTH(prefixLength);
- return intToInet4AddressHTH(intBroadcastAddr);
- }
-
- /**
* Check if IP address type is consistent between two InetAddress.
* @return true if both are the same type. False otherwise.
*/
diff --git a/core/java/android/net/shared/Inet4AddressUtils.java b/core/java/android/net/shared/Inet4AddressUtils.java
new file mode 100644
index 0000000..bec0c84
--- /dev/null
+++ b/core/java/android/net/shared/Inet4AddressUtils.java
@@ -0,0 +1,166 @@
+/*
+ * 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 java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * Collection of utilities to work with IPv4 addresses.
+ * @hide
+ */
+public class Inet4AddressUtils {
+
+ /**
+ * Convert a IPv4 address from an integer to an InetAddress (0x04030201 -> 1.2.3.4)
+ *
+ * <p>This method uses the higher-order int bytes as the lower-order IPv4 address bytes,
+ * which is an unusual convention. Consider {@link #intToInet4AddressHTH(int)} instead.
+ * @param hostAddress an int coding for an IPv4 address, where higher-order int byte is
+ * lower-order IPv4 address byte
+ */
+ public static Inet4Address intToInet4AddressHTL(int hostAddress) {
+ return intToInet4AddressHTH(Integer.reverseBytes(hostAddress));
+ }
+
+ /**
+ * Convert a IPv4 address from an integer to an InetAddress (0x01020304 -> 1.2.3.4)
+ * @param hostAddress an int coding for an IPv4 address
+ */
+ public static Inet4Address intToInet4AddressHTH(int hostAddress) {
+ byte[] addressBytes = { (byte) (0xff & (hostAddress >> 24)),
+ (byte) (0xff & (hostAddress >> 16)),
+ (byte) (0xff & (hostAddress >> 8)),
+ (byte) (0xff & hostAddress) };
+
+ try {
+ return (Inet4Address) InetAddress.getByAddress(addressBytes);
+ } catch (UnknownHostException e) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * Convert an IPv4 address from an InetAddress to an integer (1.2.3.4 -> 0x01020304)
+ *
+ * <p>This conversion can help order IP addresses: considering the ordering
+ * 192.0.2.1 < 192.0.2.2 < ..., resulting ints will follow that ordering if read as unsigned
+ * integers with {@link Integer#toUnsignedLong}.
+ * @param inetAddr is an InetAddress corresponding to the IPv4 address
+ * @return the IP address as integer
+ */
+ public static int inet4AddressToIntHTH(Inet4Address inetAddr)
+ throws IllegalArgumentException {
+ byte [] addr = inetAddr.getAddress();
+ return ((addr[0] & 0xff) << 24) | ((addr[1] & 0xff) << 16)
+ | ((addr[2] & 0xff) << 8) | (addr[3] & 0xff);
+ }
+
+ /**
+ * Convert a IPv4 address from an InetAddress to an integer (1.2.3.4 -> 0x04030201)
+ *
+ * <p>This method stores the higher-order IPv4 address bytes in the lower-order int bytes,
+ * which is an unusual convention. Consider {@link #inet4AddressToIntHTH(Inet4Address)} instead.
+ * @param inetAddr is an InetAddress corresponding to the IPv4 address
+ * @return the IP address as integer
+ */
+ public static int inet4AddressToIntHTL(Inet4Address inetAddr) {
+ return Integer.reverseBytes(inet4AddressToIntHTH(inetAddr));
+ }
+
+ /**
+ * Convert a network prefix length to an IPv4 netmask integer (prefixLength 17 -> 0xffff8000)
+ * @return the IPv4 netmask as an integer
+ */
+ public static int prefixLengthToV4NetmaskIntHTH(int prefixLength)
+ throws IllegalArgumentException {
+ if (prefixLength < 0 || prefixLength > 32) {
+ throw new IllegalArgumentException("Invalid prefix length (0 <= prefix <= 32)");
+ }
+ // (int)a << b is equivalent to a << (b & 0x1f): can't shift by 32 (-1 << 32 == -1)
+ return prefixLength == 0 ? 0 : 0xffffffff << (32 - prefixLength);
+ }
+
+ /**
+ * Convert a network prefix length to an IPv4 netmask integer (prefixLength 17 -> 0x0080ffff).
+ *
+ * <p>This method stores the higher-order IPv4 address bytes in the lower-order int bytes,
+ * which is an unusual convention. Consider {@link #prefixLengthToV4NetmaskIntHTH(int)} instead.
+ * @return the IPv4 netmask as an integer
+ */
+ public static int prefixLengthToV4NetmaskIntHTL(int prefixLength)
+ throws IllegalArgumentException {
+ return Integer.reverseBytes(prefixLengthToV4NetmaskIntHTH(prefixLength));
+ }
+
+ /**
+ * Convert an IPv4 netmask to a prefix length, checking that the netmask is contiguous.
+ * @param netmask as a {@code Inet4Address}.
+ * @return the network prefix length
+ * @throws IllegalArgumentException the specified netmask was not contiguous.
+ * @hide
+ */
+ public static int netmaskToPrefixLength(Inet4Address netmask) {
+ // inetAddressToInt returns an int in *network* byte order.
+ int i = inet4AddressToIntHTH(netmask);
+ int prefixLength = Integer.bitCount(i);
+ int trailingZeros = Integer.numberOfTrailingZeros(i);
+ if (trailingZeros != 32 - prefixLength) {
+ throw new IllegalArgumentException("Non-contiguous netmask: " + Integer.toHexString(i));
+ }
+ return prefixLength;
+ }
+
+ /**
+ * Returns the implicit netmask of an IPv4 address, as was the custom before 1993.
+ */
+ public static int getImplicitNetmask(Inet4Address address) {
+ int firstByte = address.getAddress()[0] & 0xff; // Convert to an unsigned value.
+ if (firstByte < 128) {
+ return 8;
+ } else if (firstByte < 192) {
+ return 16;
+ } else if (firstByte < 224) {
+ return 24;
+ } else {
+ return 32; // Will likely not end well for other reasons.
+ }
+ }
+
+ /**
+ * Get the broadcast address for a given prefix.
+ *
+ * <p>For example 192.168.0.1/24 -> 192.168.0.255
+ */
+ public static Inet4Address getBroadcastAddress(Inet4Address addr, int prefixLength)
+ throws IllegalArgumentException {
+ final int intBroadcastAddr = inet4AddressToIntHTH(addr)
+ | ~prefixLengthToV4NetmaskIntHTH(prefixLength);
+ return intToInet4AddressHTH(intBroadcastAddr);
+ }
+
+ /**
+ * Get a prefix mask as Inet4Address for a given prefix length.
+ *
+ * <p>For example 20 -> 255.255.240.0
+ */
+ public static Inet4Address getPrefixMaskAsInet4Address(int prefixLength)
+ throws IllegalArgumentException {
+ return intToInet4AddressHTH(prefixLengthToV4NetmaskIntHTH(prefixLength));
+ }
+}
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 6f5f807..3a5b8a8 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.os.IBinder.DeathRecipient;
@@ -35,8 +36,7 @@
*
* @hide
*/
-// TODO: Expose API when the implementation is more complete.
-//@SystemApi
+@SystemApi
@SystemService(Context.BUGREPORT_SERVICE)
public class BugreportManager {
private final Context mContext;
diff --git a/core/java/android/os/BugreportParams.java b/core/java/android/os/BugreportParams.java
index 4e696ae..3871375 100644
--- a/core/java/android/os/BugreportParams.java
+++ b/core/java/android/os/BugreportParams.java
@@ -17,6 +17,7 @@
package android.os;
import android.annotation.IntDef;
+import android.annotation.SystemApi;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -26,8 +27,7 @@
*
* @hide
*/
-// TODO: Expose API when the implementation is more complete.
-// @SystemApi
+@SystemApi
public final class BugreportParams {
private final int mMode;
diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java
index dd85e15..da03895 100644
--- a/core/java/android/os/FileObserver.java
+++ b/core/java/android/os/FileObserver.java
@@ -16,11 +16,15 @@
package android.os;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Log;
+import java.io.File;
import java.lang.ref.WeakReference;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
/**
* Monitors files (using <a href="http://en.wikipedia.org/wiki/Inotify">inotify</a>)
@@ -28,7 +32,7 @@
* the device (including this one). FileObserver is an abstract class;
* subclasses must implement the event handler {@link #onEvent(int, String)}.
*
- * <p>Each FileObserver instance monitors a single file or directory.
+ * <p>Each FileObserver instance can monitor multiple files or directories.
* If a directory is monitored, events will be triggered for all files and
* subdirectories inside the monitored directory.</p>
*
@@ -86,21 +90,32 @@
observe(m_fd);
}
- public int startWatching(String path, int mask, FileObserver observer) {
- int wfd = startWatching(m_fd, path, mask);
+ public int[] startWatching(List<File> files, int mask, FileObserver observer) {
+ final int count = files.size();
+ final String[] paths = new String[count];
+ for (int i = 0; i < count; ++i) {
+ paths[i] = files.get(i).getAbsolutePath();
+ }
+ final int[] wfds = new int[count];
+ Arrays.fill(wfds, -1);
- Integer i = new Integer(wfd);
- if (wfd >= 0) {
- synchronized (m_observers) {
- m_observers.put(i, new WeakReference(observer));
+ startWatching(m_fd, paths, mask, wfds);
+
+ final WeakReference<FileObserver> fileObserverWeakReference =
+ new WeakReference<>(observer);
+ synchronized (m_observers) {
+ for (int wfd : wfds) {
+ if (wfd >= 0) {
+ m_observers.put(wfd, fileObserverWeakReference);
+ }
}
}
- return i;
+ return wfds;
}
- public void stopWatching(int descriptor) {
- stopWatching(m_fd, descriptor);
+ public void stopWatching(int[] descriptors) {
+ stopWatching(m_fd, descriptors);
}
public void onEvent(int wfd, int mask, String path) {
@@ -129,8 +144,8 @@
private native int init();
private native void observe(int fd);
- private native int startWatching(int fd, String path, int mask);
- private native void stopWatching(int fd, int wfd);
+ private native void startWatching(int fd, String[] paths, int mask, int[] wfds);
+ private native void stopWatching(int fd, int[] wfds);
}
private static ObserverThread s_observerThread;
@@ -141,15 +156,34 @@
}
// instance
- private String m_path;
- private Integer m_descriptor;
- private int m_mask;
+ private final List<File> mFiles;
+ private int[] mDescriptors;
+ private final int mMask;
/**
* Equivalent to FileObserver(path, FileObserver.ALL_EVENTS).
+ *
+ * @deprecated use {@link #FileObserver(File)} instead.
*/
+ @Deprecated
public FileObserver(String path) {
- this(path, ALL_EVENTS);
+ this(new File(path));
+ }
+
+ /**
+ * Equivalent to FileObserver(file, FileObserver.ALL_EVENTS).
+ */
+ public FileObserver(@NonNull File file) {
+ this(Arrays.asList(file));
+ }
+
+ /**
+ * Equivalent to FileObserver(paths, FileObserver.ALL_EVENTS).
+ *
+ * @param files The files or directories to monitor
+ */
+ public FileObserver(@NonNull List<File> files) {
+ this(files, ALL_EVENTS);
}
/**
@@ -159,11 +193,36 @@
*
* @param path The file or directory to monitor
* @param mask The event or events (added together) to watch for
+ *
+ * @deprecated use {@link #FileObserver(File, int)} instead.
*/
+ @Deprecated
public FileObserver(String path, int mask) {
- m_path = path;
- m_mask = mask;
- m_descriptor = -1;
+ this(new File(path), mask);
+ }
+
+ /**
+ * Create a new file observer for a certain file or directory.
+ * Monitoring does not start on creation! You must call
+ * {@link #startWatching()} before you will receive events.
+ *
+ * @param file The file or directory to monitor
+ * @param mask The event or events (added together) to watch for
+ */
+ public FileObserver(@NonNull File file, int mask) {
+ this(Arrays.asList(file), mask);
+ }
+
+ /**
+ * Version of {@link #FileObserver(File, int)} that allows callers to monitor
+ * multiple files or directories.
+ *
+ * @param files The files or directories to monitor
+ * @param mask The event or events (added together) to watch for
+ */
+ public FileObserver(@NonNull List<File> files, int mask) {
+ mFiles = files;
+ mMask = mask;
}
protected void finalize() {
@@ -176,8 +235,8 @@
* If monitoring is already started, this call has no effect.
*/
public void startWatching() {
- if (m_descriptor < 0) {
- m_descriptor = s_observerThread.startWatching(m_path, m_mask, this);
+ if (mDescriptors == null) {
+ mDescriptors = s_observerThread.startWatching(mFiles, mMask, this);
}
}
@@ -187,9 +246,9 @@
* monitoring is already stopped, this call has no effect.
*/
public void stopWatching() {
- if (m_descriptor >= 0) {
- s_observerThread.stopWatching(m_descriptor);
- m_descriptor = -1;
+ if (mDescriptors != null) {
+ s_observerThread.stopWatching(mDescriptors);
+ mDescriptors = null;
}
}
diff --git a/core/java/android/os/ParcelUuid.java b/core/java/android/os/ParcelUuid.java
index 2c68ddd..5b45ac2 100644
--- a/core/java/android/os/ParcelUuid.java
+++ b/core/java/android/os/ParcelUuid.java
@@ -16,6 +16,8 @@
package android.os;
+import android.annotation.UnsupportedAppUsage;
+
import java.util.UUID;
/**
@@ -109,6 +111,7 @@
public static final Parcelable.Creator<ParcelUuid> CREATOR =
new Parcelable.Creator<ParcelUuid>() {
+ @UnsupportedAppUsage
public ParcelUuid createFromParcel(Parcel source) {
long mostSigBits = source.readLong();
long leastSigBits = source.readLong();
diff --git a/core/java/android/os/SELinux.java b/core/java/android/os/SELinux.java
index 94441ca..a96618a 100644
--- a/core/java/android/os/SELinux.java
+++ b/core/java/android/os/SELinux.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.UnsupportedAppUsage;
import android.util.Slog;
import java.io.File;
@@ -69,6 +70,7 @@
* @param path the pathname of the file object.
* @return a security context given as a String.
*/
+ @UnsupportedAppUsage
public static final native String getFileContext(String path);
/**
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 148dd91..d58e00a 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -112,14 +112,12 @@
@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 @@
}
/**
+ * 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/Settings.java b/core/java/android/provider/Settings.java
index 43e68e5..927c729 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -486,6 +486,37 @@
"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
@@ -11723,6 +11754,7 @@
* entity_list_default (String[])
* entity_list_not_editable (String[])
* entity_list_editable (String[])
+ * lang_id_threshold_override (float)
* </pre>
*
* <p>
@@ -11767,6 +11799,44 @@
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.
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index b6788f5..93189b3 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -63,15 +63,17 @@
/**
* Data type: ArrayList of {@link android.app.Notification.Action}.
- * Used to suggest extra actions for a notification.
+ * Used to suggest contextual actions for a notification.
+ *
+ * @see Notification.Action.Builder#setContextual(boolean)
*/
- public static final String KEY_SMART_ACTIONS = "key_smart_actions";
+ public static final String KEY_CONTEXTUAL_ACTIONS = "key_contextual_actions";
/**
* Data type: ArrayList of {@link CharSequence}.
* Used to suggest smart replies for a notification.
*/
- public static final String KEY_SMART_REPLIES = "key_smart_replies";
+ public static final String KEY_TEXT_REPLIES = "key_text_replies";
/**
* Data type: int, one of importance values e.g.
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
new file mode 100644
index 0000000..7026d2b
--- /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 2142c36..4f9ecd5 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 @@
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 @@
}
/**
+ * 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 7937cb6..f48318c 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -30,12 +30,12 @@
*/
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 @@
mState = state;
mTransactionSupplier = transactionSupplier;
mController = controller;
- mVisible = InsetsState.getDefaultVisibly(type);
+ mVisible = InsetsState.getDefaultVisibility(type);
}
public void setControl(@Nullable InsetsSourceControl control) {
@@ -76,6 +76,16 @@
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 a6af1a2..4f809fe6 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -278,7 +278,7 @@
}
}
- 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 45e6c50..ecbec65 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -492,7 +492,7 @@
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 a031b70..f47eb10 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2802,6 +2802,11 @@
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 b9017b3..d7f1b9f 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -156,8 +156,7 @@
// 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 @@
Preconditions.checkNotNull(request);
try {
- mService.removeUserData(mContext.getUserId(), request);
+ mService.removeUserData(request);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@@ -227,9 +226,9 @@
/** 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 51aea16..56ed8bf 100644
--- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
@@ -34,21 +34,21 @@
*/
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 @@
* @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 9e99c88..83dbf2d 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -211,8 +211,8 @@
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 @@
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 0cb1800..7fee3ef 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -55,6 +55,7 @@
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 @@
*/
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 @@
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 @@
}
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 @@
.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 @@
// Hook 'em up and let 'er rip.
mCurrentTextBoxAttribute = tba;
+ maybeCallServedViewChangedLocked(tba);
mServedConnecting = false;
if (mServedInputConnectionWrapper != null) {
mServedInputConnectionWrapper.deactivate();
@@ -1730,6 +1770,10 @@
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 @@
}
/**
+ * 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 @@
}
}
+ 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 57ed7f9..7c79d44 100644
--- a/core/java/android/view/inputmethod/InputMethodSystemProperty.java
+++ b/core/java/android/view/inputmethod/InputMethodSystemProperty.java
@@ -93,8 +93,14 @@
* {@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/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index ce680ec..7f928f7 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -46,6 +46,7 @@
* entity_list_default (String[])
* entity_list_not_editable (String[])
* entity_list_editable (String[])
+ * lang_id_threshold_override (float)
* </pre>
*
* <p>
@@ -94,6 +95,8 @@
"in_app_conversation_action_types_default";
private static final String NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT =
"notification_conversation_action_types_default";
+ private static final String LANG_ID_THRESHOLD_OVERRIDE =
+ "lang_id_threshold_override";
private static final boolean LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
private static final boolean SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
@@ -106,8 +109,8 @@
private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000;
private static final int GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT = 100;
- private static final String ENTITY_LIST_DELIMITER = ":";
- private static final String ENTITY_LIST_DEFAULT_VALUE = new StringJoiner(ENTITY_LIST_DELIMITER)
+ private static final String STRING_LIST_DELIMITER = ":";
+ private static final String ENTITY_LIST_DEFAULT_VALUE = new StringJoiner(STRING_LIST_DELIMITER)
.add(TextClassifier.TYPE_ADDRESS)
.add(TextClassifier.TYPE_EMAIL)
.add(TextClassifier.TYPE_PHONE)
@@ -116,7 +119,7 @@
.add(TextClassifier.TYPE_DATE_TIME)
.add(TextClassifier.TYPE_FLIGHT_NUMBER).toString();
private static final String CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES =
- new StringJoiner(ENTITY_LIST_DELIMITER)
+ new StringJoiner(STRING_LIST_DELIMITER)
.add(ConversationAction.TYPE_TEXT_REPLY)
.add(ConversationAction.TYPE_CREATE_REMINDER)
.add(ConversationAction.TYPE_CALL_PHONE)
@@ -127,6 +130,13 @@
.add(ConversationAction.TYPE_VIEW_CALENDAR)
.add(ConversationAction.TYPE_VIEW_MAP)
.toString();
+ /**
+ * < 0 : Not set. Use value from LangId model.
+ * 0 - 1: Override value in LangId model.
+ * > 1 : Effectively turns off the foreign language detection. Scores should never be > 1.
+ * @see EntityConfidence
+ */
+ private static final float LANG_ID_THRESHOLD_OVERRIDE_DEFAULT = -1f;
private final boolean mSystemTextClassifierEnabled;
private final boolean mLocalTextClassifierEnabled;
@@ -144,6 +154,7 @@
private final List<String> mEntityListEditable;
private final List<String> mInAppConversationActionTypesDefault;
private final List<String> mNotificationConversationActionTypesDefault;
+ private final float mLangIdThresholdOverride;
private TextClassificationConstants(@Nullable String settings) {
final KeyValueListParser parser = new KeyValueListParser(',');
@@ -186,21 +197,24 @@
mGenerateLinksLogSampleRate = parser.getInt(
GENERATE_LINKS_LOG_SAMPLE_RATE,
GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT);
- mEntityListDefault = parseEntityList(parser.getString(
+ mEntityListDefault = parseStringList(parser.getString(
ENTITY_LIST_DEFAULT,
ENTITY_LIST_DEFAULT_VALUE));
- mEntityListNotEditable = parseEntityList(parser.getString(
+ mEntityListNotEditable = parseStringList(parser.getString(
ENTITY_LIST_NOT_EDITABLE,
ENTITY_LIST_DEFAULT_VALUE));
- mEntityListEditable = parseEntityList(parser.getString(
+ mEntityListEditable = parseStringList(parser.getString(
ENTITY_LIST_EDITABLE,
ENTITY_LIST_DEFAULT_VALUE));
- mInAppConversationActionTypesDefault = parseEntityList(parser.getString(
+ mInAppConversationActionTypesDefault = parseStringList(parser.getString(
IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT,
CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES));
- mNotificationConversationActionTypesDefault = parseEntityList(parser.getString(
+ mNotificationConversationActionTypesDefault = parseStringList(parser.getString(
NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT,
CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES));
+ mLangIdThresholdOverride = parser.getFloat(
+ LANG_ID_THRESHOLD_OVERRIDE,
+ LANG_ID_THRESHOLD_OVERRIDE_DEFAULT);
}
/** Load from a settings string. */
@@ -272,8 +286,12 @@
return mNotificationConversationActionTypesDefault;
}
- private static List<String> parseEntityList(String listStr) {
- return Collections.unmodifiableList(Arrays.asList(listStr.split(ENTITY_LIST_DELIMITER)));
+ public float getLangIdThresholdOverride() {
+ return mLangIdThresholdOverride;
+ }
+
+ private static List<String> parseStringList(String listStr) {
+ return Collections.unmodifiableList(Arrays.asList(listStr.split(STRING_LIST_DELIMITER)));
}
void dump(IndentingPrintWriter pw) {
@@ -296,6 +314,7 @@
pw.printPair("getInAppConversationActionTypes", mInAppConversationActionTypesDefault);
pw.printPair("getNotificationConversationActionTypes",
mNotificationConversationActionTypesDefault);
+ pw.printPair("getLangIdThresholdOverride", mLangIdThresholdOverride);
pw.decreaseIndent();
pw.println();
}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index a5b7c62..7782079 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -567,8 +567,9 @@
}
}
- // TODO: Make this configurable.
- final float foreignTextThreshold = typeCount == 0 ? 0.5f : 0.7f;
+ final float foreignTextThreshold = mSettings.getLangIdThresholdOverride() >= 0
+ ? mSettings.getLangIdThresholdOverride()
+ : 0.5f /* TODO: Load this from the langId model. */;
boolean isPrimaryAction = true;
final ArrayList<Intent> sourceIntents = new ArrayList<>();
for (LabeledIntent labeledIntent : IntentFactory.create(
@@ -602,6 +603,10 @@
}
private boolean isForeignText(String text, float threshold) {
+ if (threshold > 1) {
+ return false;
+ }
+
// TODO: Revisit this algorithm.
try {
final LangIdModel.LanguageResult[] langResults = getLangIdImpl().detectLanguages(text);
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
index b1609fc..735c3eb 100644
--- a/core/java/android/view/textclassifier/TextLanguage.java
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -89,9 +89,10 @@
/**
* Returns the language locale at the specified index. Locales are ordered from high
* confidence to low confidence.
+ * <p>
+ * See {@link #getLocaleHypothesisCount()} for the number of locales available.
*
* @throws IndexOutOfBoundsException if the specified index is out of range.
- * @see #getLocaleHypothesisCount() for the number of locales available.
*/
@NonNull
public ULocale getLocale(int index) {
@@ -109,7 +110,8 @@
}
/**
- * Returns a bundle containing non-structured extra information about this result.
+ * Returns a bundle containing non-structured extra information about this result. What is
+ * returned in the extras is specific to the {@link TextClassifier} implementation.
*
* <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should prefer
* to hold a reference to the returned bundle rather than frequently calling this method.
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index afe46701..5147306 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -359,7 +359,7 @@
/**
* @return the initial width of the content magnified and copied to the magnifier, in pixels
* @see Magnifier.Builder#setSize(int, int)
- * @see Magnifier.Builder#setZoom(float)
+ * @see Magnifier.Builder#setInitialZoom(float)
*/
@Px
public int getSourceWidth() {
@@ -369,7 +369,7 @@
/**
* @return the initial height of the content magnified and copied to the magnifier, in pixels
* @see Magnifier.Builder#setSize(int, int)
- * @see Magnifier.Builder#setZoom(float)
+ * @see Magnifier.Builder#setInitialZoom(float)
*/
@Px
public int getSourceHeight() {
@@ -394,7 +394,7 @@
* If the zoom is x and the magnifier window size is (width, height), the original size
* of the content being magnified will be (width / x, height / x).
* @return the zoom applied to the content
- * @see Magnifier.Builder#setZoom(float)
+ * @see Magnifier.Builder#setInitialZoom(float)
*/
public float getZoom() {
return mZoom;
@@ -1196,10 +1196,12 @@
* (content_width * zoom, content_height * zoom), which will coincide with the size
* of the magnifier. A zoom of 1 will translate to no magnification (the content will
* be just copied to the magnifier with no scaling). The zoom defaults to 1.25.
+ * Note that the zoom can also be changed after the instance is built, using the
+ * {@link Magnifier#setZoom(float)} method.
* @param zoom the zoom to be set
*/
@NonNull
- public Builder setZoom(@FloatRange(from = 0f) float zoom) {
+ public Builder setInitialZoom(@FloatRange(from = 0f) float zoom) {
Preconditions.checkArgumentPositive(zoom, "Zoom should be positive");
mZoom = zoom;
return this;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 803462d..299801a 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.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 @@
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 @@
// 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 @@
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 @@
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 d0272e0..e27ff00 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 @@
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 8978496..d42c607 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.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 @@
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/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 6a28059..943c726 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -160,4 +160,9 @@
void onBiometricError(String error);
// Used to hide the biometric dialog when the AuthenticationClient is stopped
void hideBiometricDialog();
+
+ /**
+ * Notifies System UI that the display is ready to show system decorations.
+ */
+ void onDisplayReady(int displayId);
}
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index e5ad1f4..7398e95 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -16,6 +16,7 @@
package com.android.internal.util;
+import android.annotation.UnsupportedAppUsage;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -1354,6 +1355,7 @@
* Add a new state to the state machine, parent will be null
* @param state to add
*/
+ @UnsupportedAppUsage
public final void addState(State state) {
mSmHandler.addState(state, null);
}
@@ -1372,6 +1374,7 @@
*
* @param initialState is the state which will receive the first message.
*/
+ @UnsupportedAppUsage
public final void setInitialState(State initialState) {
mSmHandler.setInitialState(initialState);
}
@@ -1410,6 +1413,7 @@
*
* @param destState will be the state that receives the next message.
*/
+ @UnsupportedAppUsage
public final void transitionTo(IState destState) {
mSmHandler.transitionTo(destState);
}
@@ -2053,6 +2057,7 @@
/**
* Start the state machine.
*/
+ @UnsupportedAppUsage
public void start() {
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index 17b2bc4..2cfdaaa 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 @@
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 be12700..9afd5d0 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -166,6 +166,7 @@
"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",
@@ -275,6 +276,7 @@
"libselinux",
"libandroidicu",
"libmedia",
+ "libmedia_helper",
"libmediametrics",
"libmeminfo",
"libaudioclient",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f458299..c90071a 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -107,6 +107,7 @@
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 @@
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_hardware_HardwareBuffer.cpp b/core/jni/android_hardware_HardwareBuffer.cpp
index 8174a41..2d1fec8 100644
--- a/core/jni/android_hardware_HardwareBuffer.cpp
+++ b/core/jni/android_hardware_HardwareBuffer.cpp
@@ -104,6 +104,21 @@
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 @@
(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_media_AudioAttributes.cpp b/core/jni/android_media_AudioAttributes.cpp
new file mode 100644
index 0000000..bfe6162
--- /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 0000000..c558352
--- /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 67c3064..02dffdc2 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 @@
// ----------------------------------------------------------------------------
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 0d8ede7..34abcd8 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 @@
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 @@
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 @@
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 @@
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 @@
"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 1065738..f9f28da 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -23,6 +23,7 @@
#include "core_jni_helpers.h"
#include <utils/Log.h>
+#include <media/AudioParameter.h>
#include <media/AudioSystem.h>
#include <media/AudioTrack.h>
@@ -36,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>
@@ -47,7 +49,6 @@
// ----------------------------------------------------------------------------
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...
@@ -56,14 +57,7 @@
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;
@@ -248,8 +242,6 @@
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);
@@ -303,18 +295,11 @@
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);
@@ -356,7 +341,7 @@
offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK : AudioTrack::TRANSFER_SYNC,
offload ? &offloadInfo : NULL,
-1, -1, // default uid, pid values
- paa);
+ paa.get());
break;
@@ -383,7 +368,7 @@
AudioTrack::TRANSFER_SHARED,
NULL, // default offloadInfo
-1, -1, // default uid, pid values
- paa);
+ paa.get());
break;
default:
@@ -451,20 +436,11 @@
// 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);
}
@@ -1311,6 +1287,33 @@
}
// ----------------------------------------------------------------------------
+static void android_media_AudioTrack_set_delay_padding(JNIEnv *env, jobject thiz,
+ jint delayInFrames, jint paddingInFrames) {
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "AudioTrack not initialized");
+ return;
+ }
+ AudioParameter param = AudioParameter();
+ param.addInt(String8(AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES), (int) delayInFrames);
+ param.addInt(String8(AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES), (int) paddingInFrames);
+ lpTrack->setParameters(param.toString());
+}
+
+static void android_media_AudioTrack_set_eos(JNIEnv *env, jobject thiz) {
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "AudioTrack not initialized");
+ return;
+ }
+ AudioParameter param = AudioParameter();
+ param.addInt(String8("EOS"), 1);
+ lpTrack->setParameters(param.toString());
+}
+
+// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
// name, signature, funcPtr
@@ -1385,6 +1388,8 @@
(void *)android_media_AudioTrack_get_volume_shaper_state},
{"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation},
{"native_getPortId", "()I", (void *)android_media_AudioTrack_get_port_id},
+ {"native_set_delay_padding", "(II)V", (void *)android_media_AudioTrack_set_delay_padding},
+ {"native_set_eos", "()V", (void *)android_media_AudioTrack_set_eos},
};
@@ -1440,17 +1445,6 @@
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_util_FileObserver.cpp b/core/jni/android_util_FileObserver.cpp
index 6f975b2..d25192a 100644
--- a/core/jni/android_util_FileObserver.cpp
+++ b/core/jni/android_util_FileObserver.cpp
@@ -16,6 +16,8 @@
*/
#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
#include "jni.h"
#include "utils/Log.h"
#include "utils/misc.h"
@@ -98,31 +100,45 @@
#endif
}
-static jint android_os_fileobserver_startWatching(JNIEnv* env, jobject object, jint fd, jstring pathString, jint mask)
+static void android_os_fileobserver_startWatching(JNIEnv* env, jobject object, jint fd,
+ jobjectArray pathStrings, jint mask,
+ jintArray wfdArray)
{
- int res = -1;
+ ScopedIntArrayRW wfds(env, wfdArray);
+ if (wfds.get() == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Failed to get ScopedIntArrayRW");
+ }
#if defined(__linux__)
if (fd >= 0)
{
- const char* path = env->GetStringUTFChars(pathString, NULL);
+ size_t count = wfds.size();
+ for (jsize i = 0; i < count; ++i) {
+ jstring pathString = (jstring) env->GetObjectArrayElement(pathStrings, i);
- res = inotify_add_watch(fd, path, mask);
+ ScopedUtfChars path(env, pathString);
- env->ReleaseStringUTFChars(pathString, path);
+ wfds[i] = inotify_add_watch(fd, path.c_str(), mask);
+ }
}
#endif
-
- return res;
}
-static void android_os_fileobserver_stopWatching(JNIEnv* env, jobject object, jint fd, jint wfd)
+static void android_os_fileobserver_stopWatching(JNIEnv* env, jobject object,
+ jint fd, jintArray wfdArray)
{
#if defined(__linux__)
- inotify_rm_watch((int)fd, (uint32_t)wfd);
+ ScopedIntArrayRO wfds(env, wfdArray);
+ if (wfds.get() == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Failed to get ScopedIntArrayRO");
+ }
+ size_t count = wfds.size();
+ for (size_t i = 0; i < count; ++i) {
+ inotify_rm_watch((int)fd, (uint32_t)wfds[i]);
+ }
#endif
}
@@ -131,8 +147,8 @@
/* name, signature, funcPtr */
{ "init", "()I", (void*)android_os_fileobserver_init },
{ "observe", "(I)V", (void*)android_os_fileobserver_observe },
- { "startWatching", "(ILjava/lang/String;I)I", (void*)android_os_fileobserver_startWatching },
- { "stopWatching", "(II)V", (void*)android_os_fileobserver_stopWatching }
+ { "startWatching", "(I[Ljava/lang/String;I[I)V", (void*)android_os_fileobserver_startWatching },
+ { "stopWatching", "(I[I)V", (void*)android_os_fileobserver_stopWatching }
};
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index b8139a7..f9407f4 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -131,6 +131,7 @@
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 @@
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 @@
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_os_AtomicDirectory.cpp b/core/jni/com_android_internal_os_AtomicDirectory.cpp
index 50b2288..76b0fc1 100644
--- a/core/jni/com_android_internal_os_AtomicDirectory.cpp
+++ b/core/jni/com_android_internal_os_AtomicDirectory.cpp
@@ -29,7 +29,7 @@
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 36487b3..984e942 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 @@
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 80cc2d4..3b891d6 100644
--- a/core/proto/Android.bp
+++ b/core/proto/Android.bp
@@ -21,7 +21,10 @@
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 bba8328..f702b3e 100644
--- a/core/proto/android/app/job/enums.proto
+++ b/core/proto/android/app/job/enums.proto
@@ -18,6 +18,9 @@
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/bluetooth/a2dp/enums.proto b/core/proto/android/bluetooth/a2dp/enums.proto
new file mode 100644
index 0000000..5a025bd
--- /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 76c240e..a88a06c 100644
--- a/core/proto/android/bluetooth/enums.proto
+++ b/core/proto/android/bluetooth/enums.proto
@@ -56,3 +56,57 @@
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 e1d96bb..ef894e5 100644
--- a/core/proto/android/bluetooth/hci/enums.proto
+++ b/core/proto/android/bluetooth/hci/enums.proto
@@ -351,7 +351,7 @@
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 @@
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 0000000..c6747b7
--- /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/secure.proto b/core/proto/android/providers/settings/secure.proto
index aaf6c63..f3733fd 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -72,6 +72,9 @@
// List of the accessibility services to which the user has granted
// permission to put the device into touch exploration mode.
optional SettingProto touch_exploration_granted_accessibility_services = 31;
+ // Settings for accessibility timeout
+ optional SettingProto non_interactive_ui_timeout_ms = 32 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto interactive_ui_timeout_ms = 33 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Accessibility accessibility = 2;
diff --git a/core/proto/android/server/job/enums.proto b/core/proto/android/server/job/enums.proto
new file mode 100644
index 0000000..50fc031
--- /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 188769d..6c9d13a 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/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 @@
}
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/packages/SettingsLib/res/drawable/btn_borderless_rect.xml b/core/res/res/drawable/btn_borderless_rect.xml
similarity index 100%
rename from packages/SettingsLib/res/drawable/btn_borderless_rect.xml
rename to 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
similarity index 100%
rename from packages/SettingsLib/res/drawable/ic_bt_cellphone.xml
rename to 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
similarity index 100%
rename from packages/SettingsLib/res/drawable/ic_bt_headphones_a2dp.xml
rename to 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
similarity index 100%
rename from packages/SettingsLib/res/drawable/ic_bt_headset_hfp.xml
rename to 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
similarity index 100%
rename from packages/SettingsLib/res/drawable/ic_bt_hearing_aid.xml
rename to 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
similarity index 100%
rename from packages/SettingsLib/res/drawable/ic_bt_laptop.xml
rename to 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
similarity index 100%
rename from packages/SettingsLib/res/drawable/ic_bt_misc_hid.xml
rename to 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
similarity index 100%
rename from packages/SettingsLib/res/drawable/ic_bt_network_pan.xml
rename to 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
similarity index 100%
rename from packages/SettingsLib/res/drawable/ic_bt_pointing_hid.xml
rename to 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
similarity index 100%
rename from packages/SettingsLib/res/drawable/ic_expand_more.xml
rename to 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
similarity index 100%
rename from packages/SettingsLib/res/drawable/ic_lockscreen_ime.xml
rename to 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
similarity index 100%
rename from packages/SettingsLib/res/drawable/ic_menu.xml
rename to 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
similarity index 100%
rename from packages/SettingsLib/res/drawable/ic_minus.xml
rename to 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
similarity index 100%
rename from packages/SettingsLib/res/drawable/ic_mode_edit.xml
rename to 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
similarity index 100%
rename from packages/SettingsLib/res/drawable/ic_plus.xml
rename to 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
similarity index 100%
rename from packages/SettingsLib/res/drawable/ic_qs_night_display_on.xml
rename to 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
similarity index 100%
rename from packages/SettingsLib/res/drawable/ic_settings_bluetooth.xml
rename to 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
similarity index 100%
rename from packages/SettingsLib/res/drawable/ic_settings_print.xml
rename to 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
similarity index 100%
rename from packages/SettingsLib/res/drawable/ic_signal_location.xml
rename to 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 43b3552..949c12e 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 @@
-->
<!-- 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/symbols.xml b/core/res/res/values/symbols.xml
index f79e22d..47bd9da 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" />
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index 8e8b07a..9d04e63 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -53,7 +53,6 @@
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 bd7f852..2a29f83 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -132,6 +132,9 @@
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,
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 0000000..b07cb99
--- /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 2db2f5f..03af67d 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -155,10 +155,10 @@
@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/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
index 9662182..32bafec 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
@@ -16,9 +16,7 @@
package android.view.textclassifier;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertWithMessage;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -30,6 +28,8 @@
@RunWith(AndroidJUnit4.class)
public class TextClassificationConstantsTest {
+ private static final float EPSILON = 0.0001f;
+
@Test
public void testLoadFromString() {
final String s = "local_textclassifier_enabled=true,"
@@ -42,26 +42,55 @@
+ "suggest_selection_max_range_length=10,"
+ "classify_text_max_range_length=11,"
+ "generate_links_max_text_length=12,"
- + "generate_links_log_sample_rate=13";
+ + "generate_links_log_sample_rate=13,"
+ + "entity_list_default=phone,"
+ + "entity_list_not_editable=address:flight,"
+ + "entity_list_editable=date:datetime,"
+ + "in_app_conversation_action_types_default=text_reply,"
+ + "notification_conversation_action_types_default=send_email:call_phone,"
+ + "lang_id_threshold_override=0.3";
final TextClassificationConstants constants =
TextClassificationConstants.loadFromString(s);
- assertTrue("local_textclassifier_enabled",
- constants.isLocalTextClassifierEnabled());
- assertTrue("system_textclassifier_enabled",
- constants.isSystemTextClassifierEnabled());
- assertTrue("model_dark_launch_enabled", constants.isModelDarkLaunchEnabled());
- assertTrue("smart_selection_enabled", constants.isSmartSelectionEnabled());
- assertTrue("smart_text_share_enabled", constants.isSmartTextShareEnabled());
- assertTrue("smart_linkify_enabled", constants.isSmartLinkifyEnabled());
- assertTrue("smart_select_animation_enabled", constants.isSmartSelectionAnimationEnabled());
- assertEquals("suggest_selection_max_range_length",
- 10, constants.getSuggestSelectionMaxRangeLength());
- assertEquals("classify_text_max_range_length",
- 11, constants.getClassifyTextMaxRangeLength());
- assertEquals("generate_links_max_text_length",
- 12, constants.getGenerateLinksMaxTextLength());
- assertEquals("generate_links_log_sample_rate",
- 13, constants.getGenerateLinksLogSampleRate());
+
+ assertWithMessage("local_textclassifier_enabled")
+ .that(constants.isLocalTextClassifierEnabled()).isTrue();
+ assertWithMessage("system_textclassifier_enabled")
+ .that(constants.isSystemTextClassifierEnabled()).isTrue();
+ assertWithMessage("model_dark_launch_enabled")
+ .that(constants.isModelDarkLaunchEnabled()).isTrue();
+ assertWithMessage("smart_selection_enabled")
+ .that(constants.isSmartSelectionEnabled()).isTrue();
+ assertWithMessage("smart_text_share_enabled")
+ .that(constants.isSmartTextShareEnabled()).isTrue();
+ assertWithMessage("smart_linkify_enabled")
+ .that(constants.isSmartLinkifyEnabled()).isTrue();
+ assertWithMessage("smart_select_animation_enabled")
+ .that(constants.isSmartSelectionAnimationEnabled()).isTrue();
+ assertWithMessage("suggest_selection_max_range_length")
+ .that(constants.getSuggestSelectionMaxRangeLength()).isEqualTo(10);
+ assertWithMessage("classify_text_max_range_length")
+ .that(constants.getClassifyTextMaxRangeLength()).isEqualTo(11);
+ assertWithMessage("generate_links_max_text_length")
+ .that(constants.getGenerateLinksMaxTextLength()).isEqualTo(12);
+ assertWithMessage("generate_links_log_sample_rate")
+ .that(constants.getGenerateLinksLogSampleRate()).isEqualTo(13);
+ assertWithMessage("entity_list_default")
+ .that(constants.getEntityListDefault())
+ .containsExactly("phone");
+ assertWithMessage("entity_list_not_editable")
+ .that(constants.getEntityListNotEditable())
+ .containsExactly("address", "flight");
+ assertWithMessage("entity_list_editable")
+ .that(constants.getEntityListEditable())
+ .containsExactly("date", "datetime");
+ assertWithMessage("in_app_conversation_action_types_default")
+ .that(constants.getInAppConversationActionTypes())
+ .containsExactly("text_reply");
+ assertWithMessage("notification_conversation_action_types_default")
+ .that(constants.getNotificationConversationActionTypes())
+ .containsExactly("send_email", "call_phone");
+ assertWithMessage("lang_id_threshold_override")
+ .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(0.3f);
}
@Test
@@ -76,42 +105,102 @@
+ "suggest_selection_max_range_length=8,"
+ "classify_text_max_range_length=7,"
+ "generate_links_max_text_length=6,"
- + "generate_links_log_sample_rate=5";
+ + "generate_links_log_sample_rate=5,"
+ + "entity_list_default=email:url,"
+ + "entity_list_not_editable=date,"
+ + "entity_list_editable=flight,"
+ + "in_app_conversation_action_types_default=view_map:track_flight,"
+ + "notification_conversation_action_types_default=share_location,"
+ + "lang_id_threshold_override=2";
final TextClassificationConstants constants =
TextClassificationConstants.loadFromString(s);
- assertFalse("local_textclassifier_enabled",
- constants.isLocalTextClassifierEnabled());
- assertFalse("system_textclassifier_enabled",
- constants.isSystemTextClassifierEnabled());
- assertFalse("model_dark_launch_enabled", constants.isModelDarkLaunchEnabled());
- assertFalse("smart_selection_enabled", constants.isSmartSelectionEnabled());
- assertFalse("smart_text_share_enabled", constants.isSmartTextShareEnabled());
- assertFalse("smart_linkify_enabled", constants.isSmartLinkifyEnabled());
- assertFalse("smart_select_animation_enabled",
- constants.isSmartSelectionAnimationEnabled());
- assertEquals("suggest_selection_max_range_length",
- 8, constants.getSuggestSelectionMaxRangeLength());
- assertEquals("classify_text_max_range_length",
- 7, constants.getClassifyTextMaxRangeLength());
- assertEquals("generate_links_max_text_length",
- 6, constants.getGenerateLinksMaxTextLength());
- assertEquals("generate_links_log_sample_rate",
- 5, constants.getGenerateLinksLogSampleRate());
+
+ assertWithMessage("local_textclassifier_enabled")
+ .that(constants.isLocalTextClassifierEnabled()).isFalse();
+ assertWithMessage("system_textclassifier_enabled")
+ .that(constants.isSystemTextClassifierEnabled()).isFalse();
+ assertWithMessage("model_dark_launch_enabled")
+ .that(constants.isModelDarkLaunchEnabled()).isFalse();
+ assertWithMessage("smart_selection_enabled")
+ .that(constants.isSmartSelectionEnabled()).isFalse();
+ assertWithMessage("smart_text_share_enabled")
+ .that(constants.isSmartTextShareEnabled()).isFalse();
+ assertWithMessage("smart_linkify_enabled")
+ .that(constants.isSmartLinkifyEnabled()).isFalse();
+ assertWithMessage("smart_select_animation_enabled")
+ .that(constants.isSmartSelectionAnimationEnabled()).isFalse();
+ assertWithMessage("suggest_selection_max_range_length")
+ .that(constants.getSuggestSelectionMaxRangeLength()).isEqualTo(8);
+ assertWithMessage("classify_text_max_range_length")
+ .that(constants.getClassifyTextMaxRangeLength()).isEqualTo(7);
+ assertWithMessage("generate_links_max_text_length")
+ .that(constants.getGenerateLinksMaxTextLength()).isEqualTo(6);
+ assertWithMessage("generate_links_log_sample_rate")
+ .that(constants.getGenerateLinksLogSampleRate()).isEqualTo(5);
+ assertWithMessage("entity_list_default")
+ .that(constants.getEntityListDefault())
+ .containsExactly("email", "url");
+ assertWithMessage("entity_list_not_editable")
+ .that(constants.getEntityListNotEditable())
+ .containsExactly("date");
+ assertWithMessage("entity_list_editable")
+ .that(constants.getEntityListEditable())
+ .containsExactly("flight");
+ assertWithMessage("in_app_conversation_action_types_default")
+ .that(constants.getInAppConversationActionTypes())
+ .containsExactly("view_map", "track_flight");
+ assertWithMessage("notification_conversation_action_types_default")
+ .that(constants.getNotificationConversationActionTypes())
+ .containsExactly("share_location");
+ assertWithMessage("lang_id_threshold_override")
+ .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(2f);
}
@Test
- public void testEntityListParsing() {
- final TextClassificationConstants constants = TextClassificationConstants.loadFromString(
- "entity_list_default=phone,"
- + "entity_list_not_editable=address:flight,"
- + "entity_list_editable=date:datetime");
- assertEquals(1, constants.getEntityListDefault().size());
- assertEquals("phone", constants.getEntityListDefault().get(0));
- assertEquals(2, constants.getEntityListNotEditable().size());
- assertEquals("address", constants.getEntityListNotEditable().get(0));
- assertEquals("flight", constants.getEntityListNotEditable().get(1));
- assertEquals(2, constants.getEntityListEditable().size());
- assertEquals("date", constants.getEntityListEditable().get(0));
- assertEquals("datetime", constants.getEntityListEditable().get(1));
+ public void testLoadFromString_defaultValues() {
+ final TextClassificationConstants constants =
+ TextClassificationConstants.loadFromString("");
+
+ assertWithMessage("local_textclassifier_enabled")
+ .that(constants.isLocalTextClassifierEnabled()).isTrue();
+ assertWithMessage("system_textclassifier_enabled")
+ .that(constants.isSystemTextClassifierEnabled()).isTrue();
+ assertWithMessage("model_dark_launch_enabled")
+ .that(constants.isModelDarkLaunchEnabled()).isFalse();
+ assertWithMessage("smart_selection_enabled")
+ .that(constants.isSmartSelectionEnabled()).isTrue();
+ assertWithMessage("smart_text_share_enabled")
+ .that(constants.isSmartTextShareEnabled()).isTrue();
+ assertWithMessage("smart_linkify_enabled")
+ .that(constants.isSmartLinkifyEnabled()).isTrue();
+ assertWithMessage("smart_select_animation_enabled")
+ .that(constants.isSmartSelectionAnimationEnabled()).isTrue();
+ assertWithMessage("suggest_selection_max_range_length")
+ .that(constants.getSuggestSelectionMaxRangeLength()).isEqualTo(10 * 1000);
+ assertWithMessage("classify_text_max_range_length")
+ .that(constants.getClassifyTextMaxRangeLength()).isEqualTo(10 * 1000);
+ assertWithMessage("generate_links_max_text_length")
+ .that(constants.getGenerateLinksMaxTextLength()).isEqualTo(100 * 1000);
+ assertWithMessage("generate_links_log_sample_rate")
+ .that(constants.getGenerateLinksLogSampleRate()).isEqualTo(100);
+ assertWithMessage("entity_list_default")
+ .that(constants.getEntityListDefault())
+ .containsExactly("address", "email", "url", "phone", "date", "datetime", "flight");
+ assertWithMessage("entity_list_not_editable")
+ .that(constants.getEntityListNotEditable())
+ .containsExactly("address", "email", "url", "phone", "date", "datetime", "flight");
+ assertWithMessage("entity_list_editable")
+ .that(constants.getEntityListEditable())
+ .containsExactly("address", "email", "url", "phone", "date", "datetime", "flight");
+ assertWithMessage("in_app_conversation_action_types_default")
+ .that(constants.getInAppConversationActionTypes())
+ .containsExactly("text_reply", "create_reminder", "call_phone", "open_url",
+ "send_email", "send_sms", "track_flight", "view_calendar", "view_map");
+ assertWithMessage("notification_conversation_action_types_default")
+ .that(constants.getNotificationConversationActionTypes())
+ .containsExactly("text_reply", "create_reminder", "call_phone", "open_url",
+ "send_email", "send_sms", "track_flight", "view_calendar", "view_map");
+ assertWithMessage("lang_id_threshold_override")
+ .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(-1f);
}
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 904c3fb..1684138 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -87,6 +87,7 @@
<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/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index c580c46..0787d85 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -183,6 +183,14 @@
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 @@
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 25f6775..cb1f69c 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -33,6 +33,7 @@
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.util.Preconditions;
import dalvik.annotation.optimization.CriticalNative;
+import dalvik.system.VMRuntime;
import libcore.util.NativeAllocationRegistry;
@@ -243,7 +245,16 @@
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 072fe73..2ae28f5 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.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 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 @@
}
}
- 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/apex/java/android/media/MediaPlayer2.java b/media/apex/java/android/media/MediaPlayer2.java
index a2feec2..6a90c17 100644
--- a/media/apex/java/android/media/MediaPlayer2.java
+++ b/media/apex/java/android/media/MediaPlayer2.java
@@ -4482,6 +4482,7 @@
};
// Modular DRM
+ private final Map<UUID, MediaDrm> mDrmObjs = new HashMap<>();
private class DrmHandle {
static final int PROVISION_TIMEOUT_MS = 60000;
@@ -4594,7 +4595,13 @@
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);
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 5e77fdf..3440cde 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -996,6 +996,50 @@
}
/**
+ * Configures the delay and padding values for the current compressed stream playing
+ * in offload mode.
+ * This can only be used on a track successfully initialized with
+ * {@link AudioTrack.Builder#setOffloadedPlayback(boolean)}. The unit is frames, where a
+ * frame indicates the number of samples per channel, e.g. 100 frames for a stereo compressed
+ * stream corresponds to 200 decoded interleaved PCM samples.
+ * @param delayInFrames number of frames to be ignored at the beginning of the stream. A value
+ * of 0 indicates no padding is to be applied.
+ * @param paddingInFrames number of frames to be ignored at the end of the stream. A value of 0
+ * of 0 indicates no delay is to be applied.
+ */
+ public void setOffloadDelayPadding(int delayInFrames, int paddingInFrames) {
+ if (paddingInFrames < 0) {
+ throw new IllegalArgumentException("Illegal negative padding");
+ }
+ if (delayInFrames < 0) {
+ throw new IllegalArgumentException("Illegal negative delay");
+ }
+ if (!mOffloaded) {
+ throw new IllegalStateException("Illegal use of delay/padding on non-offloaded track");
+ }
+ if (mState == STATE_UNINITIALIZED) {
+ throw new IllegalStateException("Uninitialized track");
+ }
+ native_set_delay_padding(delayInFrames, paddingInFrames);
+ }
+
+ /**
+ * Declares that the last write() operation on this track provided the last buffer of this
+ * stream.
+ * After the end of stream, previously set padding and delay values are ignored.
+ * Use this method in the same thread as any write() operation.
+ */
+ public void setOffloadEndOfStream() {
+ if (!mOffloaded) {
+ throw new IllegalStateException("EOS not supported on non-offloaded track");
+ }
+ if (mState == STATE_UNINITIALIZED) {
+ throw new IllegalStateException("Uninitialized track");
+ }
+ native_set_eos();
+ }
+
+ /**
* Returns whether direct playback of an audio format with the provided attributes is
* currently supported on the system.
* <p>Direct playback means that the audio stream is not resampled or downmixed
@@ -3457,6 +3501,9 @@
private native int native_getPortId();
+ private native void native_set_delay_padding(int delayInFrames, int paddingInFrames);
+ private native void native_set_eos();
+
//---------------------------------------------------------
// Utility methods
//------------------
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 9ac147b..60ef1d9 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 @@
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 @@
* @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 @@
* 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 @@
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 @@
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 @@
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 2a0e04e..b77a884 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -63,6 +63,7 @@
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 @@
// 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 33b8c42..112ce1d 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -920,12 +920,14 @@
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/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 21c58b3..09b7559 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -20,9 +20,9 @@
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 @@
TYPE_ISDB_T,
TYPE_ISDB_TB,
TYPE_ISDB_S,
+ TYPE_ISDB_S3,
TYPE_ISDB_C,
TYPE_1SEG,
TYPE_DTMB,
@@ -1821,6 +1822,13 @@
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 @@
* {@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 852d296..0fb4d2a 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -94,26 +94,14 @@
],
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 @@
],
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 @@
"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 @@
"libstagefright_player2",
"libstagefright_rtsp_player2",
"libstagefright_timedtext2",
+ "libutils",
"libmedia2_jni_core",
],
@@ -161,6 +161,7 @@
"-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 417a427..7168b2d 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -379,24 +379,9 @@
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);
+ uint64_t consumerUsage =
+ android_hardware_HardwareBuffer_convertToGrallocUsageBits(ndkUsage);
- 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");
- }
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 5ab5092..0340cec 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 @@
// 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 @@
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 @@
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 @@
}
+ 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/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index f346b00..ce58d4e 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -230,10 +230,10 @@
Bundle signals = new Bundle();
if (!smartActions.isEmpty()) {
- signals.putParcelableArrayList(Adjustment.KEY_SMART_ACTIONS, smartActions);
+ signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, smartActions);
}
if (!smartReplies.isEmpty()) {
- signals.putCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES, smartReplies);
+ signals.putCharSequenceArrayList(Adjustment.KEY_TEXT_REPLIES, smartReplies);
}
if (mSettings.mNewInterruptionModel) {
if (mNotificationCategorizer.shouldSilence(entry)) {
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index a2da0a0..9b0d896 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -21,6 +21,7 @@
installable: true,
srcs: [
"src/**/*.java",
+ ":framework-networkstack-shared-srcs",
":services-networkstack-shared-srcs",
],
static_libs: [
diff --git a/packages/NetworkStack/TEST_MAPPING b/packages/NetworkStack/TEST_MAPPING
new file mode 100644
index 0000000..55ba591
--- /dev/null
+++ b/packages/NetworkStack/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "NetworkStackTests"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/NetworkStack/src/android/net/apf/ApfFilter.java b/packages/NetworkStack/src/android/net/apf/ApfFilter.java
index 50c4dfc..08452bb 100644
--- a/packages/NetworkStack/src/android/net/apf/ApfFilter.java
+++ b/packages/NetworkStack/src/android/net/apf/ApfFilter.java
@@ -26,11 +26,6 @@
import static android.system.OsConstants.IPPROTO_UDP;
import static android.system.OsConstants.SOCK_RAW;
-import static com.android.internal.util.BitUtils.bytesToBEInt;
-import static com.android.internal.util.BitUtils.getUint16;
-import static com.android.internal.util.BitUtils.getUint32;
-import static com.android.internal.util.BitUtils.getUint8;
-import static com.android.internal.util.BitUtils.uint32;
import static com.android.server.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE;
import static com.android.server.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
@@ -1586,6 +1581,29 @@
// TODO: move to android.net.NetworkUtils
@VisibleForTesting
public static int ipv4BroadcastAddress(byte[] addrBytes, int prefixLength) {
- return bytesToBEInt(addrBytes) | (int) (uint32(-1) >>> prefixLength);
+ return bytesToBEInt(addrBytes) | (int) (Integer.toUnsignedLong(-1) >>> prefixLength);
+ }
+
+ private static int uint8(byte b) {
+ return b & 0xff;
+ }
+
+ private static int getUint16(ByteBuffer buffer, int position) {
+ return buffer.getShort(position) & 0xffff;
+ }
+
+ private static long getUint32(ByteBuffer buffer, int position) {
+ return Integer.toUnsignedLong(buffer.getInt(position));
+ }
+
+ private static int getUint8(ByteBuffer buffer, int position) {
+ return uint8(buffer.get(position));
+ }
+
+ private static int bytesToBEInt(byte[] bytes) {
+ return (uint8(bytes[0]) << 24)
+ + (uint8(bytes[1]) << 16)
+ + (uint8(bytes[2]) << 8)
+ + (uint8(bytes[3]));
}
}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java
index 04ac9a3..12eecc0 100644
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java
@@ -40,6 +40,8 @@
import static android.system.OsConstants.SO_RCVBUF;
import static android.system.OsConstants.SO_REUSEADDR;
+import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
+
import android.content.Context;
import android.net.DhcpResults;
import android.net.NetworkUtils;
@@ -328,7 +330,7 @@
Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1);
Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_BROADCAST, 1);
Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_RCVBUF, 0);
- Os.bind(mUdpSock, Inet4Address.ANY, DhcpPacket.DHCP_CLIENT);
+ Os.bind(mUdpSock, IPV4_ADDR_ANY, DhcpPacket.DHCP_CLIENT);
} catch(SocketException|ErrnoException e) {
Log.e(TAG, "Error creating UDP socket", e);
return false;
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java b/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java
index 0d298de..0a15cd7 100644
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java
@@ -16,12 +16,13 @@
package android.net.dhcp;
-import static android.net.NetworkUtils.inet4AddressToIntHTH;
-import static android.net.NetworkUtils.intToInet4AddressHTH;
-import static android.net.NetworkUtils.prefixLengthToV4NetmaskIntHTH;
import static android.net.dhcp.DhcpLease.EXPIRATION_NEVER;
import static android.net.dhcp.DhcpLease.inet4AddrToString;
+import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH;
+import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
+import static android.net.shared.Inet4AddressUtils.prefixLengthToV4NetmaskIntHTH;
+import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_BITS;
import static java.lang.Math.min;
@@ -201,7 +202,7 @@
private static boolean isIpAddrOutsidePrefix(@NonNull IpPrefix prefix,
@Nullable Inet4Address addr) {
- return addr != null && !addr.equals(Inet4Address.ANY) && !prefix.contains(addr);
+ return addr != null && !addr.equals(IPV4_ADDR_ANY) && !prefix.contains(addr);
}
@Nullable
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpPacket.java
index ce8b7e7..d7ff98b1 100644
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpPacket.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpPacket.java
@@ -1,10 +1,13 @@
package android.net.dhcp;
+import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ALL;
+import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
+
import android.annotation.Nullable;
import android.net.DhcpResults;
import android.net.LinkAddress;
-import android.net.NetworkUtils;
import android.net.metrics.DhcpErrorEvent;
+import android.net.shared.Inet4AddressUtils;
import android.os.Build;
import android.os.SystemProperties;
import android.system.OsConstants;
@@ -43,8 +46,8 @@
public static final int MINIMUM_LEASE = 60;
public static final int INFINITE_LEASE = (int) 0xffffffff;
- public static final Inet4Address INADDR_ANY = (Inet4Address) Inet4Address.ANY;
- public static final Inet4Address INADDR_BROADCAST = (Inet4Address) Inet4Address.ALL;
+ public static final Inet4Address INADDR_ANY = IPV4_ADDR_ANY;
+ public static final Inet4Address INADDR_BROADCAST = IPV4_ADDR_ALL;
public static final byte[] ETHER_BROADCAST = new byte[] {
(byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0xff,
@@ -1212,9 +1215,9 @@
*/
public DhcpResults toDhcpResults() {
Inet4Address ipAddress = mYourIp;
- if (ipAddress.equals(Inet4Address.ANY)) {
+ if (ipAddress.equals(IPV4_ADDR_ANY)) {
ipAddress = mClientIp;
- if (ipAddress.equals(Inet4Address.ANY)) {
+ if (ipAddress.equals(IPV4_ADDR_ANY)) {
return null;
}
}
@@ -1222,13 +1225,13 @@
int prefixLength;
if (mSubnetMask != null) {
try {
- prefixLength = NetworkUtils.netmaskToPrefixLength(mSubnetMask);
+ prefixLength = Inet4AddressUtils.netmaskToPrefixLength(mSubnetMask);
} catch (IllegalArgumentException e) {
// Non-contiguous netmask.
return null;
}
} else {
- prefixLength = NetworkUtils.getImplicitNetmask(ipAddress);
+ prefixLength = Inet4AddressUtils.getImplicitNetmask(ipAddress);
}
DhcpResults results = new DhcpResults();
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
index 7b112df..beabd3e 100644
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
@@ -16,8 +16,6 @@
package android.net.dhcp;
-import static android.net.NetworkUtils.getBroadcastAddress;
-import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
import static android.net.TrafficStats.TAG_SYSTEM_DHCP_SERVER;
import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
@@ -25,6 +23,8 @@
import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
import static android.net.dhcp.IDhcpServer.STATUS_INVALID_ARGUMENT;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
+import static android.net.shared.Inet4AddressUtils.getBroadcastAddress;
+import static android.net.shared.Inet4AddressUtils.getPrefixMaskAsInet4Address;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.IPPROTO_UDP;
import static android.system.OsConstants.SOCK_DGRAM;
@@ -33,6 +33,8 @@
import static android.system.OsConstants.SO_REUSEADDR;
import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE;
+import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ALL;
+import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
import static java.lang.Integer.toUnsignedLong;
@@ -434,7 +436,7 @@
if (!isEmpty(request.mRelayIp)) {
return request.mRelayIp;
} else if (broadcastFlag) {
- return (Inet4Address) Inet4Address.ALL;
+ return IPV4_ADDR_ALL;
} else if (!isEmpty(request.mClientIp)) {
return request.mClientIp;
} else {
@@ -517,7 +519,7 @@
request.mRelayIp, request.mClientMac, true /* broadcast */, message);
final Inet4Address dst = isEmpty(request.mRelayIp)
- ? (Inet4Address) Inet4Address.ALL
+ ? IPV4_ADDR_ALL
: request.mRelayIp;
return transmitPacket(nakPacket, DhcpNakPacket.class.getSimpleName(), dst);
}
@@ -598,7 +600,7 @@
}
private static boolean isEmpty(@Nullable Inet4Address address) {
- return address == null || Inet4Address.ANY.equals(address);
+ return address == null || IPV4_ADDR_ANY.equals(address);
}
private class PacketListener extends DhcpPacketListener {
@@ -632,7 +634,7 @@
SocketUtils.bindSocketToInterface(mSocket, mIfName);
Os.setsockoptInt(mSocket, SOL_SOCKET, SO_REUSEADDR, 1);
Os.setsockoptInt(mSocket, SOL_SOCKET, SO_BROADCAST, 1);
- Os.bind(mSocket, Inet4Address.ANY, DHCP_SERVER);
+ Os.bind(mSocket, IPV4_ADDR_ANY, DHCP_SERVER);
return mSocket;
} catch (IOException | ErrnoException e) {
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
index 868f3be..31ce95b 100644
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
@@ -16,8 +16,8 @@
package android.net.dhcp;
-import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
-import static android.net.NetworkUtils.intToInet4AddressHTH;
+import static android.net.shared.Inet4AddressUtils.getPrefixMaskAsInet4Address;
+import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE;
import static com.android.server.util.NetworkStackConstants.IPV4_MAX_MTU;
@@ -29,7 +29,7 @@
import android.annotation.Nullable;
import android.net.IpPrefix;
import android.net.LinkAddress;
-import android.net.NetworkUtils;
+import android.net.shared.Inet4AddressUtils;
import android.util.ArraySet;
import java.net.Inet4Address;
@@ -164,7 +164,8 @@
*/
@NonNull
public Inet4Address getBroadcastAddress() {
- return NetworkUtils.getBroadcastAddress(getServerInet4Addr(), serverAddr.getPrefixLength());
+ return Inet4AddressUtils.getBroadcastAddress(
+ getServerInet4Addr(), serverAddr.getPrefixLength());
}
/**
diff --git a/packages/NetworkStack/src/android/net/ip/IpNeighborMonitor.java b/packages/NetworkStack/src/android/net/ip/IpNeighborMonitor.java
index eb993a4..2e6ff24 100644
--- a/packages/NetworkStack/src/android/net/ip/IpNeighborMonitor.java
+++ b/packages/NetworkStack/src/android/net/ip/IpNeighborMonitor.java
@@ -36,8 +36,6 @@
import android.system.OsConstants;
import android.util.Log;
-import com.android.internal.util.BitUtils;
-
import libcore.io.IoUtils;
import java.io.FileDescriptor;
@@ -186,7 +184,7 @@
final int srcPortId = nlMsg.getHeader().nlmsg_pid;
if (srcPortId != 0) {
- mLog.e("non-kernel source portId: " + BitUtils.uint32(srcPortId));
+ mLog.e("non-kernel source portId: " + Integer.toUnsignedLong(srcPortId));
break;
}
diff --git a/packages/NetworkStack/src/android/net/ip/IpReachabilityMonitor.java b/packages/NetworkStack/src/android/net/ip/IpReachabilityMonitor.java
index 761db68..76a0338 100644
--- a/packages/NetworkStack/src/android/net/ip/IpReachabilityMonitor.java
+++ b/packages/NetworkStack/src/android/net/ip/IpReachabilityMonitor.java
@@ -31,15 +31,15 @@
import android.net.netlink.StructNdMsg;
import android.net.util.InterfaceParams;
import android.net.util.SharedLog;
+import android.os.ConditionVariable;
import android.os.Handler;
+import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.SystemClock;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.DumpUtils.Dump;
import java.io.PrintWriter;
import java.net.Inet6Address;
@@ -215,15 +215,20 @@
}
public void dump(PrintWriter pw) {
- DumpUtils.dumpAsync(
- mIpNeighborMonitor.getHandler(),
- new Dump() {
- @Override
- public void dump(PrintWriter pw, String prefix) {
- pw.println(describeWatchList("\n"));
- }
- },
- pw, "", 1000);
+ if (Looper.myLooper() == mIpNeighborMonitor.getHandler().getLooper()) {
+ pw.println(describeWatchList("\n"));
+ return;
+ }
+
+ final ConditionVariable cv = new ConditionVariable(false);
+ mIpNeighborMonitor.getHandler().post(() -> {
+ pw.println(describeWatchList("\n"));
+ cv.open();
+ });
+
+ if (!cv.block(1000)) {
+ pw.println("Timed out waiting for IpReachabilityMonitor dump");
+ }
}
private String describeWatchList() { return describeWatchList(" "); }
diff --git a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
new file mode 100644
index 0000000..6dcf0c0
--- /dev/null
+++ b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
@@ -0,0 +1,30 @@
+/*
+ * 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.util;
+
+/**
+ * Collection of utilities for the network stack.
+ */
+public class NetworkStackUtils {
+
+ /**
+ * @return True if the array is null or 0-length.
+ */
+ public static <T> boolean isEmpty(T[] array) {
+ return array == null || array.length == 0;
+ }
+}
diff --git a/packages/NetworkStack/src/com/android/server/NetworkObserver.java b/packages/NetworkStack/src/com/android/server/NetworkObserver.java
new file mode 100644
index 0000000..d3b40a6
--- /dev/null
+++ b/packages/NetworkStack/src/com/android/server/NetworkObserver.java
@@ -0,0 +1,87 @@
+/*
+ * 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;
+
+import android.net.LinkAddress;
+
+/**
+ * Observer for network events, to use with {@link NetworkObserverRegistry}.
+ */
+public interface NetworkObserver {
+
+ /**
+ * @see android.net.INetdUnsolicitedEventListener#onInterfaceChanged(java.lang.String, boolean)
+ */
+ default void onInterfaceChanged(String ifName, boolean up) {}
+
+ /**
+ * @see android.net.INetdUnsolicitedEventListener#onInterfaceRemoved(String)
+ */
+ default void onInterfaceRemoved(String ifName) {}
+
+ /**
+ * @see android.net.INetdUnsolicitedEventListener
+ * #onInterfaceAddressUpdated(String, String, int, int)
+ */
+ default void onInterfaceAddressUpdated(LinkAddress address, String ifName) {}
+
+ /**
+ * @see android.net.INetdUnsolicitedEventListener
+ * #onInterfaceAddressRemoved(String, String, int, int)
+ */
+ default void onInterfaceAddressRemoved(LinkAddress address, String ifName) {}
+
+ /**
+ * @see android.net.INetdUnsolicitedEventListener#onInterfaceLinkStateChanged(String, boolean)
+ */
+ default void onInterfaceLinkStateChanged(String ifName, boolean up) {}
+
+ /**
+ * @see android.net.INetdUnsolicitedEventListener#onInterfaceAdded(String)
+ */
+ default void onInterfaceAdded(String ifName) {}
+
+ /**
+ * @see android.net.INetdUnsolicitedEventListener
+ * #onInterfaceClassActivityChanged(boolean, int, long, int)
+ */
+ default void onInterfaceClassActivityChanged(
+ boolean isActive, int label, long timestamp, int uid) {}
+
+ /**
+ * @see android.net.INetdUnsolicitedEventListener#onQuotaLimitReached(String, String)
+ */
+ default void onQuotaLimitReached(String alertName, String ifName) {}
+
+ /**
+ * @see android.net.INetdUnsolicitedEventListener
+ * #onInterfaceDnsServerInfo(String, long, String[])
+ */
+ default void onInterfaceDnsServerInfo(String ifName, long lifetime, String[] servers) {}
+
+ /**
+ * @see android.net.INetdUnsolicitedEventListener
+ * #onRouteChanged(boolean, String, String, String)
+ */
+ default void onRouteUpdated(String route, String gateway, String ifName) {}
+
+ /**
+ * @see android.net.INetdUnsolicitedEventListener
+ * #onRouteChanged(boolean, String, String, String)
+ */
+ default void onRouteRemoved(String route, String gateway, String ifName) {}
+}
diff --git a/packages/NetworkStack/src/com/android/server/NetworkObserverRegistry.java b/packages/NetworkStack/src/com/android/server/NetworkObserverRegistry.java
new file mode 100644
index 0000000..14e6c5f
--- /dev/null
+++ b/packages/NetworkStack/src/com/android/server/NetworkObserverRegistry.java
@@ -0,0 +1,150 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.net.INetd;
+import android.net.INetdUnsolicitedEventListener;
+import android.net.LinkAddress;
+import android.os.Handler;
+import android.os.RemoteException;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 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.
+ */
+public class NetworkObserverRegistry extends INetdUnsolicitedEventListener.Stub {
+
+ /**
+ * Constructs a new NetworkObserverRegistry.
+ *
+ * <p>Only one registry should be used per process since netd will silently ignore multiple
+ * registrations from the same process.
+ */
+ NetworkObserverRegistry() {}
+
+ /**
+ * Start listening for Netd events.
+ *
+ * <p>This should be called before allowing any observer to be registered.
+ */
+ void register(@NonNull INetd netd) throws RemoteException {
+ netd.registerUnsolicitedEventListener(this);
+ }
+
+ private final ConcurrentHashMap<NetworkObserver, Handler> mObservers =
+ new ConcurrentHashMap<>();
+
+ /**
+ * Registers the specified observer and start sending callbacks to it.
+ * This method may be called on any thread.
+ */
+ public void registerObserver(@NonNull NetworkObserver observer, @NonNull Handler handler) {
+ mObservers.put(observer, handler);
+ }
+
+ /**
+ * Unregisters the specified observer and stop sending callbacks to it.
+ * This method may be called on any thread.
+ */
+ public void unregisterObserver(@NonNull NetworkObserver observer) {
+ mObservers.remove(observer);
+ }
+
+ @FunctionalInterface
+ private interface NetworkObserverEventCallback {
+ void sendCallback(NetworkObserver o);
+ }
+
+ 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()) {
+ final NetworkObserver observer = entry.getKey();
+ entry.getValue().post(() -> callback.sendCallback(observer));
+ }
+ }
+
+ @Override
+ public void onInterfaceClassActivityChanged(boolean isActive,
+ int label, long timestamp, int uid) {
+ invokeForAllObservers(o -> o.onInterfaceClassActivityChanged(
+ isActive, label, timestamp, uid));
+ }
+
+ /**
+ * Notify our observers of a limit reached.
+ */
+ @Override
+ public void onQuotaLimitReached(String alertName, String ifName) {
+ invokeForAllObservers(o -> o.onQuotaLimitReached(alertName, ifName));
+ }
+
+ @Override
+ public void onInterfaceDnsServerInfo(String ifName, long lifetime, String[] servers) {
+ invokeForAllObservers(o -> o.onInterfaceDnsServerInfo(ifName, lifetime, servers));
+ }
+
+ @Override
+ public void onInterfaceAddressUpdated(String addr, String ifName, int flags, int scope) {
+ final LinkAddress address = new LinkAddress(addr, flags, scope);
+ invokeForAllObservers(o -> o.onInterfaceAddressUpdated(address, ifName));
+ }
+
+ @Override
+ public void onInterfaceAddressRemoved(String addr,
+ String ifName, int flags, int scope) {
+ final LinkAddress address = new LinkAddress(addr, flags, scope);
+ invokeForAllObservers(o -> o.onInterfaceAddressRemoved(address, ifName));
+ }
+
+ @Override
+ public void onInterfaceAdded(String ifName) {
+ invokeForAllObservers(o -> o.onInterfaceAdded(ifName));
+ }
+
+ @Override
+ public void onInterfaceRemoved(String ifName) {
+ invokeForAllObservers(o -> o.onInterfaceRemoved(ifName));
+ }
+
+ @Override
+ public void onInterfaceChanged(String ifName, boolean up) {
+ invokeForAllObservers(o -> o.onInterfaceChanged(ifName, up));
+ }
+
+ @Override
+ public void onInterfaceLinkStateChanged(String ifName, boolean up) {
+ invokeForAllObservers(o -> o.onInterfaceLinkStateChanged(ifName, up));
+ }
+
+ @Override
+ public void onRouteChanged(boolean updated, String route, String gateway, String ifName) {
+ if (updated) {
+ invokeForAllObservers(o -> o.onRouteUpdated(route, gateway, ifName));
+ } else {
+ invokeForAllObservers(o -> o.onRouteRemoved(route, gateway, ifName));
+ }
+ }
+
+ @Override
+ public void onStrictCleartextDetected(int uid, String hex) {}
+}
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
index 4080ddf..631ee45 100644
--- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java
+++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
@@ -29,6 +29,7 @@
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
+import android.net.INetd;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
import android.net.INetworkStackConnector;
@@ -65,6 +66,7 @@
*/
public class NetworkStackService extends Service {
private static final String TAG = NetworkStackService.class.getSimpleName();
+ private static NetworkStackConnector sConnector;
/**
* Create a binder connector for the system server to communicate with the network stack.
@@ -72,8 +74,11 @@
* <p>On platforms where the network stack runs in the system server process, this method may
* be called directly instead of obtaining the connector by binding to the service.
*/
- public static IBinder makeConnector(Context context) {
- return new NetworkStackConnector(context);
+ public static synchronized IBinder makeConnector(Context context) {
+ if (sConnector == null) {
+ sConnector = new NetworkStackConnector(context);
+ }
+ return sConnector;
}
@NonNull
@@ -85,6 +90,8 @@
private static class NetworkStackConnector extends INetworkStackConnector.Stub {
private static final int NUM_VALIDATION_LOG_LINES = 20;
private final Context mContext;
+ private final INetd mNetd;
+ private final NetworkObserverRegistry mObserverRegistry;
private final ConnectivityManager mCm;
@GuardedBy("mIpClients")
private final ArrayList<WeakReference<IpClient>> mIpClients = new ArrayList<>();
@@ -106,7 +113,11 @@
NetworkStackConnector(Context context) {
mContext = context;
+ mNetd = (INetd) context.getSystemService(Context.NETD_SERVICE);
+ mObserverRegistry = new NetworkObserverRegistry();
mCm = context.getSystemService(ConnectivityManager.class);
+
+ // TODO: call mObserverRegistry here after adding sepolicy changes
}
@NonNull
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index 0cca778..96eaa50 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -31,6 +31,7 @@
import static android.net.metrics.ValidationProbeEvent.DNS_SUCCESS;
import static android.net.metrics.ValidationProbeEvent.PROBE_FALLBACK;
import static android.net.metrics.ValidationProbeEvent.PROBE_PRIVDNS;
+import static android.net.util.NetworkStackUtils.isEmpty;
import android.annotation.Nullable;
import android.app.PendingIntent;
@@ -80,7 +81,6 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.RingBufferIndices;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
@@ -1173,7 +1173,7 @@
}
private CaptivePortalProbeSpec nextFallbackSpec() {
- if (ArrayUtils.isEmpty(mCaptivePortalFallbackSpecs)) {
+ if (isEmpty(mCaptivePortalFallbackSpecs)) {
return null;
}
// Randomly change spec without memory. Also randomize the first attempt.
diff --git a/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java b/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java
index eedaf30..804765e 100644
--- a/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java
+++ b/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java
@@ -16,6 +16,10 @@
package com.android.server.util;
+import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
+
+import java.net.Inet4Address;
+
/**
* Network constants used by the network stack.
*/
@@ -79,6 +83,8 @@
public static final int IPV4_SRC_ADDR_OFFSET = 12;
public static final int IPV4_DST_ADDR_OFFSET = 16;
public static final int IPV4_ADDR_LEN = 4;
+ public static final Inet4Address IPV4_ADDR_ALL = intToInet4AddressHTH(0xffffffff);
+ public static final Inet4Address IPV4_ADDR_ANY = intToInet4AddressHTH(0x0);
/**
* IPv6 constants.
diff --git a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java
index 51d50d9..4abd77e 100644
--- a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java
+++ b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java
@@ -21,6 +21,8 @@
import static android.net.dhcp.DhcpLeaseRepository.CLIENTID_UNSPEC;
import static android.net.dhcp.DhcpLeaseRepository.INETADDR_UNSPEC;
+import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -55,7 +57,6 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class DhcpLeaseRepositoryTest {
- private static final Inet4Address INET4_ANY = (Inet4Address) Inet4Address.ANY;
private static final Inet4Address TEST_DEF_ROUTER = parseAddr4("192.168.42.247");
private static final Inet4Address TEST_SERVER_ADDR = parseAddr4("192.168.42.241");
private static final Inet4Address TEST_RESERVED_ADDR = parseAddr4("192.168.42.243");
@@ -108,7 +109,7 @@
MacAddress newMac = MacAddress.fromBytes(hwAddrBytes);
final String hostname = "host_" + i;
final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, newMac,
- INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, hostname);
+ IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, hostname);
assertNotNull(lease);
assertEquals(newMac, lease.getHwAddr());
@@ -130,7 +131,7 @@
try {
mRepo.getOffer(null, TEST_MAC_2,
- INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
fail("Should be out of addresses");
} catch (DhcpLeaseRepository.OutOfAddressesException e) {
// Expected
@@ -181,11 +182,11 @@
public void testGetOffer_StableAddress() throws Exception {
for (final MacAddress macAddr : new MacAddress[] { TEST_MAC_1, TEST_MAC_2, TEST_MAC_3 }) {
final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, macAddr,
- INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
// Same lease is offered twice
final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, macAddr,
- INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
assertEquals(lease, newLease);
}
}
@@ -196,7 +197,7 @@
mRepo.updateParams(newPrefix, TEST_EXCL_SET, TEST_LEASE_TIME_MS);
DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
- INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
assertTrue(newPrefix.contains(lease.getNetAddr()));
}
@@ -205,7 +206,7 @@
requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1, TEST_HOSTNAME_1);
DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
- INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
assertEquals(TEST_INETADDR_1, offer.getNetAddr());
assertEquals(TEST_HOSTNAME_1, offer.getHostname());
}
@@ -213,12 +214,13 @@
@Test
public void testGetOffer_ClientIdHasExistingLease() throws Exception {
final byte[] clientId = new byte[] { 1, 2 };
- mRepo.requestLease(clientId, TEST_MAC_1, INET4_ANY /* clientAddr */,
- INET4_ANY /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, false, TEST_HOSTNAME_1);
+ mRepo.requestLease(clientId, TEST_MAC_1, IPV4_ADDR_ANY /* clientAddr */,
+ IPV4_ADDR_ANY /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, false,
+ TEST_HOSTNAME_1);
// Different MAC, but same clientId
DhcpLease offer = mRepo.getOffer(clientId, TEST_MAC_2,
- INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
assertEquals(TEST_INETADDR_1, offer.getNetAddr());
assertEquals(TEST_HOSTNAME_1, offer.getHostname());
}
@@ -227,12 +229,13 @@
public void testGetOffer_DifferentClientId() throws Exception {
final byte[] clientId1 = new byte[] { 1, 2 };
final byte[] clientId2 = new byte[] { 3, 4 };
- mRepo.requestLease(clientId1, TEST_MAC_1, INET4_ANY /* clientAddr */,
- INET4_ANY /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, false, TEST_HOSTNAME_1);
+ mRepo.requestLease(clientId1, TEST_MAC_1, IPV4_ADDR_ANY /* clientAddr */,
+ IPV4_ADDR_ANY /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, false,
+ TEST_HOSTNAME_1);
// Same MAC, different client ID
DhcpLease offer = mRepo.getOffer(clientId2, TEST_MAC_1,
- INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
// Obtains a different address
assertNotEquals(TEST_INETADDR_1, offer.getNetAddr());
assertEquals(HOSTNAME_NONE, offer.getHostname());
@@ -241,7 +244,7 @@
@Test
public void testGetOffer_RequestedAddress() throws Exception {
- DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY /* relayAddr */,
+ DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */,
TEST_INETADDR_1 /* reqAddr */, TEST_HOSTNAME_1);
assertEquals(TEST_INETADDR_1, offer.getNetAddr());
assertEquals(TEST_HOSTNAME_1, offer.getHostname());
@@ -250,14 +253,14 @@
@Test
public void testGetOffer_RequestedAddressInUse() throws Exception {
requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
- DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_2, INET4_ANY /* relayAddr */,
+ DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_2, IPV4_ADDR_ANY /* relayAddr */,
TEST_INETADDR_1 /* reqAddr */, HOSTNAME_NONE);
assertNotEquals(TEST_INETADDR_1, offer.getNetAddr());
}
@Test
public void testGetOffer_RequestedAddressReserved() throws Exception {
- DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY /* relayAddr */,
+ DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */,
TEST_RESERVED_ADDR /* reqAddr */, HOSTNAME_NONE);
assertNotEquals(TEST_RESERVED_ADDR, offer.getNetAddr());
}
@@ -265,7 +268,7 @@
@Test
public void testGetOffer_RequestedAddressInvalid() throws Exception {
final Inet4Address invalidAddr = parseAddr4("192.168.42.0");
- DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY /* relayAddr */,
+ DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */,
invalidAddr /* reqAddr */, HOSTNAME_NONE);
assertNotEquals(invalidAddr, offer.getNetAddr());
}
@@ -273,7 +276,7 @@
@Test
public void testGetOffer_RequestedAddressOutsideSubnet() throws Exception {
final Inet4Address invalidAddr = parseAddr4("192.168.254.2");
- DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY /* relayAddr */,
+ DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */,
invalidAddr /* reqAddr */, HOSTNAME_NONE);
assertNotEquals(invalidAddr, offer.getNetAddr());
}
@@ -322,7 +325,7 @@
@Test(expected = DhcpLeaseRepository.InvalidSubnetException.class)
public void testRequestLease_SelectingRelayInInvalidSubnet() throws Exception {
- mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY /* clientAddr */,
+ mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* clientAddr */,
parseAddr4("192.168.128.1") /* relayAddr */, TEST_INETADDR_1 /* reqAddr */,
true /* sidSet */, HOSTNAME_NONE);
}
@@ -419,14 +422,14 @@
public void testReleaseLease_StableOffer() throws Exception {
for (MacAddress mac : new MacAddress[] { TEST_MAC_1, TEST_MAC_2, TEST_MAC_3 }) {
final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, mac,
- INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
requestLeaseSelecting(mac, lease.getNetAddr());
mRepo.releaseLease(CLIENTID_UNSPEC, mac, lease.getNetAddr());
// Same lease is offered after it was released
final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, mac,
- INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
assertEquals(lease.getNetAddr(), newLease.getNetAddr());
}
}
@@ -434,13 +437,13 @@
@Test
public void testMarkLeaseDeclined() throws Exception {
final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
- INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
mRepo.markLeaseDeclined(lease.getNetAddr());
// Same lease is not offered again
final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
- INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
assertNotEquals(lease.getNetAddr(), newLease.getNetAddr());
}
@@ -457,16 +460,16 @@
// Last 2 addresses: addresses marked declined should be used
final DhcpLease firstLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
- INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_1);
+ IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_1);
requestLeaseSelecting(TEST_MAC_1, firstLease.getNetAddr());
final DhcpLease secondLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_2,
- INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_2);
+ IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_2);
requestLeaseSelecting(TEST_MAC_2, secondLease.getNetAddr());
// Now out of addresses
try {
- mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_3, INET4_ANY /* relayAddr */,
+ mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_3, IPV4_ADDR_ANY /* relayAddr */,
INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
fail("Repository should be out of addresses and throw");
} catch (DhcpLeaseRepository.OutOfAddressesException e) { /* expected */ }
@@ -480,7 +483,8 @@
private DhcpLease requestLease(@NonNull MacAddress macAddr, @NonNull Inet4Address clientAddr,
@Nullable Inet4Address reqAddr, @Nullable String hostname, boolean sidSet)
throws DhcpLeaseRepository.DhcpLeaseException {
- return mRepo.requestLease(CLIENTID_UNSPEC, macAddr, clientAddr, INET4_ANY /* relayAddr */,
+ return mRepo.requestLease(CLIENTID_UNSPEC, macAddr, clientAddr,
+ IPV4_ADDR_ANY /* relayAddr */,
reqAddr, sidSet, hostname);
}
@@ -490,7 +494,7 @@
private DhcpLease requestLeaseSelecting(@NonNull MacAddress macAddr,
@NonNull Inet4Address reqAddr, @Nullable String hostname)
throws DhcpLeaseRepository.DhcpLeaseException {
- return requestLease(macAddr, INET4_ANY /* clientAddr */, reqAddr, hostname,
+ return requestLease(macAddr, IPV4_ADDR_ANY /* clientAddr */, reqAddr, hostname,
true /* sidSet */);
}
@@ -507,7 +511,7 @@
*/
private DhcpLease requestLeaseInitReboot(@NonNull MacAddress macAddr,
@NonNull Inet4Address reqAddr) throws DhcpLeaseRepository.DhcpLeaseException {
- return requestLease(macAddr, INET4_ANY /* clientAddr */, reqAddr, HOSTNAME_NONE,
+ return requestLease(macAddr, IPV4_ADDR_ANY /* clientAddr */, reqAddr, HOSTNAME_NONE,
false /* sidSet */);
}
diff --git a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpPacketTest.java b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpPacketTest.java
index a592809..7544e72 100644
--- a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpPacketTest.java
+++ b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpPacketTest.java
@@ -16,9 +16,27 @@
package android.net.dhcp;
-import static android.net.NetworkUtils.getBroadcastAddress;
-import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
-import static android.net.dhcp.DhcpPacket.*;
+import static android.net.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS;
+import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER;
+import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME;
+import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME;
+import static android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE_ACK;
+import static android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE_OFFER;
+import static android.net.dhcp.DhcpPacket.DHCP_MTU;
+import static android.net.dhcp.DhcpPacket.DHCP_REBINDING_TIME;
+import static android.net.dhcp.DhcpPacket.DHCP_RENEWAL_TIME;
+import static android.net.dhcp.DhcpPacket.DHCP_ROUTER;
+import static android.net.dhcp.DhcpPacket.DHCP_SUBNET_MASK;
+import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO;
+import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
+import static android.net.dhcp.DhcpPacket.ENCAP_L2;
+import static android.net.dhcp.DhcpPacket.ENCAP_L3;
+import static android.net.dhcp.DhcpPacket.INADDR_ANY;
+import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
+import static android.net.dhcp.DhcpPacket.ParseException;
+import static android.net.shared.Inet4AddressUtils.getBroadcastAddress;
+import static android.net.shared.Inet4AddressUtils.getPrefixMaskAsInet4Address;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -30,11 +48,15 @@
import android.net.LinkAddress;
import android.net.NetworkUtils;
import android.net.metrics.DhcpErrorEvent;
-import android.support.test.runner.AndroidJUnit4;
import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
import com.android.internal.util.HexDump;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.ByteArrayOutputStream;
import java.net.Inet4Address;
import java.nio.ByteBuffer;
@@ -44,10 +66,6 @@
import java.util.Collections;
import java.util.Random;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
@RunWith(AndroidJUnit4.class)
@SmallTest
public class DhcpPacketTest {
@@ -60,9 +78,9 @@
SERVER_ADDR, PREFIX_LENGTH);
private static final String HOSTNAME = "testhostname";
private static final short MTU = 1500;
- // Use our own empty address instead of Inet4Address.ANY or INADDR_ANY to ensure that the code
+ // Use our own empty address instead of IPV4_ADDR_ANY or INADDR_ANY to ensure that the code
// doesn't use == instead of equals when comparing addresses.
- private static final Inet4Address ANY = (Inet4Address) v4Address("0.0.0.0");
+ private static final Inet4Address ANY = v4Address("0.0.0.0");
private static final byte[] CLIENT_MAC = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
diff --git a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServingParamsTest.java b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServingParamsTest.java
index 3ca0564..1004382 100644
--- a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServingParamsTest.java
+++ b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServingParamsTest.java
@@ -17,8 +17,8 @@
package android.net.dhcp;
import static android.net.InetAddresses.parseNumericAddress;
-import static android.net.NetworkUtils.inet4AddressToIntHTH;
import static android.net.dhcp.DhcpServingParams.MTU_UNSET;
+import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -27,8 +27,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.LinkAddress;
-import android.net.NetworkUtils;
import android.net.dhcp.DhcpServingParams.InvalidParameterException;
+import android.net.shared.Inet4AddressUtils;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -200,7 +200,7 @@
}
private static int[] toIntArray(Collection<Inet4Address> addrs) {
- return addrs.stream().mapToInt(NetworkUtils::inet4AddressToIntHTH).toArray();
+ return addrs.stream().mapToInt(Inet4AddressUtils::inet4AddressToIntHTH).toArray();
}
private static <T> void assertContains(@NonNull Set<T> set, @NonNull Set<T> subset) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
index 8b00ed0..00b3736 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.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
+
import com.android.printspooler.R;
/**
@@ -366,7 +367,8 @@
// 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 46b8309..0000000
--- 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 44b1866..0000000
--- 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 c946153..0000000
--- 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 c85a892..3222174 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 bae8387..015f52c 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 c059156..bed3030 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 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 873dd1a..4ce9d3e 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 @@
}
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 ee80aa1..a8a0b6d 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 @@
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 @@
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 @@
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 6b6df9b..a1bf936 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 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 77dfbe9..41c1d60 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 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 c6bb2b3..4bdbc31 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 @@
@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 4dc050c..35600b5 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 @@
@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 ca840d9a..7f906f6 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 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 @@
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 6acdcac..0afc878 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 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 28975d4..bea944c 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 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 2d0a090..6638592 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 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 4672393..4a27715 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 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 1b3c453..165af2c 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 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 ea2ebde..9e9c5b9 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 @@
}
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 a5c6f0c..6d0e3ac 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.content.Context;
import android.util.Log;
-import com.android.settingslib.R;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
/**
@@ -45,7 +44,7 @@
@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 04f70cc..99d9d1c 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 androidx.mediarouter.media.MediaRouter;
-import com.android.settingslib.R;
-
/**
* InfoMediaDevice extends MediaDevice to represents wifi device.
*/
@@ -45,7 +43,7 @@
@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 c808214..01003aa 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 @@
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 @@
@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 f02044d..a106846 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 @@
}
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 a436cb5..cf7bcb2 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 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 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 3fe1e9e..0000000
--- 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 0eb6de9..7a71551 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 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 @@
@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 @@
@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 b77670b..491f32d 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 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 @@
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 bbb4249..b379b54 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.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 @@
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 1b350cc..c6c2a44 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 @@
@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 97de7ef..e24b525 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 @@
@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 @@
@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 @@
@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 4c68c14..63a958e 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 @@
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 cf6137d..c3ea336 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 @@
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 4f6a4ad..a1aefab 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1714,6 +1714,12 @@
dumpSetting(s, p,
Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
SecureSettingsProto.Accessibility.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES);
+ dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS,
+ SecureSettingsProto.Accessibility.NON_INTERACTIVE_UI_TIMEOUT_MS);
+ dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS,
+ SecureSettingsProto.Accessibility.INTERACTIVE_UI_TIMEOUT_MS);
p.end(accessibilityToken);
dumpSetting(s, p,
diff --git a/packages/SystemUI/res/drawable/smart_reply_button_background.xml b/packages/SystemUI/res/drawable/smart_reply_button_background.xml
index 31119a9..5652b49 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 abf9e05..67e31ac 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 a14259e..d435c67 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 190bd7a..ffa5de8 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 b7bee30..da29ab4 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 @@
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 @@
* 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 @@
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 f14495b..4527f73 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.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 @@
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 26c6d50..db5c244 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.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 @@
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 @@
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 @@
})
}
builder.setView(getContentView())
- return builder.create()
+ val dialog = builder.create()
+ dismissDialog = dialog::dismiss
+ return dialog
}
fun getContentView(): View {
@@ -116,6 +120,7 @@
return contentView
}
+ @Suppress("DEPRECATION")
private fun addAppItem(
itemList: LinearLayout,
app: PrivacyApplication,
@@ -152,6 +157,16 @@
} 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 9252167..dbe87d1 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 @@
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 2339fae..f1c3bf2 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 @@
@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 @@
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 d740033..a0f4e24 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 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 @@
/** 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 43ce0b5..de78d33 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 @@
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/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 904478e..0150852 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -111,6 +111,7 @@
private static final int MSG_SHOW_CHARGING_ANIMATION = 44 << MSG_SHIFT;
private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
private static final int MSG_SHOW_PINNING_TOAST_ESCAPE = 46 << MSG_SHIFT;
+ private static final int MSG_DISPLAY_READY = 47 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -274,6 +275,11 @@
default void onBiometricHelp(String message) { }
default void onBiometricError(String error) { }
default void hideBiometricDialog() { }
+
+ /**
+ * @see IStatusBar#onDisplayReady(int)
+ */
+ default void onDisplayReady(int displayId) { }
}
@VisibleForTesting
@@ -761,6 +767,13 @@
}
}
+ @Override
+ public void onDisplayReady(int displayId) {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_DISPLAY_READY, displayId, 0).sendToTarget();
+ }
+ }
+
private final class H extends Handler {
private H(Looper l) {
super(l);
@@ -1015,6 +1028,11 @@
mCallbacks.get(i).showPinningEscapeToast();
}
break;
+ case MSG_DISPLAY_READY:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).onDisplayReady(msg.arg1);
+ }
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index 9740d1d..e11ec2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -19,6 +19,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+import static com.android.systemui.SysUiServiceProvider.getComponent;
import android.content.Context;
import android.hardware.display.DisplayManager;
@@ -34,6 +35,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import com.android.systemui.statusbar.phone.LightBarController;
@@ -48,7 +50,7 @@
/** A controller to handle navigation bars. */
@Singleton
-public class NavigationBarController implements DisplayListener {
+public class NavigationBarController implements DisplayListener, Callbacks {
private static final String TAG = NavigationBarController.class.getName();
@@ -65,13 +67,11 @@
mHandler = handler;
mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
mDisplayManager.registerDisplayListener(this, mHandler);
+ getComponent(mContext, CommandQueue.class).addCallback(this);
}
@Override
- public void onDisplayAdded(int displayId) {
- Display display = mDisplayManager.getDisplay(displayId);
- createNavigationBar(display);
- }
+ public void onDisplayAdded(int displayId) { }
@Override
public void onDisplayRemoved(int displayId) {
@@ -79,7 +79,12 @@
}
@Override
- public void onDisplayChanged(int displayId) {
+ public void onDisplayChanged(int displayId) { }
+
+ @Override
+ public void onDisplayReady(int displayId) {
+ Display display = mDisplayManager.getDisplay(displayId);
+ createNavigationBar(display);
}
// TODO(b/117478341): I use {@code includeDefaultDisplay} to make this method compatible to
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 35b7ef4..5e52419 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.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 @@
}
@Override
+ public void onEntryReinflated(NotificationEntry entry) {
+ mExpansionStateLogger.onEntryReinflated(entry.key);
+ }
+
+ @Override
public void onInflationError(
StatusBarNotification notification,
Exception exception) {
@@ -468,6 +472,13 @@
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 c108371..711b08e 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 @@
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 c4f027f..07c6587 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 @@
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 @@
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 e802757..44ff4a7 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.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 @@
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 d3b3dae..f163b88 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 @@
@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 @@
@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 5ff9d14..f1d9003 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.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 @@
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/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java
index 73f3b43..6177344 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java
@@ -51,6 +51,7 @@
@Before
public void setup() {
+ mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
final Display display = createVirtualDisplay();
final SysuiTestableContext context =
(SysuiTestableContext) mContext.createDisplayContext(display);
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 844096d..6108aaa 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 @@
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 @@
}
@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 @@
}
@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 @@
}
@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 dbff957..2756610 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 e3dcb7d..10b5327 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -197,6 +197,8 @@
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 @@
mAlarmType = alarm.type;
}
+ boolean isBroadcast() {
+ return mPendingIntent != null && mPendingIntent.isBroadcast();
+ }
+
@Override
public String toString() {
return "InFlight{"
@@ -1354,6 +1360,20 @@
}
}
+ 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 @@
removeLocked(uid);
}
}
+
+ @Override
+ public void registerInFlightListener(InFlightListener callback) {
+ synchronized (mLock) {
+ mInFlightListeners.add(callback);
+ }
+ }
}
/**
@@ -4426,7 +4453,11 @@
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 @@
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/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index 4a8706e..2f7929c 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -55,11 +55,11 @@
"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/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 8adc416..84577f1 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -16,6 +16,9 @@
package com.android.server;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.content.Context;
import android.os.Environment;
@@ -46,6 +49,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.annotation.Retention;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
@@ -55,7 +59,8 @@
/**
* Monitors the health of packages on the system and notifies interested observers when packages
- * fail. All registered observers will be notified until an observer takes a mitigation action.
+ * fail. On failure, the registered observer with the least user impacting mitigation will
+ * be notified.
*/
public class PackageWatchdog {
private static final String TAG = "PackageWatchdog";
@@ -78,7 +83,8 @@
private final Context mContext;
// Handler to run package cleanup runnables
private final Handler mTimerHandler;
- private final Handler mIoHandler;
+ // Handler for processing IO and observer actions
+ private final Handler mWorkerHandler;
// Contains (observer-name -> observer-handle) that have ever been registered from
// previous boots. Observers with all packages expired are periodically pruned.
// It is saved to disk on system shutdown and repouplated on startup so it survives reboots.
@@ -101,7 +107,7 @@
mPolicyFile = new AtomicFile(new File(new File(Environment.getDataDirectory(), "system"),
"package-watchdog.xml"));
mTimerHandler = new Handler(Looper.myLooper());
- mIoHandler = BackgroundThread.getHandler();
+ mWorkerHandler = BackgroundThread.getHandler();
mPackageCleanup = this::rescheduleCleanup;
loadFromFile();
}
@@ -115,7 +121,7 @@
mContext = context;
mPolicyFile = new AtomicFile(new File(context.getFilesDir(), "package-watchdog.xml"));
mTimerHandler = new Handler(looper);
- mIoHandler = mTimerHandler;
+ mWorkerHandler = mTimerHandler;
mPackageCleanup = this::rescheduleCleanup;
loadFromFile();
}
@@ -228,49 +234,46 @@
/**
* Called when a process fails either due to a crash or ANR.
*
- * <p>All registered observers for the packages contained in the process will be notified in
- * order of priority until an observer signifies that it has taken action and other observers
- * should not notified.
+ * <p>For each package contained in the process, one registered observer with the least user
+ * impact will be notified for mitigation.
*
* <p>This method could be called frequently if there is a severe problem on the device.
*/
public void onPackageFailure(String[] packages) {
- ArrayMap<String, List<PackageHealthObserver>> packagesToReport = new ArrayMap<>();
- synchronized (mLock) {
- if (mAllObservers.isEmpty()) {
- return;
- }
+ mWorkerHandler.post(() -> {
+ synchronized (mLock) {
+ if (mAllObservers.isEmpty()) {
+ return;
+ }
- for (int pIndex = 0; pIndex < packages.length; pIndex++) {
- // Observers interested in receiving packageName failures
- List<PackageHealthObserver> observersToNotify = new ArrayList<>();
- for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
- PackageHealthObserver registeredObserver =
- mAllObservers.valueAt(oIndex).mRegisteredObserver;
- if (registeredObserver != null) {
- observersToNotify.add(registeredObserver);
+ for (int pIndex = 0; pIndex < packages.length; pIndex++) {
+ String packageToReport = packages[pIndex];
+ // Observer that will receive failure for packageToReport
+ PackageHealthObserver currentObserverToNotify = null;
+ int currentObserverImpact = Integer.MAX_VALUE;
+
+ // Find observer with least user impact
+ for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
+ ObserverInternal observer = mAllObservers.valueAt(oIndex);
+ PackageHealthObserver registeredObserver = observer.mRegisteredObserver;
+ if (registeredObserver != null
+ && observer.onPackageFailure(packageToReport)) {
+ int impact = registeredObserver.onHealthCheckFailed(packageToReport);
+ if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE
+ && impact < currentObserverImpact) {
+ currentObserverToNotify = registeredObserver;
+ currentObserverImpact = impact;
+ }
+ }
+ }
+
+ // Execute action with least user impact
+ if (currentObserverToNotify != null) {
+ currentObserverToNotify.execute(packageToReport);
}
}
- // Save interested observers and notify them outside the lock
- if (!observersToNotify.isEmpty()) {
- packagesToReport.put(packages[pIndex], observersToNotify);
- }
}
- }
-
- // Notify observers
- for (int pIndex = 0; pIndex < packagesToReport.size(); pIndex++) {
- List<PackageHealthObserver> observers = packagesToReport.valueAt(pIndex);
- String packageName = packages[pIndex];
- for (int oIndex = 0; oIndex < observers.size(); oIndex++) {
- PackageHealthObserver observer = observers.get(oIndex);
- if (mAllObservers.get(observer.getName()).onPackageFailure(packageName)
- && observer.onHealthCheckFailed(packageName)) {
- // Observer has handled, do not notify others
- break;
- }
- }
- }
+ });
}
// TODO(zezeozue): Optimize write? Maybe only write a separate smaller file?
@@ -278,21 +281,46 @@
/** Writes the package information to file during shutdown. */
public void writeNow() {
if (!mAllObservers.isEmpty()) {
- mIoHandler.removeCallbacks(this::saveToFile);
+ mWorkerHandler.removeCallbacks(this::saveToFile);
pruneObservers(SystemClock.uptimeMillis() - mUptimeAtLastRescheduleMs);
saveToFile();
Slog.i(TAG, "Last write to update package durations");
}
}
+ /** Possible severity values of the user impact of a {@link PackageHealthObserver#execute}. */
+ @Retention(SOURCE)
+ @IntDef(value = {PackageHealthObserverImpact.USER_IMPACT_NONE,
+ PackageHealthObserverImpact.USER_IMPACT_LOW,
+ PackageHealthObserverImpact.USER_IMPACT_MEDIUM,
+ PackageHealthObserverImpact.USER_IMPACT_HIGH})
+ public @interface PackageHealthObserverImpact {
+ /** No action to take. */
+ int USER_IMPACT_NONE = 0;
+ /* Action has low user impact, user of a device will barely notice. */
+ int USER_IMPACT_LOW = 1;
+ /* Action has medium user impact, user of a device will likely notice. */
+ int USER_IMPACT_MEDIUM = 3;
+ /* Action has high user impact, a last resort, user of a device will be very frustrated. */
+ int USER_IMPACT_HIGH = 5;
+ }
+
/** Register instances of this interface to receive notifications on package failure. */
public interface PackageHealthObserver {
/**
* Called when health check fails for the {@code packageName}.
- * @return {@code true} if action was taken and other observers should not be notified of
- * this failure, {@code false} otherwise.
+ *
+ * @return any one of {@link PackageHealthObserverImpact} to express the impact
+ * to the user on {@link #execute}
*/
- boolean onHealthCheckFailed(String packageName);
+ @PackageHealthObserverImpact int onHealthCheckFailed(String packageName);
+
+ /**
+ * Executes mitigation for {@link #onHealthCheckFailed}.
+ *
+ * @return {@code true} if action was executed successfully, {@code false} otherwise
+ */
+ boolean execute(String packageName);
// TODO(zezeozue): Ensure uniqueness?
/**
@@ -442,8 +470,8 @@
}
private void saveToFileAsync() {
- mIoHandler.removeCallbacks(this::saveToFile);
- mIoHandler.post(this::saveToFile);
+ mWorkerHandler.removeCallbacks(this::saveToFile);
+ mWorkerHandler.post(this::saveToFile);
}
/**
@@ -606,7 +634,11 @@
} else {
mFailures++;
}
- return mFailures >= TRIGGER_FAILURE_COUNT;
+ boolean failed = mFailures >= TRIGGER_FAILURE_COUNT;
+ if (failed) {
+ mFailures = 0;
+ }
+ return failed;
}
}
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index cec825f..cecd55a3 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -802,10 +802,7 @@
// 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 @@
}
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/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 0aaea2f..e42666c 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 @@
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 eb643b6..d9f3c02 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.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 @@
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 @@
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 @@
}
@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 @@
synchronized (this) {
mSystemProvidersInstalled = true;
}
-
mConstants.start(mContext.getContentResolver());
mCoreSettingsObserver = new CoreSettingsObserver(this);
mActivityTaskManager.installSystemProviders();
@@ -8711,6 +8735,12 @@
}
}
+ 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 @@
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 @@
* 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 0000000..820caf1
--- /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 0000000..0d46379
--- /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 cdf6e0e..64a36ef 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.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.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,16 +77,17 @@
final ActivityManagerService mService;
/**
+ * Behavioral parameters such as timeouts and deferral policy, tracking Settings
+ * for runtime configurability
+ */
+ final BroadcastConstants mConstants;
+
+ /**
* Recognizable moniker for this queue
*/
final String mQueueName;
/**
- * Timeout period for this queue's broadcasts
- */
- final long mTimeoutPeriod;
-
- /**
* If true, we can delay broadcasts while waiting services to finish in the previous
* receiver's process.
*/
@@ -100,13 +103,18 @@
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 @@
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 @@
}
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 void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
- mOrderedBroadcasts.add(r);
+ mDispatcher.enqueueOrderedBroadcastLocked(r);
enqueueBroadcastHelper(r);
}
@@ -255,7 +271,7 @@
* 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 @@
}
}
+ // 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 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 @@
// 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 @@
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 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 @@
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 @@
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 @@
return;
}
- r = mOrderedBroadcasts.get(0);
+
boolean forceReceive = false;
// Ensure that even if something goes awry with the timeout
@@ -967,9 +1014,8 @@
// 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 @@
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 @@
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 @@
+ 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 @@
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 @@
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 @@
}
}
- 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 @@
}
}
- 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 @@
}
final boolean isIdle() {
- return mParallelBroadcasts.isEmpty() && mOrderedBroadcasts.isEmpty()
+ return mParallelBroadcasts.isEmpty() && mDispatcher.isEmpty()
&& (mPendingBroadcast == null);
}
@@ -1677,10 +1808,7 @@
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 @@
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 @@
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 @@
}
}
+ 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 9e799f6..d9e03f8 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 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 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 @@
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 @@
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/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 7ede6dc..26d2d17 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.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 @@
}
}
+ /**
+ * 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 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 b71a751..5b469fe 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.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 @@
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 @@
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 @@
* 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 @@
* 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/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 4db541c..9b1db75 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -207,6 +207,8 @@
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;
@@ -2755,9 +2757,34 @@
Slog.e(TAG, "windowToken cannot be null.");
return InputBindResult.NULL;
}
- final InputBindResult result = startInputOrWindowGainedFocusInternal(startInputReason,
- client, windowToken, startInputFlags, softInputMode, windowFlags, attribute,
- inputContext, missingMethods, 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.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
+ // an error code instead of crashing the caller process, which at least has
+ // INTERACT_ACROSS_USERS_FULL permission thus is likely to be an important process.
+ Slog.e(TAG, "User #" + userId + " is not running.");
+ return InputBindResult.INVALID_USER;
+ }
+ } else {
+ userId = callingUserId;
+ }
+ final InputBindResult result;
+ synchronized (mMethodMap) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ 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="
@@ -2770,242 +2797,217 @@
}
@NonNull
- private InputBindResult startInputOrWindowGainedFocusInternal(
+ 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) {
- 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.");
- userId = attribute.targetInputMethodUser.getIdentifier();
- if (!mUserManagerInternal.isUserRunning(userId)) {
- // There is a chance that we hit here because of race condition. Let's just return
- // an error code instead of crashing the caller process, which at least has
- // INTERACT_ACROSS_USERS_FULL permission thus is likely to be an important process.
- Slog.e(TAG, "User #" + userId + " is not running.");
- return InputBindResult.INVALID_USER;
- }
- } else {
- userId = callingUserId;
- }
+ int unverifiedTargetSdkVersion, @UserIdInt int userId) {
InputBindResult res = null;
- 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);
+ final int windowDisplayId =
+ mWindowManagerInternal.getDisplayIdForWindow(windowToken);
+ 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);
- 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 (!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 & 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;
- }
-
- 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;
- }
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ 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 (!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 & 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;
+ }
+
+ 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 +3329,32 @@
}
}
+ @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 +3608,32 @@
}
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:
@@ -4756,5 +4810,17 @@
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/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 326984c..4349b4a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -872,7 +872,7 @@
private int getInt(String key, int defaultValue) {
if (mCopyOnWrite && mCopyOnWriteDataStore.containsKey(key)) {
final String result = mCopyOnWriteDataStore.get(key);
- return result != null ? Integer.parseInt(result) : 0;
+ return result != null ? Integer.parseInt(result) : defaultValue;
}
return Settings.Secure.getIntForUser(mResolver, key, defaultValue, mCurrentUserId);
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 7625aaf..c56db71 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -2040,6 +2040,7 @@
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 @@
}
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 82bfa51..ed1a6d0 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.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.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 @@
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 @@
}
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 @@
}
}
+ /** 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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index de3f50a..b81e200 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.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;
@@ -3859,6 +3860,24 @@
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/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index ac965fa..ab49ebb 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -667,15 +667,15 @@
getUserSentiment()));
}
}
- if (signals.containsKey(Adjustment.KEY_SMART_ACTIONS)) {
+ if (signals.containsKey(Adjustment.KEY_CONTEXTUAL_ACTIONS)) {
setSystemGeneratedSmartActions(
- signals.getParcelableArrayList(Adjustment.KEY_SMART_ACTIONS));
+ signals.getParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS));
MetricsLogger.action(getAdjustmentLogMaker()
.addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SMART_ACTIONS,
getSystemGeneratedSmartActions().size()));
}
- if (signals.containsKey(Adjustment.KEY_SMART_REPLIES)) {
- setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES));
+ if (signals.containsKey(Adjustment.KEY_TEXT_REPLIES)) {
+ setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_TEXT_REPLIES));
MetricsLogger.action(getAdjustmentLogMaker()
.addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SMART_REPLIES,
getSmartReplies().size()));
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index d0ef4f1..d5089cb 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 @@
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 eab5c8f..146a2f3 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.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 @@
private final Callbacks mCallbacks;
+ private volatile boolean mBootCompleted = false;
+
/**
* File storing persisted {@link #mSessions} metadata.
*/
@@ -203,9 +207,20 @@
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 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/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 30b5e49..3c89d78 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 @@
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 @@
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 076c94c..09bacd6 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/role/FinancialSmsManager.java b/services/core/java/com/android/server/role/FinancialSmsManager.java
new file mode 100644
index 0000000..2ec3993
--- /dev/null
+++ b/services/core/java/com/android/server/role/FinancialSmsManager.java
@@ -0,0 +1,218 @@
+/*
+ * 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.role;
+
+import android.Manifest;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.service.sms.FinancialSmsService;
+import android.service.sms.IFinancialSmsService;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * This class binds to {@code FinancialSmsService}.
+ */
+final class FinancialSmsManager {
+
+ private static final String TAG = "FinancialSmsManager";
+
+ private final Context mContext;
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private ServiceConnection mServiceConnection;
+
+ @GuardedBy("mLock")
+ private IFinancialSmsService mRemoteService;
+
+ @GuardedBy("mLock")
+ private ArrayList<Command> mQueuedCommands;
+
+ FinancialSmsManager(Context context) {
+ mContext = context;
+ }
+
+ @Nullable
+ ServiceInfo getServiceInfo() {
+ final String packageName =
+ mContext.getPackageManager().getServicesSystemSharedLibraryPackageName();
+ if (packageName == null) {
+ Slog.w(TAG, "no external services package!");
+ return null;
+ }
+
+ final Intent intent = new Intent(FinancialSmsService.ACTION_FINANCIAL_SERVICE_INTENT);
+ intent.setPackage(packageName);
+ final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
+ PackageManager.GET_SERVICES);
+ if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+ Slog.w(TAG, "No valid components found.");
+ return null;
+ }
+ return resolveInfo.serviceInfo;
+ }
+
+ @Nullable
+ private ComponentName getServiceComponentName() {
+ final ServiceInfo serviceInfo = getServiceInfo();
+ if (serviceInfo == null) return null;
+
+ final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
+ if (!Manifest.permission.BIND_FINANCIAL_SMS_SERVICE.equals(serviceInfo.permission)) {
+ Slog.w(TAG, name.flattenToShortString() + " does not require permission "
+ + Manifest.permission.BIND_FINANCIAL_SMS_SERVICE);
+ return null;
+ }
+
+ return name;
+ }
+
+ void reset() {
+ synchronized (mLock) {
+ if (mServiceConnection != null) {
+ mContext.unbindService(mServiceConnection);
+ mServiceConnection = null;
+ } else {
+ Slog.d(TAG, "reset(): service is not bound. Do nothing.");
+ }
+ }
+ }
+
+ /**
+ * Run a command, starting the service connection if necessary.
+ */
+ private void connectAndRun(@NonNull Command command) {
+ synchronized (mLock) {
+ if (mRemoteService != null) {
+ try {
+ command.run(mRemoteService);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "exception calling service: " + e);
+ }
+ return;
+ } else {
+ if (mQueuedCommands == null) {
+ mQueuedCommands = new ArrayList<>(1);
+ }
+ mQueuedCommands.add(command);
+ // If we're already connected, don't create a new connection, just leave - the
+ // command will be run when the service connects
+ if (mServiceConnection != null) return;
+ }
+
+ // Create the connection
+ mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mLock) {
+ mRemoteService = IFinancialSmsService.Stub.asInterface(service);
+ if (mQueuedCommands != null) {
+ final int size = mQueuedCommands.size();
+ for (int i = 0; i < size; i++) {
+ final Command queuedCommand = mQueuedCommands.get(i);
+ try {
+ queuedCommand.run(mRemoteService);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "exception calling " + name + ": " + e);
+ }
+ }
+ mQueuedCommands = null;
+ }
+ }
+ }
+
+ @Override
+ @MainThread
+ public void onServiceDisconnected(ComponentName name) {
+ synchronized (mLock) {
+ mRemoteService = null;
+ }
+ }
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ synchronized (mLock) {
+ mRemoteService = null;
+ }
+ }
+
+ @Override
+ public void onNullBinding(ComponentName name) {
+ synchronized (mLock) {
+ mRemoteService = null;
+ }
+ }
+ };
+
+ final ComponentName component = getServiceComponentName();
+ if (component != null) {
+ final Intent intent = new Intent();
+ intent.setComponent(component);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mContext.bindServiceAsUser(intent, mServiceConnection, Context.BIND_AUTO_CREATE,
+ UserHandle.getUserHandleForUid(UserHandle.getCallingUserId()));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+ }
+
+ void getSmsMessages(RemoteCallback callback, @Nullable Bundle params) {
+ connectAndRun((service) -> service.getSmsMessages(callback, params));
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ final ComponentName impl = getServiceComponentName();
+ pw.print(prefix); pw.print("User ID: "); pw.println(UserHandle.getCallingUserId());
+ pw.print(prefix); pw.print("Queued commands: ");
+ if (mQueuedCommands == null) {
+ pw.println("N/A");
+ } else {
+ pw.println(mQueuedCommands.size());
+ }
+ pw.print(prefix); pw.print("Implementation: ");
+ if (impl == null) {
+ pw.println("N/A");
+ return;
+ }
+ pw.println(impl.flattenToShortString());
+ }
+
+ private interface Command {
+ void run(IFinancialSmsService service) throws RemoteException;
+ }
+}
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 1c7596b..7d8c7c9 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -33,16 +33,24 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.PermissionChecker;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
+import android.database.CursorWindow;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
+import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
import android.os.UserManagerInternal;
+import android.service.sms.FinancialSmsService;
+import android.telephony.IFinancialSmsCallback;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.PackageUtils;
@@ -620,5 +628,51 @@
dumpOutputStream.flush();
}
+
+ /**
+ * Get filtered SMS messages for financial app.
+ */
+ @Override
+ public void getSmsMessagesForFinancialApp(
+ String callingPkg, Bundle params, IFinancialSmsCallback callback) {
+ int mode = PermissionChecker.checkCallingOrSelfPermission(
+ getContext(),
+ AppOpsManager.OPSTR_SMS_FINANCIAL_TRANSACTIONS);
+
+ if (mode == PermissionChecker.PERMISSION_GRANTED) {
+ FinancialSmsManager financialSmsManager = new FinancialSmsManager(getContext());
+ financialSmsManager.getSmsMessages(new RemoteCallback((result) -> {
+ CursorWindow messages = null;
+ if (result == null) {
+ Slog.w(LOG_TAG, "result is null.");
+ } else {
+ messages = result.getParcelable(FinancialSmsService.EXTRA_SMS_MSGS);
+ }
+ try {
+ callback.onGetSmsMessagesForFinancialApp(messages);
+ } catch (RemoteException e) {
+ // do nothing
+ }
+ }), params);
+ } else {
+ try {
+ callback.onGetSmsMessagesForFinancialApp(null);
+ } catch (RemoteException e) {
+ // do nothing
+ }
+ }
+ }
+
+ private int getUidForPackage(String packageName) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return getContext().getPackageManager().getApplicationInfo(packageName,
+ PackageManager.MATCH_ANY_USER).uid;
+ } catch (NameNotFoundException nnfe) {
+ return -1;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 6487bd7..e59228a 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -227,7 +227,7 @@
}
@Override
- public void executeRollback(RollbackInfo rollback, String callerPackageName,
+ public void commitRollback(int rollbackId, String callerPackageName,
IntentSender statusReceiver) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MANAGE_ROLLBACKS,
@@ -238,19 +238,19 @@
appOps.checkPackage(callingUid, callerPackageName);
getHandler().post(() ->
- executeRollbackInternal(rollback, callerPackageName, statusReceiver));
+ commitRollbackInternal(rollbackId, callerPackageName, statusReceiver));
}
/**
- * Performs the actual work to execute a rollback.
+ * Performs the actual work to commit a rollback.
* The work is done on the current thread. This may be a long running
* operation.
*/
- private void executeRollbackInternal(RollbackInfo rollback,
+ private void commitRollbackInternal(int rollbackId,
String callerPackageName, IntentSender statusReceiver) {
Log.i(TAG, "Initiating rollback");
- RollbackData data = getRollbackForId(rollback.getRollbackId());
+ RollbackData data = getRollbackForId(rollbackId);
if (data == null) {
sendFailure(statusReceiver, "Rollback unavailable");
return;
@@ -350,7 +350,8 @@
return;
}
- addRecentlyExecutedRollback(rollback);
+ addRecentlyExecutedRollback(
+ new RollbackInfo(data.rollbackId, data.packages));
sendSuccess(statusReceiver);
Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED);
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 3954a11..fcae618 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -27,6 +27,7 @@
import com.android.server.PackageWatchdog;
import com.android.server.PackageWatchdog.PackageHealthObserver;
+import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
import java.util.List;
@@ -39,10 +40,12 @@
private static final String TAG = "RollbackPackageHealthObserver";
private static final String NAME = "rollback-observer";
private Context mContext;
+ private RollbackManager mRollbackManager;
private Handler mHandler;
RollbackPackageHealthObserver(Context context) {
mContext = context;
+ mRollbackManager = mContext.getSystemService(RollbackManager.class);
HandlerThread handlerThread = new HandlerThread("RollbackPackageHealthObserver");
handlerThread.start();
mHandler = handlerThread.getThreadHandler();
@@ -50,19 +53,48 @@
}
@Override
- public boolean onHealthCheckFailed(String packageName) {
- RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
- for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) {
- for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
- if (packageName.equals(packageRollback.getPackageName())) {
- // TODO(zezeozue): Only rollback if rollback version == failed package version
- mHandler.post(() -> executeRollback(rollbackManager, rollback));
- return true;
- }
- }
+ public int onHealthCheckFailed(String packageName) {
+ RollbackInfo rollback = getAvailableRollback(packageName);
+ if (rollback == null) {
+ // Don't handle the notification, no rollbacks available for the package
+ return PackageHealthObserverImpact.USER_IMPACT_NONE;
}
- // Don't handle the notification, no rollbacks available
- return false;
+ // Rollback is available, we may get a callback into #execute
+ return PackageHealthObserverImpact.USER_IMPACT_MEDIUM;
+ }
+
+ @Override
+ public boolean execute(String packageName) {
+ RollbackInfo rollback = getAvailableRollback(packageName);
+ if (rollback == null) {
+ // Expected a rollback to be available, what happened?
+ return false;
+ }
+
+ // TODO(zezeozue): Only rollback if rollback version == failed package version
+ LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
+ int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ // TODO(zezeozue); Log success metrics
+ // Rolledback successfully, no action required by other observers
+ } else {
+ // TODO(zezeozue); Log failure metrics
+ // Rollback failed other observers should have a shot
+ }
+ });
+
+ // TODO(zezeozue): Log initiated metrics
+ mHandler.post(() ->
+ mRollbackManager.commitRollback(rollback.getRollbackId(),
+ rollbackReceiver.getIntentSender()));
+ // Assume rollback executed successfully
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
}
/**
@@ -73,26 +105,15 @@
PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs);
}
- private void executeRollback(RollbackManager manager, RollbackInfo rollback) {
- // TODO(zezeozue): Log initiated metrics
- LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
- mHandler.post(() -> {
- int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status == PackageInstaller.STATUS_SUCCESS) {
- // TODO(zezeozue); Log success metrics
- // Rolledback successfully, no action required by other observers
- } else {
- // TODO(zezeozue); Log failure metrics
- // Rollback failed other observers should have a shot
+ private RollbackInfo getAvailableRollback(String packageName) {
+ for (RollbackInfo rollback : mRollbackManager.getAvailableRollbacks()) {
+ for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
+ if (packageName.equals(packageRollback.getPackageName())) {
+ // TODO(zezeozue): Only rollback if rollback version == failed package version
+ return rollback;
}
- });
- });
- manager.commitRollback(rollback, rollbackReceiver.getIntentSender());
- }
-
- @Override
- public String getName() {
- return NAME;
+ }
+ }
+ return null;
}
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index a66f0ca..b9b5aae 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -100,4 +100,11 @@
* @param rotation rotation suggestion
*/
void onProposedRotationChanged(int rotation, boolean isValid);
+
+ /**
+ * Notifies System UI that the display is ready to show system decorations.
+ *
+ * @param displayId display ID
+ */
+ void onDisplayReady(int displayId);
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 8d2bab4..7e87c29 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -443,6 +443,15 @@
} catch (RemoteException ex) {}
}
}
+
+ @Override
+ public void onDisplayReady(int displayId) {
+ if (mBar != null) {
+ try {
+ mBar.onDisplayReady(displayId);
+ } catch (RemoteException ex) { }
+ }
+ }
};
private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() {
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 1023182..2d89bc7 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 @@
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 @@
// 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 @@
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 @@
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 @@
(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 b7c35c0..8f39f3d 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.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 @@
}
}
+ 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 @@
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 c97e4e8..549567a 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 @@
// 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 a83ef34..c8a150b 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 @@
// 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 22a81ef..95a6f71 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 @@
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 @@
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 @@
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,60 +989,14 @@
+ "; 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 (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/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 4006332..bbf115f 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2574,6 +2574,10 @@
}
}
+ void notifyDisplayReady() {
+ mHandler.post(() -> getStatusBarManagerInternal().onDisplayReady(getDisplayId()));
+ }
+
/**
* Return the display width available after excluding any screen
* decorations that could never be removed in Honeycomb. That is, system bar or
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index e798203..66666e6 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 @@
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 @@
return;
}
mAdapter = new ControlAdapter();
+ setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
mWin.startAnimation(mDisplayContent.getPendingTransaction(), mAdapter,
!mClientVisible /* hidden */);
mControllingWin = target;
@@ -219,7 +220,7 @@
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 c4a853d..624fdc2 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 @@
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 @@
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.
@@ -1296,16 +1319,21 @@
public void onDisplayAdded(int displayId) {
if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId);
synchronized (mService.mGlobalLock) {
- getActivityDisplayOrCreate(displayId);
+ final ActivityDisplay display = getActivityDisplayOrCreate(displayId);
// Do not start home before booting, or it may accidentally finish booting before it
// starts. Instead, we expect home activities to be launched when the system is ready
// (ActivityManagerService#systemReady).
if (mService.isBooted() || mService.isBooting()) {
- startHomeOnDisplay(mCurrentUser, "displayAdded", displayId);
+ startSystemDecorations(display.mDisplayContent);
}
}
}
+ private void startSystemDecorations(final DisplayContent displayContent) {
+ startHomeOnDisplay(mCurrentUser, "displayAdded", displayContent.getDisplayId());
+ displayContent.getDisplayPolicy().notifyDisplayReady();
+ }
+
@Override
public void onDisplayRemoved(int displayId) {
if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId);
@@ -1399,6 +1427,7 @@
mActivityDisplays.remove(display);
mActivityDisplays.add(position, display);
}
+ updateTopResumedActivityIfNeeded();
}
@VisibleForTesting
diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java
index e99dd4f..bbecc63 100644
--- a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java
+++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java
@@ -16,6 +16,9 @@
package com.android.server.net.ipmemorystore;
+import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH;
+import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentValues;
@@ -27,7 +30,6 @@
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQuery;
-import android.net.NetworkUtils;
import android.net.ipmemorystore.NetworkAttributes;
import android.net.ipmemorystore.Status;
import android.util.Log;
@@ -200,7 +202,7 @@
if (null == attributes) return values;
if (null != attributes.assignedV4Address) {
values.put(NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESS,
- NetworkUtils.inet4AddressToIntHTH(attributes.assignedV4Address));
+ inet4AddressToIntHTH(attributes.assignedV4Address));
}
if (null != attributes.groupHint) {
values.put(NetworkAttributesContract.COLNAME_GROUPHINT, attributes.groupHint);
@@ -254,7 +256,7 @@
getBlob(cursor, NetworkAttributesContract.COLNAME_DNSADDRESSES);
final int mtu = getInt(cursor, NetworkAttributesContract.COLNAME_MTU, -1);
if (0 != assignedV4AddressInt) {
- builder.setAssignedV4Address(NetworkUtils.intToInet4AddressHTH(assignedV4AddressInt));
+ builder.setAssignedV4Address(intToInet4AddressHTH(assignedV4AddressInt));
}
builder.setGroupHint(groupHint);
if (null != dnsAddressesBlob) {
diff --git a/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java b/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java
index f068c3a..1fe2328 100644
--- a/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java
+++ b/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java
@@ -16,7 +16,7 @@
package android.net.dhcp;
-import static android.net.NetworkUtils.inet4AddressToIntHTH;
+import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH;
import android.annotation.NonNull;
import android.net.LinkAddress;
diff --git a/services/net/java/android/net/shared/NetdService.java b/services/net/java/android/net/shared/NetdService.java
index be0f5f2..f5ae725 100644
--- a/services/net/java/android/net/shared/NetdService.java
+++ b/services/net/java/android/net/shared/NetdService.java
@@ -16,6 +16,7 @@
package android.net.shared;
+import android.content.Context;
import android.net.INetd;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -28,7 +29,6 @@
*/
public class NetdService {
private static final String TAG = NetdService.class.getSimpleName();
- private static final String NETD_SERVICE_NAME = "netd";
private static final long BASE_TIMEOUT_MS = 100;
private static final long MAX_TIMEOUT_MS = 1000;
@@ -48,7 +48,7 @@
// NOTE: ServiceManager does no caching for the netd service,
// because netd is not one of the defined common services.
final INetd netdInstance = INetd.Stub.asInterface(
- ServiceManager.getService(NETD_SERVICE_NAME));
+ ServiceManager.getService(Context.NETD_SERVICE));
if (netdInstance == null) {
Log.w(TAG, "WARNING: returning null INetd instance.");
}
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 5d69bbd..4e43d00 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 @@
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/NotificationAdjustmentExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
index 410ab87..e658d17 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
@@ -59,7 +59,7 @@
signals.putStringArrayList(Adjustment.KEY_PEOPLE, people);
ArrayList<Notification.Action> smartActions = new ArrayList<>();
smartActions.add(createAction());
- signals.putParcelableArrayList(Adjustment.KEY_SMART_ACTIONS, smartActions);
+ signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, smartActions);
Adjustment adjustment = new Adjustment("pkg", r.getKey(), signals, "", 0);
r.addAdjustment(adjustment);
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 056568a..ace965b 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 @@
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 @@
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 e3bacf9..67ee4ad 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 @@
/**
* 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 fa472e2..873ada0 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 @@
}
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 @@
}
@GuardedBy("mLock")
- boolean isGroupLimit() {
- return mGroupLimit;
- }
-
- @GuardedBy("mLock")
long getTotaUsageLimit() {
return mTimeLimitMs;
}
@@ -547,14 +539,6 @@
return mTimeLimitMs - mUsageTimeMs;
}
}
-
- @Override
- @GuardedBy("mLock")
- void dump(PrintWriter pw) {
- super.dump(pw);
- pw.print(" groupLimit=");
- pw.print(mGroupLimit);
- }
}
@@ -692,7 +676,7 @@
smallestGroup = otherGroup;
}
}
- return new UsageStatsManagerInternal.AppUsageLimitData(smallestGroup.isGroupLimit(),
+ return new UsageStatsManagerInternal.AppUsageLimitData(
smallestGroup.getTotaUsageLimit(), smallestGroup.getUsageRemaining());
}
}
diff --git a/telephony/java/android/telephony/IFinancialSmsCallback.aidl b/telephony/java/android/telephony/IFinancialSmsCallback.aidl
new file mode 100644
index 0000000..aa88615
--- /dev/null
+++ b/telephony/java/android/telephony/IFinancialSmsCallback.aidl
@@ -0,0 +1,34 @@
+/*
+** 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.telephony;
+
+import android.app.PendingIntent;
+import android.database.CursorWindow;
+import android.net.Uri;
+import android.os.Bundle;
+import com.android.internal.telephony.SmsRawData;
+
+/** Interface for returning back the financial sms messages asynchrously.
+ * @hide
+ */
+interface IFinancialSmsCallback {
+ /**
+ * Return sms messages back to calling financial app.
+ *
+ * @param messages the sms messages returned for cinancial app.
+ */
+ oneway void onGetSmsMessagesForFinancialApp(in CursorWindow messages);
+}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index bf9bf9a..4475630 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1631,8 +1631,9 @@
/** @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/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index aaca18a..fae7920 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -32,8 +33,10 @@
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
+import android.database.CursorWindow;
import android.net.Uri;
import android.os.BaseBundle;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
@@ -51,6 +54,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Executor;
/*
* TODO(code review): Curious question... Why are a lot of these
@@ -2146,6 +2150,43 @@
}
}
+ /** callback for providing asynchronous sms messages for financial app. */
+ public abstract static class FinancialSmsCallback {
+ /**
+ * Callback to send sms messages back to financial app asynchronously.
+ *
+ * @param msgs SMS messages.
+ */
+ public abstract void onFinancialSmsMessages(CursorWindow msgs);
+ };
+
+ /**
+ * Get SMS messages for the calling financial app.
+ * The result will be delivered asynchronously in the passing in callback interface.
+ *
+ * @param params the parameters to filter SMS messages returned.
+ * @param executor the executor on which callback will be invoked.
+ * @param callback a callback to receive CursorWindow with SMS messages.
+ */
+ @RequiresPermission(android.Manifest.permission.SMS_FINANCIAL_TRANSACTIONS)
+ public void getSmsMessagesForFinancialApp(
+ Bundle params,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull FinancialSmsCallback callback) {
+ try {
+ ISms iccSms = getISmsServiceOrThrow();
+ iccSms.getSmsMessagesForFinancialApp(
+ getSubscriptionId(), ActivityThread.currentPackageName(), params,
+ new IFinancialSmsCallback.Stub() {
+ public void onGetSmsMessagesForFinancialApp(CursorWindow msgs) {
+ Binder.withCleanCallingIdentity(() -> executor.execute(
+ () -> callback.onFinancialSmsMessages(msgs)));
+ }});
+ } catch (RemoteException ex) {
+ ex.rethrowFromSystemServer();
+ }
+ }
+
/**
* @see #createAppSpecificSmsTokenWithPackageInfo().
* The prefixes is a list of prefix {@code String} separated by this delimiter.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 98fc725..80ee0a7 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -10134,4 +10134,26 @@
}
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 294c79b..3d2fe5f 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -23,6 +23,7 @@
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 @@
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 @@
* @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 @@
* 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 @@
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 @@
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 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 @@
.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 @@
&& 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 @@
@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 @@
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 da4822c..1d196f9 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 @@
*/
@SystemApi
public final class DataProfile implements Parcelable {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"TYPE_"},
+ value = {
+ TYPE_COMMON,
+ TYPE_3GPP,
+ TYPE_3GPP2})
+ public @interface DataProfileType {}
- // The types indicating the data profile is used on GSM (3GPP) or CDMA (3GPP2) network.
+ /** 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 @@
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 @@
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 @@
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 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 @@
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 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 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 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 @@
@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 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 @@
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/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index 58ebb15..b51eda3 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -18,6 +18,8 @@
import android.app.PendingIntent;
import android.net.Uri;
+import android.os.Bundle;
+import android.telephony.IFinancialSmsCallback;
import com.android.internal.telephony.SmsRawData;
/** Interface for applications to access the ICC phone book.
@@ -575,4 +577,15 @@
*/
String createAppSpecificSmsTokenWithPackageInfo(
int subId, String callingPkg, String prefixes, in PendingIntent intent);
+
+ /**
+ * Get sms inbox messages for the calling financial app.
+ *
+ * @param subId the SIM id.
+ * @param callingPkg the package name of the calling app.
+ * @param params parameters to filter the sms messages.
+ * @param callback the callback interface to deliver the result.
+ */
+ void getSmsMessagesForFinancialApp(
+ int subId, String callingPkg, in Bundle params, in IFinancialSmsCallback callback);
}
diff --git a/telephony/java/com/android/internal/telephony/ISmsImplBase.java b/telephony/java/com/android/internal/telephony/ISmsImplBase.java
index f2f2960..12c5c30 100644
--- a/telephony/java/com/android/internal/telephony/ISmsImplBase.java
+++ b/telephony/java/com/android/internal/telephony/ISmsImplBase.java
@@ -18,6 +18,8 @@
import android.app.PendingIntent;
import android.net.Uri;
+import android.os.Bundle;
+import android.telephony.IFinancialSmsCallback;
import java.util.List;
@@ -194,4 +196,10 @@
int subId, String callingPkg, String prefixes, PendingIntent intent) {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public void getSmsMessagesForFinancialApp(
+ int subId, String callingPkg, Bundle params, IFinancialSmsCallback callback) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 9300034..d9b206f 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -268,31 +268,11 @@
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 b381cbf..0b3bd70 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 0f49608..2581e08 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.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 @@
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 @@
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 @@
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 @@
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 @@
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 0000000..0437a28
--- /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/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index ec07037..86af642 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -26,8 +26,8 @@
import android.support.test.InstrumentationRegistry;
import com.android.server.PackageWatchdog.PackageHealthObserver;
+import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -45,23 +45,22 @@
public class PackageWatchdogTest {
private static final String APP_A = "com.package.a";
private static final String APP_B = "com.package.b";
+ private static final String APP_C = "com.package.c";
+ private static final String APP_D = "com.package.d";
private static final String OBSERVER_NAME_1 = "observer1";
private static final String OBSERVER_NAME_2 = "observer2";
private static final String OBSERVER_NAME_3 = "observer3";
+ private static final String OBSERVER_NAME_4 = "observer4";
private static final long SHORT_DURATION = TimeUnit.SECONDS.toMillis(1);
private static final long LONG_DURATION = TimeUnit.SECONDS.toMillis(5);
private TestLooper mTestLooper;
@Before
public void setUp() throws Exception {
- mTestLooper = new TestLooper();
- mTestLooper.startAutoDispatch();
- }
-
- @After
- public void tearDown() throws Exception {
new File(InstrumentationRegistry.getContext().getFilesDir(),
"package-watchdog.xml").delete();
+ mTestLooper = new TestLooper();
+ mTestLooper.startAutoDispatch();
}
/**
@@ -154,7 +153,6 @@
assertTrue(watchdog1.getPackages(observer2).contains(APP_A));
assertTrue(watchdog1.getPackages(observer2).contains(APP_B));
-
// Then advance time and run IO Handler so file is saved
mTestLooper.dispatchAll();
@@ -198,47 +196,191 @@
watchdog.onPackageFailure(new String[]{APP_A});
}
+ // Run handler so package failures are dispatched to observers
+ mTestLooper.dispatchAll();
+
// Verify that observers are not notified
assertEquals(0, observer1.mFailedPackages.size());
assertEquals(0, observer2.mFailedPackages.size());
}
/**
- * Test package failure and notifies all observer since none handles the failure
+ * Test package failure and does not notify any observer because they are not observing
+ * the failed packages.
*/
@Test
- public void testPackageFailureNotifyAll() throws Exception {
+ public void testPackageFailureNotifyNone() throws Exception {
PackageWatchdog watchdog = createWatchdog();
- TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
- TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
+ TestObserver observer1 = new TestObserver(OBSERVER_NAME_1,
+ PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ TestObserver observer2 = new TestObserver(OBSERVER_NAME_2,
+ PackageHealthObserverImpact.USER_IMPACT_HIGH);
- // Start observing for observer1 and observer2 without handling failures
+
watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
+ watchdog.startObservingHealth(observer1, Arrays.asList(APP_B), SHORT_DURATION);
- // Then fail APP_A and APP_B above the threshold
+ // Then fail APP_C (not observed) above the threshold
for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) {
- watchdog.onPackageFailure(new String[]{APP_A, APP_B});
+ watchdog.onPackageFailure(new String[]{APP_C});
}
- // Verify all observers are notifed of all package failures
- List<String> observer1Packages = observer1.mFailedPackages;
- List<String> observer2Packages = observer2.mFailedPackages;
- assertEquals(2, observer1Packages.size());
- assertEquals(1, observer2Packages.size());
- assertEquals(APP_A, observer1Packages.get(0));
- assertEquals(APP_B, observer1Packages.get(1));
- assertEquals(APP_A, observer2Packages.get(0));
+ // Run handler so package failures are dispatched to observers
+ mTestLooper.dispatchAll();
+
+ // Verify that observers are not notified
+ assertEquals(0, observer1.mFailedPackages.size());
+ assertEquals(0, observer2.mFailedPackages.size());
}
/**
- * Test package failure and notifies only one observer because it handles the failure
+ * Test package failure and notifies only least impact observers.
*/
@Test
- public void testPackageFailureNotifyOne() throws Exception {
+ public void testPackageFailureNotifyAllDifferentImpacts() throws Exception {
PackageWatchdog watchdog = createWatchdog();
- TestObserver observer1 = new TestObserver(OBSERVER_NAME_1, true /* shouldHandle */);
- TestObserver observer2 = new TestObserver(OBSERVER_NAME_2, true /* shouldHandle */);
+ TestObserver observerNone = new TestObserver(OBSERVER_NAME_1,
+ PackageHealthObserverImpact.USER_IMPACT_NONE);
+ TestObserver observerHigh = new TestObserver(OBSERVER_NAME_2,
+ PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ TestObserver observerMid = new TestObserver(OBSERVER_NAME_3,
+ PackageHealthObserverImpact.USER_IMPACT_MEDIUM);
+ TestObserver observerLow = new TestObserver(OBSERVER_NAME_4,
+ PackageHealthObserverImpact.USER_IMPACT_LOW);
+
+ // Start observing for all impact observers
+ watchdog.startObservingHealth(observerNone, Arrays.asList(APP_A, APP_B, APP_C, APP_D),
+ SHORT_DURATION);
+ watchdog.startObservingHealth(observerHigh, Arrays.asList(APP_A, APP_B, APP_C),
+ SHORT_DURATION);
+ watchdog.startObservingHealth(observerMid, Arrays.asList(APP_A, APP_B),
+ SHORT_DURATION);
+ watchdog.startObservingHealth(observerLow, Arrays.asList(APP_A),
+ SHORT_DURATION);
+
+ // Then fail all apps above the threshold
+ for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) {
+ watchdog.onPackageFailure(new String[]{APP_A, APP_B, APP_C, APP_D});
+ }
+
+ // Run handler so package failures are dispatched to observers
+ mTestLooper.dispatchAll();
+
+ // Verify least impact observers are notifed of package failures
+ List<String> observerNonePackages = observerNone.mFailedPackages;
+ List<String> observerHighPackages = observerHigh.mFailedPackages;
+ List<String> observerMidPackages = observerMid.mFailedPackages;
+ List<String> observerLowPackages = observerLow.mFailedPackages;
+
+ // APP_D failure observed by only observerNone is not caught cos its impact is none
+ assertEquals(0, observerNonePackages.size());
+ // APP_C failure is caught by observerHigh cos it's the lowest impact observer
+ assertEquals(1, observerHighPackages.size());
+ assertEquals(APP_C, observerHighPackages.get(0));
+ // APP_B failure is caught by observerMid cos it's the lowest impact observer
+ assertEquals(1, observerMidPackages.size());
+ assertEquals(APP_B, observerMidPackages.get(0));
+ // APP_A failure is caught by observerLow cos it's the lowest impact observer
+ assertEquals(1, observerLowPackages.size());
+ assertEquals(APP_A, observerLowPackages.get(0));
+ }
+
+ /**
+ * Test package failure and least impact observers are notified successively.
+ * State transistions:
+ *
+ * <ul>
+ * <li>(observer1:low, observer2:mid) -> {observer1}
+ * <li>(observer1:high, observer2:mid) -> {observer2}
+ * <li>(observer1:high, observer2:none) -> {observer1}
+ * <li>(observer1:none, observer2:none) -> {}
+ * <ul>
+ */
+ @Test
+ public void testPackageFailureNotifyLeastSuccessively() throws Exception {
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver observerFirst = new TestObserver(OBSERVER_NAME_1,
+ PackageHealthObserverImpact.USER_IMPACT_LOW);
+ TestObserver observerSecond = new TestObserver(OBSERVER_NAME_2,
+ PackageHealthObserverImpact.USER_IMPACT_MEDIUM);
+
+ // Start observing for observerFirst and observerSecond with failure handling
+ watchdog.startObservingHealth(observerFirst, Arrays.asList(APP_A), LONG_DURATION);
+ watchdog.startObservingHealth(observerSecond, Arrays.asList(APP_A), LONG_DURATION);
+
+ // Then fail APP_A above the threshold
+ for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) {
+ watchdog.onPackageFailure(new String[]{APP_A});
+ }
+ // Run handler so package failures are dispatched to observers
+ mTestLooper.dispatchAll();
+
+ // Verify only observerFirst is notifed
+ assertEquals(1, observerFirst.mFailedPackages.size());
+ assertEquals(APP_A, observerFirst.mFailedPackages.get(0));
+ assertEquals(0, observerSecond.mFailedPackages.size());
+
+ // After observerFirst handles failure, next action it has is high impact
+ observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_HIGH;
+ observerFirst.mFailedPackages.clear();
+ observerSecond.mFailedPackages.clear();
+
+ // Then fail APP_A again above the threshold
+ for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) {
+ watchdog.onPackageFailure(new String[]{APP_A});
+ }
+ // Run handler so package failures are dispatched to observers
+ mTestLooper.dispatchAll();
+
+ // Verify only observerSecond is notifed cos it has least impact
+ assertEquals(1, observerSecond.mFailedPackages.size());
+ assertEquals(APP_A, observerSecond.mFailedPackages.get(0));
+ assertEquals(0, observerFirst.mFailedPackages.size());
+
+ // After observerSecond handles failure, it has no further actions
+ observerSecond.mImpact = PackageHealthObserverImpact.USER_IMPACT_NONE;
+ observerFirst.mFailedPackages.clear();
+ observerSecond.mFailedPackages.clear();
+
+ // Then fail APP_A again above the threshold
+ for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) {
+ watchdog.onPackageFailure(new String[]{APP_A});
+ }
+ // Run handler so package failures are dispatched to observers
+ mTestLooper.dispatchAll();
+
+ // Verify only observerFirst is notifed cos it has the only action
+ assertEquals(1, observerFirst.mFailedPackages.size());
+ assertEquals(APP_A, observerFirst.mFailedPackages.get(0));
+ assertEquals(0, observerSecond.mFailedPackages.size());
+
+ // After observerFirst handles failure, it too has no further actions
+ observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_NONE;
+ observerFirst.mFailedPackages.clear();
+ observerSecond.mFailedPackages.clear();
+
+ // Then fail APP_A again above the threshold
+ for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) {
+ watchdog.onPackageFailure(new String[]{APP_A});
+ }
+ // Run handler so package failures are dispatched to observers
+ mTestLooper.dispatchAll();
+
+ // Verify no observer is notified cos no actions left
+ assertEquals(0, observerFirst.mFailedPackages.size());
+ assertEquals(0, observerSecond.mFailedPackages.size());
+ }
+
+ /**
+ * Test package failure and notifies only one observer even with observer impact tie.
+ */
+ @Test
+ public void testPackageFailureNotifyOneSameImpact() throws Exception {
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver observer1 = new TestObserver(OBSERVER_NAME_1,
+ PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ TestObserver observer2 = new TestObserver(OBSERVER_NAME_2,
+ PackageHealthObserverImpact.USER_IMPACT_HIGH);
// Start observing for observer1 and observer2 with failure handling
watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
@@ -249,6 +391,9 @@
watchdog.onPackageFailure(new String[]{APP_A});
}
+ // Run handler so package failures are dispatched to observers
+ mTestLooper.dispatchAll();
+
// Verify only one observer is notifed
assertEquals(1, observer1.mFailedPackages.size());
assertEquals(APP_A, observer1.mFailedPackages.get(0));
@@ -262,21 +407,26 @@
private static class TestObserver implements PackageHealthObserver {
private final String mName;
- private boolean mShouldHandle;
+ private int mImpact;
final List<String> mFailedPackages = new ArrayList<>();
TestObserver(String name) {
mName = name;
+ mImpact = PackageHealthObserverImpact.USER_IMPACT_MEDIUM;
}
- TestObserver(String name, boolean shouldHandle) {
+ TestObserver(String name, int impact) {
mName = name;
- mShouldHandle = shouldHandle;
+ mImpact = impact;
}
- public boolean onHealthCheckFailed(String packageName) {
+ public int onHealthCheckFailed(String packageName) {
+ return mImpact;
+ }
+
+ public boolean execute(String packageName) {
mFailedPackages.add(packageName);
- return mShouldHandle;
+ return true;
}
public String getName() {
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 7ab716f..0493ef8 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -139,7 +139,7 @@
assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS));
// Roll back the app.
- RollbackTestUtils.rollback(rollback);
+ RollbackTestUtils.rollback(rollback.getRollbackId());
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
// Verify we received a broadcast for the rollback.
@@ -211,7 +211,7 @@
assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB);
// Rollback of B should not rollback A
- RollbackTestUtils.rollback(rollbackB);
+ RollbackTestUtils.rollback(rollbackB.getRollbackId());
assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
} finally {
@@ -268,7 +268,7 @@
assertRollbackInfoForAandB(rollbackB);
// Rollback of B should rollback A as well
- RollbackTestUtils.rollback(rollbackB);
+ RollbackTestUtils.rollback(rollbackB.getRollbackId());
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
} finally {
@@ -303,7 +303,7 @@
rm.getAvailableRollbacks(), TEST_APP_A);
// Roll back the app.
- RollbackTestUtils.rollback(rollback);
+ RollbackTestUtils.rollback(rollback.getRollbackId());
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
// Verify the recent rollback has been recorded.
@@ -430,7 +430,7 @@
RollbackManager rm = RollbackTestUtils.getRollbackManager();
RollbackInfo rollback = getUniqueRollbackInfoForPackage(
rm.getAvailableRollbacks(), TEST_APP_A);
- RollbackTestUtils.rollback(rollback);
+ RollbackTestUtils.rollback(rollback.getRollbackId());
processUserData(TEST_APP_A);
} finally {
RollbackTestUtils.dropShellPermissionIdentity();
@@ -500,7 +500,7 @@
assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB);
// Executing rollback should roll back the correct package.
- RollbackTestUtils.rollback(rollbackA);
+ RollbackTestUtils.rollback(rollbackA.getRollbackId());
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
@@ -509,7 +509,7 @@
RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
- RollbackTestUtils.rollback(rollbackB);
+ RollbackTestUtils.rollback(rollbackB.getRollbackId());
assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
} finally {
@@ -544,7 +544,7 @@
try {
// TODO: What if the implementation checks arguments for non-null
// first? Then this test isn't valid.
- rm.commitRollback(null, null);
+ rm.commitRollback(0, null);
fail("expected SecurityException");
} catch (SecurityException e) {
// Expected.
@@ -598,7 +598,7 @@
// Rollback the app. It should cause both test apps to be rolled
// back.
- RollbackTestUtils.rollback(rollback);
+ RollbackTestUtils.rollback(rollback.getRollbackId());
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
index f481897..1478657 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
@@ -21,7 +21,6 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
-import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import android.support.test.InstrumentationRegistry;
@@ -93,9 +92,9 @@
* Commit the given rollback.
* @throws AssertionError if the rollback fails.
*/
- static void rollback(RollbackInfo rollback) throws InterruptedException {
+ static void rollback(int rollbackId) throws InterruptedException {
RollbackManager rm = getRollbackManager();
- rm.commitRollback(rollback, LocalIntentSender.getIntentSender());
+ rm.commitRollback(rollbackId, LocalIntentSender.getIntentSender());
assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
}
diff --git a/tests/net/java/android/net/NetworkUtilsTest.java b/tests/net/java/android/net/NetworkUtilsTest.java
index 3452819..ba6e0f2 100644
--- a/tests/net/java/android/net/NetworkUtilsTest.java
+++ b/tests/net/java/android/net/NetworkUtilsTest.java
@@ -16,161 +16,19 @@
package android.net;
-import static android.net.NetworkUtils.getImplicitNetmask;
-import static android.net.NetworkUtils.inet4AddressToIntHTH;
-import static android.net.NetworkUtils.inet4AddressToIntHTL;
-import static android.net.NetworkUtils.intToInet4AddressHTH;
-import static android.net.NetworkUtils.intToInet4AddressHTL;
-import static android.net.NetworkUtils.netmaskToPrefixLength;
-import static android.net.NetworkUtils.prefixLengthToV4NetmaskIntHTH;
-import static android.net.NetworkUtils.prefixLengthToV4NetmaskIntHTL;
-import static android.net.NetworkUtils.getBroadcastAddress;
-import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
-
import static junit.framework.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
import android.support.test.runner.AndroidJUnit4;
-import java.math.BigInteger;
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.util.TreeSet;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.math.BigInteger;
+import java.util.TreeSet;
+
@RunWith(AndroidJUnit4.class)
@android.support.test.filters.SmallTest
public class NetworkUtilsTest {
-
- private InetAddress Address(String addr) {
- return InetAddress.parseNumericAddress(addr);
- }
-
- private Inet4Address IPv4Address(String addr) {
- return (Inet4Address) Address(addr);
- }
-
- @Test
- public void testGetImplicitNetmask() {
- assertEquals(8, getImplicitNetmask(IPv4Address("4.2.2.2")));
- assertEquals(8, getImplicitNetmask(IPv4Address("10.5.6.7")));
- assertEquals(16, getImplicitNetmask(IPv4Address("173.194.72.105")));
- assertEquals(16, getImplicitNetmask(IPv4Address("172.23.68.145")));
- assertEquals(24, getImplicitNetmask(IPv4Address("192.0.2.1")));
- assertEquals(24, getImplicitNetmask(IPv4Address("192.168.5.1")));
- assertEquals(32, getImplicitNetmask(IPv4Address("224.0.0.1")));
- assertEquals(32, getImplicitNetmask(IPv4Address("255.6.7.8")));
- }
-
- private void assertInvalidNetworkMask(Inet4Address addr) {
- try {
- netmaskToPrefixLength(addr);
- fail("Invalid netmask " + addr.getHostAddress() + " did not cause exception");
- } catch (IllegalArgumentException expected) {
- }
- }
-
- @Test
- public void testInet4AddressToIntHTL() {
- assertEquals(0, inet4AddressToIntHTL(IPv4Address("0.0.0.0")));
- assertEquals(0x000080ff, inet4AddressToIntHTL(IPv4Address("255.128.0.0")));
- assertEquals(0x0080ff0a, inet4AddressToIntHTL(IPv4Address("10.255.128.0")));
- assertEquals(0x00feff0a, inet4AddressToIntHTL(IPv4Address("10.255.254.0")));
- assertEquals(0xfeffa8c0, inet4AddressToIntHTL(IPv4Address("192.168.255.254")));
- assertEquals(0xffffa8c0, inet4AddressToIntHTL(IPv4Address("192.168.255.255")));
- }
-
- @Test
- public void testIntToInet4AddressHTL() {
- assertEquals(IPv4Address("0.0.0.0"), intToInet4AddressHTL(0));
- assertEquals(IPv4Address("255.128.0.0"), intToInet4AddressHTL(0x000080ff));
- assertEquals(IPv4Address("10.255.128.0"), intToInet4AddressHTL(0x0080ff0a));
- assertEquals(IPv4Address("10.255.254.0"), intToInet4AddressHTL(0x00feff0a));
- assertEquals(IPv4Address("192.168.255.254"), intToInet4AddressHTL(0xfeffa8c0));
- assertEquals(IPv4Address("192.168.255.255"), intToInet4AddressHTL(0xffffa8c0));
- }
-
- @Test
- public void testInet4AddressToIntHTH() {
- assertEquals(0, inet4AddressToIntHTH(IPv4Address("0.0.0.0")));
- assertEquals(0xff800000, inet4AddressToIntHTH(IPv4Address("255.128.0.0")));
- assertEquals(0x0aff8000, inet4AddressToIntHTH(IPv4Address("10.255.128.0")));
- assertEquals(0x0afffe00, inet4AddressToIntHTH(IPv4Address("10.255.254.0")));
- assertEquals(0xc0a8fffe, inet4AddressToIntHTH(IPv4Address("192.168.255.254")));
- assertEquals(0xc0a8ffff, inet4AddressToIntHTH(IPv4Address("192.168.255.255")));
- }
-
- @Test
- public void testIntToInet4AddressHTH() {
- assertEquals(IPv4Address("0.0.0.0"), intToInet4AddressHTH(0));
- assertEquals(IPv4Address("255.128.0.0"), intToInet4AddressHTH(0xff800000));
- assertEquals(IPv4Address("10.255.128.0"), intToInet4AddressHTH(0x0aff8000));
- assertEquals(IPv4Address("10.255.254.0"), intToInet4AddressHTH(0x0afffe00));
- assertEquals(IPv4Address("192.168.255.254"), intToInet4AddressHTH(0xc0a8fffe));
- assertEquals(IPv4Address("192.168.255.255"), intToInet4AddressHTH(0xc0a8ffff));
- }
-
- @Test
- public void testNetmaskToPrefixLength() {
- assertEquals(0, netmaskToPrefixLength(IPv4Address("0.0.0.0")));
- assertEquals(9, netmaskToPrefixLength(IPv4Address("255.128.0.0")));
- assertEquals(17, netmaskToPrefixLength(IPv4Address("255.255.128.0")));
- assertEquals(23, netmaskToPrefixLength(IPv4Address("255.255.254.0")));
- assertEquals(31, netmaskToPrefixLength(IPv4Address("255.255.255.254")));
- assertEquals(32, netmaskToPrefixLength(IPv4Address("255.255.255.255")));
-
- assertInvalidNetworkMask(IPv4Address("0.0.0.1"));
- assertInvalidNetworkMask(IPv4Address("255.255.255.253"));
- assertInvalidNetworkMask(IPv4Address("255.255.0.255"));
- }
-
- @Test
- public void testPrefixLengthToV4NetmaskIntHTL() {
- assertEquals(0, prefixLengthToV4NetmaskIntHTL(0));
- assertEquals(0x000080ff /* 255.128.0.0 */, prefixLengthToV4NetmaskIntHTL(9));
- assertEquals(0x0080ffff /* 255.255.128.0 */, prefixLengthToV4NetmaskIntHTL(17));
- assertEquals(0x00feffff /* 255.255.254.0 */, prefixLengthToV4NetmaskIntHTL(23));
- assertEquals(0xfeffffff /* 255.255.255.254 */, prefixLengthToV4NetmaskIntHTL(31));
- assertEquals(0xffffffff /* 255.255.255.255 */, prefixLengthToV4NetmaskIntHTL(32));
- }
-
- @Test
- public void testPrefixLengthToV4NetmaskIntHTH() {
- assertEquals(0, prefixLengthToV4NetmaskIntHTH(0));
- assertEquals(0xff800000 /* 255.128.0.0 */, prefixLengthToV4NetmaskIntHTH(9));
- assertEquals(0xffff8000 /* 255.255.128.0 */, prefixLengthToV4NetmaskIntHTH(17));
- assertEquals(0xfffffe00 /* 255.255.254.0 */, prefixLengthToV4NetmaskIntHTH(23));
- assertEquals(0xfffffffe /* 255.255.255.254 */, prefixLengthToV4NetmaskIntHTH(31));
- assertEquals(0xffffffff /* 255.255.255.255 */, prefixLengthToV4NetmaskIntHTH(32));
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testPrefixLengthToV4NetmaskIntHTH_NegativeLength() {
- prefixLengthToV4NetmaskIntHTH(-1);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testPrefixLengthToV4NetmaskIntHTH_LengthTooLarge() {
- prefixLengthToV4NetmaskIntHTH(33);
- }
-
- private void checkAddressMasking(String expectedAddr, String addr, int prefixLength) {
- final int prefix = prefixLengthToV4NetmaskIntHTH(prefixLength);
- final int addrInt = inet4AddressToIntHTH(IPv4Address(addr));
- assertEquals(IPv4Address(expectedAddr), intToInet4AddressHTH(prefix & addrInt));
- }
-
- @Test
- public void testPrefixLengthToV4NetmaskIntHTH_MaskAddr() {
- checkAddressMasking("192.168.0.0", "192.168.128.1", 16);
- checkAddressMasking("255.240.0.0", "255.255.255.255", 12);
- checkAddressMasking("255.255.255.255", "255.255.255.255", 32);
- checkAddressMasking("0.0.0.0", "255.255.255.255", 0);
- }
-
@Test
public void testRoutedIPv4AddressCount() {
final TreeSet<IpPrefix> set = new TreeSet<>(IpPrefix.lengthComparator());
@@ -267,44 +125,4 @@
assertEquals(BigInteger.valueOf(7l - 4 + 4 + 16 + 65536),
NetworkUtils.routedIPv6AddressCount(set));
}
-
- @Test
- public void testGetPrefixMaskAsAddress() {
- assertEquals("255.255.240.0", getPrefixMaskAsInet4Address(20).getHostAddress());
- assertEquals("255.0.0.0", getPrefixMaskAsInet4Address(8).getHostAddress());
- assertEquals("0.0.0.0", getPrefixMaskAsInet4Address(0).getHostAddress());
- assertEquals("255.255.255.255", getPrefixMaskAsInet4Address(32).getHostAddress());
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testGetPrefixMaskAsAddress_PrefixTooLarge() {
- getPrefixMaskAsInet4Address(33);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testGetPrefixMaskAsAddress_NegativePrefix() {
- getPrefixMaskAsInet4Address(-1);
- }
-
- @Test
- public void testGetBroadcastAddress() {
- assertEquals("192.168.15.255",
- getBroadcastAddress(IPv4Address("192.168.0.123"), 20).getHostAddress());
- assertEquals("192.255.255.255",
- getBroadcastAddress(IPv4Address("192.168.0.123"), 8).getHostAddress());
- assertEquals("192.168.0.123",
- getBroadcastAddress(IPv4Address("192.168.0.123"), 32).getHostAddress());
- assertEquals("255.255.255.255",
- getBroadcastAddress(IPv4Address("192.168.0.123"), 0).getHostAddress());
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testGetBroadcastAddress_PrefixTooLarge() {
- getBroadcastAddress(IPv4Address("192.168.0.123"), 33);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testGetBroadcastAddress_NegativePrefix() {
- getBroadcastAddress(IPv4Address("192.168.0.123"), -1);
- }
}
diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/tests/net/java/android/net/ip/IpServerTest.java
index 80aac04..f7542a7 100644
--- a/tests/net/java/android/net/ip/IpServerTest.java
+++ b/tests/net/java/android/net/ip/IpServerTest.java
@@ -22,11 +22,11 @@
import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
-import static android.net.NetworkUtils.intToInet4AddressHTH;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.ip.IpServer.STATE_AVAILABLE;
import static android.net.ip.IpServer.STATE_TETHERED;
import static android.net.ip.IpServer.STATE_UNAVAILABLE;
+import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
diff --git a/tests/net/java/android/net/shared/Inet4AddressUtilsTest.java b/tests/net/java/android/net/shared/Inet4AddressUtilsTest.java
new file mode 100644
index 0000000..6da8514
--- /dev/null
+++ b/tests/net/java/android/net/shared/Inet4AddressUtilsTest.java
@@ -0,0 +1,209 @@
+/*
+ * 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.net.shared.Inet4AddressUtils.getBroadcastAddress;
+import static android.net.shared.Inet4AddressUtils.getImplicitNetmask;
+import static android.net.shared.Inet4AddressUtils.getPrefixMaskAsInet4Address;
+import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH;
+import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTL;
+import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
+import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTL;
+import static android.net.shared.Inet4AddressUtils.netmaskToPrefixLength;
+import static android.net.shared.Inet4AddressUtils.prefixLengthToV4NetmaskIntHTH;
+import static android.net.shared.Inet4AddressUtils.prefixLengthToV4NetmaskIntHTL;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.junit.Assert.fail;
+
+import android.net.InetAddresses;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class Inet4AddressUtilsTest {
+
+ @Test
+ public void testInet4AddressToIntHTL() {
+ assertEquals(0, inet4AddressToIntHTL(ipv4Address("0.0.0.0")));
+ assertEquals(0x000080ff, inet4AddressToIntHTL(ipv4Address("255.128.0.0")));
+ assertEquals(0x0080ff0a, inet4AddressToIntHTL(ipv4Address("10.255.128.0")));
+ assertEquals(0x00feff0a, inet4AddressToIntHTL(ipv4Address("10.255.254.0")));
+ assertEquals(0xfeffa8c0, inet4AddressToIntHTL(ipv4Address("192.168.255.254")));
+ assertEquals(0xffffa8c0, inet4AddressToIntHTL(ipv4Address("192.168.255.255")));
+ }
+
+ @Test
+ public void testIntToInet4AddressHTL() {
+ assertEquals(ipv4Address("0.0.0.0"), intToInet4AddressHTL(0));
+ assertEquals(ipv4Address("255.128.0.0"), intToInet4AddressHTL(0x000080ff));
+ assertEquals(ipv4Address("10.255.128.0"), intToInet4AddressHTL(0x0080ff0a));
+ assertEquals(ipv4Address("10.255.254.0"), intToInet4AddressHTL(0x00feff0a));
+ assertEquals(ipv4Address("192.168.255.254"), intToInet4AddressHTL(0xfeffa8c0));
+ assertEquals(ipv4Address("192.168.255.255"), intToInet4AddressHTL(0xffffa8c0));
+ }
+
+ @Test
+ public void testInet4AddressToIntHTH() {
+ assertEquals(0, inet4AddressToIntHTH(ipv4Address("0.0.0.0")));
+ assertEquals(0xff800000, inet4AddressToIntHTH(ipv4Address("255.128.0.0")));
+ assertEquals(0x0aff8000, inet4AddressToIntHTH(ipv4Address("10.255.128.0")));
+ assertEquals(0x0afffe00, inet4AddressToIntHTH(ipv4Address("10.255.254.0")));
+ assertEquals(0xc0a8fffe, inet4AddressToIntHTH(ipv4Address("192.168.255.254")));
+ assertEquals(0xc0a8ffff, inet4AddressToIntHTH(ipv4Address("192.168.255.255")));
+ }
+
+ @Test
+ public void testIntToInet4AddressHTH() {
+ assertEquals(ipv4Address("0.0.0.0"), intToInet4AddressHTH(0));
+ assertEquals(ipv4Address("255.128.0.0"), intToInet4AddressHTH(0xff800000));
+ assertEquals(ipv4Address("10.255.128.0"), intToInet4AddressHTH(0x0aff8000));
+ assertEquals(ipv4Address("10.255.254.0"), intToInet4AddressHTH(0x0afffe00));
+ assertEquals(ipv4Address("192.168.255.254"), intToInet4AddressHTH(0xc0a8fffe));
+ assertEquals(ipv4Address("192.168.255.255"), intToInet4AddressHTH(0xc0a8ffff));
+ }
+
+
+ @Test
+ public void testPrefixLengthToV4NetmaskIntHTL() {
+ assertEquals(0, prefixLengthToV4NetmaskIntHTL(0));
+ assertEquals(0x000080ff /* 255.128.0.0 */, prefixLengthToV4NetmaskIntHTL(9));
+ assertEquals(0x0080ffff /* 255.255.128.0 */, prefixLengthToV4NetmaskIntHTL(17));
+ assertEquals(0x00feffff /* 255.255.254.0 */, prefixLengthToV4NetmaskIntHTL(23));
+ assertEquals(0xfeffffff /* 255.255.255.254 */, prefixLengthToV4NetmaskIntHTL(31));
+ assertEquals(0xffffffff /* 255.255.255.255 */, prefixLengthToV4NetmaskIntHTL(32));
+ }
+
+ @Test
+ public void testPrefixLengthToV4NetmaskIntHTH() {
+ assertEquals(0, prefixLengthToV4NetmaskIntHTH(0));
+ assertEquals(0xff800000 /* 255.128.0.0 */, prefixLengthToV4NetmaskIntHTH(9));
+ assertEquals(0xffff8000 /* 255.255.128.0 */, prefixLengthToV4NetmaskIntHTH(17));
+ assertEquals(0xfffffe00 /* 255.255.254.0 */, prefixLengthToV4NetmaskIntHTH(23));
+ assertEquals(0xfffffffe /* 255.255.255.254 */, prefixLengthToV4NetmaskIntHTH(31));
+ assertEquals(0xffffffff /* 255.255.255.255 */, prefixLengthToV4NetmaskIntHTH(32));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testPrefixLengthToV4NetmaskIntHTH_NegativeLength() {
+ prefixLengthToV4NetmaskIntHTH(-1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testPrefixLengthToV4NetmaskIntHTH_LengthTooLarge() {
+ prefixLengthToV4NetmaskIntHTH(33);
+ }
+
+ private void checkAddressMasking(String expectedAddr, String addr, int prefixLength) {
+ final int prefix = prefixLengthToV4NetmaskIntHTH(prefixLength);
+ final int addrInt = inet4AddressToIntHTH(ipv4Address(addr));
+ assertEquals(ipv4Address(expectedAddr), intToInet4AddressHTH(prefix & addrInt));
+ }
+
+ @Test
+ public void testPrefixLengthToV4NetmaskIntHTH_MaskAddr() {
+ checkAddressMasking("192.168.0.0", "192.168.128.1", 16);
+ checkAddressMasking("255.240.0.0", "255.255.255.255", 12);
+ checkAddressMasking("255.255.255.255", "255.255.255.255", 32);
+ checkAddressMasking("0.0.0.0", "255.255.255.255", 0);
+ }
+
+ @Test
+ public void testGetImplicitNetmask() {
+ assertEquals(8, getImplicitNetmask(ipv4Address("4.2.2.2")));
+ assertEquals(8, getImplicitNetmask(ipv4Address("10.5.6.7")));
+ assertEquals(16, getImplicitNetmask(ipv4Address("173.194.72.105")));
+ assertEquals(16, getImplicitNetmask(ipv4Address("172.23.68.145")));
+ assertEquals(24, getImplicitNetmask(ipv4Address("192.0.2.1")));
+ assertEquals(24, getImplicitNetmask(ipv4Address("192.168.5.1")));
+ assertEquals(32, getImplicitNetmask(ipv4Address("224.0.0.1")));
+ assertEquals(32, getImplicitNetmask(ipv4Address("255.6.7.8")));
+ }
+
+ private void assertInvalidNetworkMask(Inet4Address addr) {
+ try {
+ netmaskToPrefixLength(addr);
+ fail("Invalid netmask " + addr.getHostAddress() + " did not cause exception");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testNetmaskToPrefixLength() {
+ assertEquals(0, netmaskToPrefixLength(ipv4Address("0.0.0.0")));
+ assertEquals(9, netmaskToPrefixLength(ipv4Address("255.128.0.0")));
+ assertEquals(17, netmaskToPrefixLength(ipv4Address("255.255.128.0")));
+ assertEquals(23, netmaskToPrefixLength(ipv4Address("255.255.254.0")));
+ assertEquals(31, netmaskToPrefixLength(ipv4Address("255.255.255.254")));
+ assertEquals(32, netmaskToPrefixLength(ipv4Address("255.255.255.255")));
+
+ assertInvalidNetworkMask(ipv4Address("0.0.0.1"));
+ assertInvalidNetworkMask(ipv4Address("255.255.255.253"));
+ assertInvalidNetworkMask(ipv4Address("255.255.0.255"));
+ }
+
+ @Test
+ public void testGetPrefixMaskAsAddress() {
+ assertEquals("255.255.240.0", getPrefixMaskAsInet4Address(20).getHostAddress());
+ assertEquals("255.0.0.0", getPrefixMaskAsInet4Address(8).getHostAddress());
+ assertEquals("0.0.0.0", getPrefixMaskAsInet4Address(0).getHostAddress());
+ assertEquals("255.255.255.255", getPrefixMaskAsInet4Address(32).getHostAddress());
+ }
+
+ @Test
+ public void testGetBroadcastAddress() {
+ assertEquals("192.168.15.255",
+ getBroadcastAddress(ipv4Address("192.168.0.123"), 20).getHostAddress());
+ assertEquals("192.255.255.255",
+ getBroadcastAddress(ipv4Address("192.168.0.123"), 8).getHostAddress());
+ assertEquals("192.168.0.123",
+ getBroadcastAddress(ipv4Address("192.168.0.123"), 32).getHostAddress());
+ assertEquals("255.255.255.255",
+ getBroadcastAddress(ipv4Address("192.168.0.123"), 0).getHostAddress());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetBroadcastAddress_PrefixTooLarge() {
+ getBroadcastAddress(ipv4Address("192.168.0.123"), 33);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetBroadcastAddress_NegativePrefix() {
+ getBroadcastAddress(ipv4Address("192.168.0.123"), -1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetPrefixMaskAsAddress_PrefixTooLarge() {
+ getPrefixMaskAsInet4Address(33);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetPrefixMaskAsAddress_NegativePrefix() {
+ getPrefixMaskAsInet4Address(-1);
+ }
+
+ private Inet4Address ipv4Address(String addr) {
+ return (Inet4Address) InetAddresses.parseNumericAddress(addr);
+ }
+}
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 fc4cd01..2690ee8 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 @@
}
/**
+ * 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 f1ebb87..6f58893 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 @@
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 @@
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 @@
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 @@
* An enumeration packed into an {@code int}.
*
* @see android.view.inspector.IntEnumMapping
+ * @see IntEnumEntry
*/
INT_ENUM,
@@ -188,8 +227,74 @@
* 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 f666be7..42ae890 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 @@
/**
* 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 @@
/**
* @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 @@
* 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 @@
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 @@
/**
* 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 @@
.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 @@
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 @@
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 @@
* 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 @@
}
/**
+ * 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 @@
*
* @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 @@
}
/**
+ * 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 dd4d8f5..7b04645 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 @@
"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 @@
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 @@
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 @@
}
/**
+ * 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 @@
}
}
+ 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 455f5b0..01d9430 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.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 @@
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 b0775dc..f6d8bb0 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 java.io.IOException;
import java.net.URL;
+import java.util.Arrays;
import java.util.Optional;
/**
@@ -40,7 +43,7 @@
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 @@
@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 @@
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 0000000..764aa8b
--- /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 0000000..75f2813
--- /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 2fc242c..0cac462 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 @@
}
@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 23d0f78..ce0f867 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 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 @@
}
@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 1142548..f7357fe 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.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 @@
}
@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 57eb080..dfc1bce 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 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 @@
}
@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 6b6ce21..d72cdd5 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 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 @@
}
@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 0668239..e5733ed 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1221,7 +1221,6 @@
* @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 @@
*
* @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 @@
*
* @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 @@
* 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 @@
* 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 @@
* 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