diff options
164 files changed, 4611 insertions, 1483 deletions
diff --git a/Android.bp b/Android.bp index a5cc89cd0ea1..6964dafced1b 100644 --- a/Android.bp +++ b/Android.bp @@ -271,6 +271,7 @@ java_defaults { "core/java/android/os/IThermalService.aidl", "core/java/android/os/IUpdateLock.aidl", "core/java/android/os/IUserManager.aidl", + ":libvibrator_aidl", "core/java/android/os/IVibratorService.aidl", "core/java/android/os/storage/IStorageManager.aidl", "core/java/android/os/storage/IStorageEventListener.aidl", @@ -561,6 +562,7 @@ java_defaults { "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", @@ -726,7 +728,7 @@ java_defaults { "ext", ], - jarjar_rules: ":framework-hidl-jarjar", + jarjar_rules: "jarjar_rules_hidl.txt", static_libs: [ "apex_aidl_interface-java", @@ -738,6 +740,15 @@ java_defaults { "android.hardware.cas-V1.0-java", "android.hardware.contexthub-V1.0-java", "android.hardware.health-V1.0-java-constants", + "android.hardware.radio-V1.0-java", + "android.hardware.radio-V1.1-java", + "android.hardware.radio-V1.2-java", + "android.hardware.radio-V1.3-java", + "android.hardware.radio-V1.4-java", + "android.hardware.radio.config-V1.0-java", + "android.hardware.radio.config-V1.1-java", + "android.hardware.radio.config-V1.2-java", + "android.hardware.radio.deprecated-V1.0-java", "android.hardware.thermal-V1.0-java-constants", "android.hardware.thermal-V1.0-java", "android.hardware.thermal-V1.1-java", @@ -746,15 +757,12 @@ java_defaults { "android.hardware.usb-V1.0-java-constants", "android.hardware.usb-V1.1-java-constants", "android.hardware.usb-V1.2-java-constants", + "android.hardware.usb.gadget-V1.0-java", "android.hardware.vibrator-V1.0-java", "android.hardware.vibrator-V1.1-java", "android.hardware.vibrator-V1.2-java", "android.hardware.vibrator-V1.3-java", "android.hardware.wifi-V1.0-java-constants", - "android.hardware.radio-V1.0-java", - "android.hardware.radio-V1.3-java", - "android.hardware.radio-V1.4-java", - "android.hardware.usb.gadget-V1.0-java", "networkstack-aidl-interfaces-java", "netd_aidl_interface-java", "devicepolicyprotosnano", @@ -789,8 +797,11 @@ filegroup { } filegroup { - name: "framework-hidl-jarjar", - srcs: ["jarjar_rules_hidl.txt"], + name: "libvibrator_aidl", + srcs: [ + "core/java/android/os/IExternalVibrationController.aidl", + "core/java/android/os/IExternalVibratorService.aidl", + ], } java_library { @@ -910,6 +921,31 @@ aidl_interface { 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 2320a4ccde0f..361ee74becdd 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5101,7 +5101,7 @@ package android.app { } public class KeyguardManager { - method public android.content.Intent createConfirmDeviceCredentialIntent(CharSequence, CharSequence); + method @Deprecated public android.content.Intent createConfirmDeviceCredentialIntent(CharSequence, CharSequence); method @Deprecated @RequiresPermission(android.Manifest.permission.DISABLE_KEYGUARD) public void exitKeyguardSecurely(android.app.KeyguardManager.OnKeyguardExitResult); method @Deprecated public boolean inKeyguardRestrictedInputMode(); method public boolean isDeviceLocked(); @@ -12512,6 +12512,7 @@ package android.database { method public int getInt(int); method public long getLong(int); method public android.net.Uri getNotificationUri(); + method public default java.util.List<android.net.Uri> getNotificationUris(); method public int getPosition(); method public short getShort(int); method public String getString(int); @@ -12535,6 +12536,7 @@ package android.database { method public android.os.Bundle respond(android.os.Bundle); method public void setExtras(android.os.Bundle); method public void setNotificationUri(android.content.ContentResolver, android.net.Uri); + method public default void setNotificationUris(@NonNull android.content.ContentResolver, @NonNull java.util.List<android.net.Uri>); method public void unregisterContentObserver(android.database.ContentObserver); method public void unregisterDataSetObserver(android.database.DataSetObserver); field public static final int FIELD_TYPE_BLOB = 4; // 0x4 @@ -13947,8 +13949,6 @@ package android.graphics { @AnyThread public abstract class ColorSpace { method @NonNull public static android.graphics.ColorSpace adapt(@NonNull android.graphics.ColorSpace, @NonNull @Size(min=2, max=3) float[]); method @NonNull public static android.graphics.ColorSpace adapt(@NonNull android.graphics.ColorSpace, @NonNull @Size(min=2, max=3) float[], @NonNull android.graphics.ColorSpace.Adaptation); - method @NonNull @Size(3) public static float[] cctToIlluminantdXyz(@IntRange(from=1) int); - method @NonNull @Size(9) public static float[] chromaticAdaptation(@NonNull android.graphics.ColorSpace.Adaptation, @NonNull @Size(min=2, max=3) float[], @NonNull @Size(min=2, max=3) float[]); method @NonNull public static android.graphics.ColorSpace.Connector connect(@NonNull android.graphics.ColorSpace, @NonNull android.graphics.ColorSpace); method @NonNull public static android.graphics.ColorSpace.Connector connect(@NonNull android.graphics.ColorSpace, @NonNull android.graphics.ColorSpace, @NonNull android.graphics.ColorSpace.RenderIntent); method @NonNull public static android.graphics.ColorSpace.Connector connect(@NonNull android.graphics.ColorSpace); @@ -16498,6 +16498,7 @@ package android.hardware.biometrics { ctor public BiometricPrompt.Builder(android.content.Context); method public android.hardware.biometrics.BiometricPrompt build(); method public android.hardware.biometrics.BiometricPrompt.Builder setDescription(@NonNull CharSequence); + method public android.hardware.biometrics.BiometricPrompt.Builder setEnableFallback(boolean); method public android.hardware.biometrics.BiometricPrompt.Builder setNegativeButton(@NonNull CharSequence, @NonNull java.util.concurrent.Executor, @NonNull android.content.DialogInterface.OnClickListener); method public android.hardware.biometrics.BiometricPrompt.Builder setRequireConfirmation(boolean); method public android.hardware.biometrics.BiometricPrompt.Builder setSubtitle(@NonNull CharSequence); @@ -23522,6 +23523,8 @@ package android.media { 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); @@ -41409,9 +41412,9 @@ package android.service.notification { 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"; } @@ -44666,12 +44669,14 @@ package android.telephony { public final class SmsManager { method public String createAppSpecificSmsToken(android.app.PendingIntent); + method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent); method public java.util.ArrayList<java.lang.String> divideMessage(String); method public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent); method public android.os.Bundle getCarrierConfigValues(); 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); @@ -44734,6 +44739,11 @@ package android.telephony { 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); @@ -48572,6 +48582,7 @@ package android.view { method public String getName(); method @Deprecated public int getOrientation(); method @Deprecated public int getPixelFormat(); + method @Nullable public android.graphics.ColorSpace getPreferredWideGamutColorSpace(); method public long getPresentationDeadlineNanos(); method public void getRealMetrics(android.util.DisplayMetrics); method public void getRealSize(android.graphics.Point); @@ -55935,10 +55946,10 @@ package android.widget { 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 30d42a232c8d..66e3642a8c05 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -508,11 +508,13 @@ package android.app { method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getStatsMetadata() throws android.app.StatsManager.StatsUnavailableException; method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void removeConfig(long) throws android.app.StatsManager.StatsUnavailableException; method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean removeConfiguration(long); + method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void setActiveConfigsChangedOperation(@Nullable android.app.PendingIntent) throws android.app.StatsManager.StatsUnavailableException; method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void setBroadcastSubscriber(android.app.PendingIntent, long, long) throws android.app.StatsManager.StatsUnavailableException; method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent); method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setDataFetchOperation(long, android.app.PendingIntent); method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void setFetchReportsOperation(android.app.PendingIntent, long) throws android.app.StatsManager.StatsUnavailableException; field public static final String ACTION_STATSD_STARTED = "android.app.action.STATSD_STARTED"; + field public static final String EXTRA_STATS_ACTIVE_CONFIG_KEYS = "android.app.extra.STATS_ACTIVE_CONFIG_KEYS"; field public static final String EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES = "android.app.extra.STATS_BROADCAST_SUBSCRIBER_COOKIES"; field public static final String EXTRA_STATS_CONFIG_KEY = "android.app.extra.STATS_CONFIG_KEY"; field public static final String EXTRA_STATS_CONFIG_UID = "android.app.extra.STATS_CONFIG_UID"; @@ -1265,6 +1267,7 @@ package android.content { 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"; @@ -1303,13 +1306,13 @@ package android.content { field public static final String ACTION_MANAGE_PERMISSION_APPS = "android.intent.action.MANAGE_PERMISSION_APPS"; field @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public static final String ACTION_MANAGE_SPECIAL_APP_ACCESSES = "android.intent.action.MANAGE_SPECIAL_APP_ACCESSES"; field public static final String ACTION_MASTER_CLEAR_NOTIFICATION = "android.intent.action.MASTER_CLEAR_NOTIFICATION"; - field public static final String ACTION_PACKAGE_ROLLBACK_EXECUTED = "android.intent.action.PACKAGE_ROLLBACK_EXECUTED"; field public static final String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED"; field public static final String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART"; field public static final String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE"; field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_APP_PERMISSION_USAGE = "android.intent.action.REVIEW_APP_PERMISSION_USAGE"; field public static final String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS"; field public static final String ACTION_REVIEW_PERMISSION_USAGE = "android.intent.action.REVIEW_PERMISSION_USAGE"; + field public static final String ACTION_ROLLBACK_COMMITTED = "android.intent.action.ROLLBACK_COMMITTED"; field public static final String ACTION_SHOW_SUSPENDED_APP_DETAILS = "android.intent.action.SHOW_SUSPENDED_APP_DETAILS"; field @Deprecated public static final String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED"; field public static final String ACTION_SPLIT_CONFIGURATION_CHANGED = "android.intent.action.SPLIT_CONFIGURATION_CHANGED"; @@ -1695,18 +1698,19 @@ package android.content.rollback { public final class RollbackInfo implements android.os.Parcelable { 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; - field public final android.content.rollback.PackageRollbackInfo targetPackage; } public final class RollbackManager { - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void executeRollback(@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) @Nullable public android.content.rollback.RollbackInfo getAvailableRollback(@NonNull String); - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) @NonNull public java.util.List<java.lang.String> getPackagesWithAvailableRollbacks(); - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) @NonNull public java.util.List<android.content.rollback.RollbackInfo> getRecentlyExecutedRollbacks(); + 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(); method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void reloadPersistedData(); } @@ -4101,25 +4105,10 @@ package android.net { 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); @@ -4249,6 +4238,24 @@ package android.net { field public final android.net.RssiCurve rssiCurve; } + public final class StaticIpConfiguration implements android.os.Parcelable { + ctor public StaticIpConfiguration(); + ctor public StaticIpConfiguration(android.net.StaticIpConfiguration); + method public void addDnsServer(java.net.InetAddress); + method public void clear(); + method public int describeContents(); + method public java.util.List<java.net.InetAddress> getDnsServers(); + method public String getDomains(); + method public java.net.InetAddress getGateway(); + method public android.net.LinkAddress getIpAddress(); + method public java.util.List<android.net.RouteInfo> getRoutes(String); + method public void setDomains(String); + method public void setGateway(java.net.InetAddress); + method public void setIpAddress(android.net.LinkAddress); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.StaticIpConfiguration> CREATOR; + } + public class TrafficStats { method public static void setThreadStatsTagApp(); method public static void setThreadStatsTagBackup(); @@ -4274,6 +4281,47 @@ package android.net { } +package android.net.apf { + + public class ApfCapabilities { + ctor public ApfCapabilities(int, int, int); + method public boolean hasDataAccess(); + field public final int apfPacketFormat; + field public final int apfVersionSupported; + field public final int maximumApfProgramSize; + } + +} + +package android.net.captiveportal { + + public final class CaptivePortalProbeResult { + ctor public CaptivePortalProbeResult(int); + ctor public CaptivePortalProbeResult(int, String, String); + ctor public CaptivePortalProbeResult(int, String, String, android.net.captiveportal.CaptivePortalProbeSpec); + method public boolean isFailed(); + method public boolean isPortal(); + method public boolean isSuccessful(); + field public static final android.net.captiveportal.CaptivePortalProbeResult FAILED; + field public static final int FAILED_CODE = 599; // 0x257 + field public static final int PORTAL_CODE = 302; // 0x12e + field public static final android.net.captiveportal.CaptivePortalProbeResult SUCCESS; + field public static final int SUCCESS_CODE = 204; // 0xcc + field public final String detectUrl; + field @Nullable public final android.net.captiveportal.CaptivePortalProbeSpec probeSpec; + field public final String redirectUrl; + } + + public abstract class CaptivePortalProbeSpec { + method public String getEncodedSpec(); + method public abstract android.net.captiveportal.CaptivePortalProbeResult getResult(int, @Nullable String); + method public java.net.URL getUrl(); + method public static java.util.Collection<android.net.captiveportal.CaptivePortalProbeSpec> parseCaptivePortalProbeSpecs(String); + method @Nullable public static android.net.captiveportal.CaptivePortalProbeSpec parseSpecOrNull(@Nullable String); + } + +} + package android.net.metrics { public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event { @@ -5060,6 +5108,36 @@ package android.os { 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; } @@ -5534,7 +5612,6 @@ package android.permissionpresenterservice { method @Deprecated public final void attachBaseContext(android.content.Context); method @Deprecated public final android.os.IBinder onBind(android.content.Intent); method @Deprecated public abstract java.util.List<android.content.pm.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(@NonNull String); - method @Deprecated public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String); field @Deprecated public static final String SERVICE_INTERFACE = "android.permissionpresenterservice.RuntimePermissionPresenterService"; } diff --git a/api/test-current.txt b/api/test-current.txt index 0f2ba12bd9ad..049e0025a59b 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -6,6 +6,7 @@ package android { field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING"; field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE"; field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE"; + field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA"; field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"; field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES"; field public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS"; @@ -866,6 +867,24 @@ package android.net { field public static final int RTN_UNREACHABLE = 7; // 0x7 } + public final class StaticIpConfiguration implements android.os.Parcelable { + ctor public StaticIpConfiguration(); + ctor public StaticIpConfiguration(android.net.StaticIpConfiguration); + method public void addDnsServer(java.net.InetAddress); + method public void clear(); + method public int describeContents(); + method public java.util.List<java.net.InetAddress> getDnsServers(); + method public String getDomains(); + method public java.net.InetAddress getGateway(); + method public android.net.LinkAddress getIpAddress(); + method public java.util.List<android.net.RouteInfo> getRoutes(String); + method public void setDomains(String); + method public void setGateway(java.net.InetAddress); + method public void setIpAddress(android.net.LinkAddress); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.StaticIpConfiguration> CREATOR; + } + public class TrafficStats { method public static long getLoopbackRxBytes(); method public static long getLoopbackRxPackets(); @@ -875,6 +894,47 @@ package android.net { } +package android.net.apf { + + public class ApfCapabilities { + ctor public ApfCapabilities(int, int, int); + method public boolean hasDataAccess(); + field public final int apfPacketFormat; + field public final int apfVersionSupported; + field public final int maximumApfProgramSize; + } + +} + +package android.net.captiveportal { + + public final class CaptivePortalProbeResult { + ctor public CaptivePortalProbeResult(int); + ctor public CaptivePortalProbeResult(int, String, String); + ctor public CaptivePortalProbeResult(int, String, String, android.net.captiveportal.CaptivePortalProbeSpec); + method public boolean isFailed(); + method public boolean isPortal(); + method public boolean isSuccessful(); + field public static final android.net.captiveportal.CaptivePortalProbeResult FAILED; + field public static final int FAILED_CODE = 599; // 0x257 + field public static final int PORTAL_CODE = 302; // 0x12e + field public static final android.net.captiveportal.CaptivePortalProbeResult SUCCESS; + field public static final int SUCCESS_CODE = 204; // 0xcc + field public final String detectUrl; + field @Nullable public final android.net.captiveportal.CaptivePortalProbeSpec probeSpec; + field public final String redirectUrl; + } + + public abstract class CaptivePortalProbeSpec { + method public String getEncodedSpec(); + method public abstract android.net.captiveportal.CaptivePortalProbeResult getResult(int, @Nullable String); + method public java.net.URL getUrl(); + method public static java.util.Collection<android.net.captiveportal.CaptivePortalProbeSpec> parseCaptivePortalProbeSpecs(String); + method @Nullable public static android.net.captiveportal.CaptivePortalProbeSpec parseSpecOrNull(@Nullable String); + } + +} + package android.net.metrics { public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event { @@ -1053,6 +1113,10 @@ package android.os { method public static java.io.File getStorageDirectory(); } + public class FileUtils { + method public static boolean contains(java.io.File, java.io.File); + } + public abstract class HwBinder implements android.os.IHwBinder { ctor public HwBinder(); method public static final void configureRpcThreadpool(long, boolean); @@ -1218,6 +1282,10 @@ package android.os { method public boolean hasSingleFileDescriptor(); } + public class ParcelFileDescriptor implements java.io.Closeable android.os.Parcelable { + method public static java.io.File getFile(java.io.FileDescriptor) throws java.io.IOException; + } + public final class PowerManager { method @RequiresPermission("android.permission.POWER_SAVER") public int getPowerSaveMode(); method @RequiresPermission("android.permission.POWER_SAVER") public boolean setDynamicPowerSavings(boolean, int); @@ -1485,17 +1553,37 @@ package android.print { package android.provider { + public static final class CalendarContract.Calendars implements android.provider.BaseColumns android.provider.CalendarContract.CalendarColumns android.provider.CalendarContract.SyncColumns { + field public static final String[] SYNC_WRITABLE_COLUMNS; + } + + public static final class CalendarContract.Events implements android.provider.BaseColumns android.provider.CalendarContract.CalendarColumns android.provider.CalendarContract.EventsColumns android.provider.CalendarContract.SyncColumns { + field public static final String[] SYNC_WRITABLE_COLUMNS; + } + + public final class ContactsContract { + field public static final String HIDDEN_COLUMN_PREFIX = "x_"; + } + public static final class ContactsContract.CommonDataKinds.Phone implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins { field public static final android.net.Uri ENTERPRISE_CONTENT_URI; } + public static final class ContactsContract.PinnedPositions { + field public static final String UNDEMOTE_METHOD = "undemote"; + } + public static final class ContactsContract.RawContactsEntity implements android.provider.BaseColumns android.provider.ContactsContract.DataColumns android.provider.ContactsContract.RawContactsColumns { field public static final android.net.Uri CORP_CONTENT_URI; } public final class MediaStore { - method @RequiresPermission("android.permission.CLEAR_APP_USER_DATA") public static void deleteContributedMedia(android.content.Context, String, android.os.UserHandle) throws java.io.IOException; - method @RequiresPermission("android.permission.CLEAR_APP_USER_DATA") public static long getContributedMediaSize(android.content.Context, String, android.os.UserHandle) throws java.io.IOException; + method @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA) public static void deleteContributedMedia(android.content.Context, String, android.os.UserHandle) throws java.io.IOException; + method @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA) public static long getContributedMediaSize(android.content.Context, String, android.os.UserHandle) throws java.io.IOException; + method @NonNull public static java.io.File getVolumePath(@NonNull String) throws java.io.FileNotFoundException; + method @NonNull public static java.util.Collection<java.io.File> getVolumeScanPaths(@NonNull String) throws java.io.FileNotFoundException; + field public static final String SCAN_FILE_CALL = "scan_file"; + field public static final String SCAN_VOLUME_CALL = "scan_volume"; } public final class Settings { @@ -1555,6 +1643,10 @@ package android.provider { field public static final String SMS_CARRIER_PROVISION_ACTION = "android.provider.Telephony.SMS_CARRIER_PROVISION"; } + public static final class VoicemailContract.Voicemails implements android.provider.BaseColumns android.provider.OpenableColumns { + field public static final String _DATA = "_data"; + } + } package android.security { @@ -1829,10 +1921,15 @@ package android.telephony { } public class TelephonyManager { + method public int checkCarrierPrivilegesForPackage(String); method public int getCarrierIdListVersion(); method public boolean isRttSupported(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile(); method public void setCarrierTestOverride(String, String, String, String, String, String, String); + field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe + field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1 + field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0 + field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff field public static final int UNKNOWN_CARRIER_ID_LIST_VERSION = -1; // 0xffffffff } diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 9c320d3e2b03..b26c713877db 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -989,6 +989,25 @@ Status StatsService::setDataFetchOperation(int64_t key, return Status::ok(); } +Status StatsService::setActiveConfigsChangedOperation(const sp<android::IBinder>& intentSender, + const String16& packageName, + vector<int64_t>* output) { + ENFORCE_DUMP_AND_USAGE_STATS(packageName); + + IPCThreadState* ipc = IPCThreadState::self(); + mConfigManager->SetActiveConfigsChangedReceiver(ipc->getCallingUid(), intentSender); + //TODO: Return the list of configs that are already active + return Status::ok(); +} + +Status StatsService::removeActiveConfigsChangedOperation(const String16& packageName) { + ENFORCE_DUMP_AND_USAGE_STATS(packageName); + + IPCThreadState* ipc = IPCThreadState::self(); + mConfigManager->RemoveActiveConfigsChangedReceiver(ipc->getCallingUid()); + return Status::ok(); +} + Status StatsService::removeConfiguration(int64_t key, const String16& packageName) { ENFORCE_DUMP_AND_USAGE_STATS(packageName); diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index fe0504fc034f..cdff50fcb62e 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -132,6 +132,17 @@ public: const String16& packageName) override; /** + * Binder call to let clients register the active configs changed operation. + */ + virtual Status setActiveConfigsChangedOperation(const sp<android::IBinder>& intentSender, + const String16& packageName, + vector<int64_t>* output) override; + + /** + * Binder call to remove the active configs changed operation for the specified package.. + */ + virtual Status removeActiveConfigsChangedOperation(const String16& packageName) override; + /** * Binder call to allow clients to remove the specified configuration. */ virtual Status removeConfiguration(int64_t key, diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 1a0a98352051..8e56bef6856e 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -28,6 +28,7 @@ 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/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"; @@ -146,9 +147,9 @@ message Atom { VibratorStateChanged vibrator_state_changed = 84; DeferredJobStatsReported deferred_job_stats_reported = 85; ThermalThrottlingStateChanged thermal_throttling = 86; - FingerprintAcquired fingerprint_acquired = 87; - FingerprintAuthenticated fingerprint_authenticated = 88; - FingerprintErrorOccurred fingerprint_error_occurred = 89; + BiometricAcquired biometric_acquired = 87; + BiometricAuthenticated biometric_authenticated = 88; + BiometricErrorOccurred biometric_error_occurred = 89; Notification notification = 90; BatteryHealthSnapshot battery_health_snapshot = 91; SlowIo slow_io = 92; @@ -163,7 +164,7 @@ message Atom { // Consider removing this if it becomes a problem ServiceStateChanged service_state_changed = 99; ServiceLaunchReported service_launch_reported = 100; - PhenotypeFlagStateChanged phenotype_flag_state_changed = 101; + FlagFlipUpdateOccurred flag_flip_update_occurred = 101; BinaryPushStateChanged binary_push_state_changed = 102; DevicePolicyEvent device_policy_event = 103; DocsUIFileOperationCanceledReported docs_ui_file_op_canceled = 104; @@ -209,6 +210,9 @@ message Atom { AdbConnectionChanged adb_connection_changed = 144; SpeechDspStatReported speech_dsp_stat_reported = 145; UsbContaminantReported usb_contaminant_reported = 146; + WatchdogRollbackOccurred watchdog_rollback_occurred = 147; + BiometricHalDeathReported biometric_hal_death_reported = 148; + BubbleUIChanged bubble_ui_changed = 149; } // Pulled events will start at field 10000. @@ -245,7 +249,7 @@ message Atom { CategorySize category_size = 10028; ProcStats proc_stats = 10029; BatteryVoltage battery_voltage = 10030; - NumFingerprints num_fingerprints = 10031; + NumBiometricsEnrolled num_fingerprints_enrolled = 10031; DiskIo disk_io = 10032; PowerProfile power_profile = 10033; ProcStatsPkgProc proc_stats_pkg_proc = 10034; @@ -262,6 +266,7 @@ message Atom { BatteryCycleCount battery_cycle_count = 10045; DebugElapsedClock debug_elapsed_clock = 10046; DebugFailingElapsedClock debug_failing_elapsed_clock = 10047; + NumBiometricsEnrolled num_faces_enrolled = 10048; } // DO NOT USE field numbers above 100,000 in AOSP. @@ -1485,6 +1490,25 @@ message BluetoothLinkLayerConnectionEvent { optional android.bluetooth.hci.StatusEnum reason_code = 9; } +/** + * Logs when a module is rolled back by Watchdog. + * + * Logged from: Rollback Manager + */ +message WatchdogRollbackOccurred { + enum RollbackType { + UNKNOWN = 0; + ROLLBACK_INITIATE = 1; + ROLLBACK_SUCCESS = 2; + ROLLBACK_FAILURE = 3; + } + optional RollbackType rollback_type = 1; + + optional string package_name = 2; + + optional int32 package_version_code = 3; +} + /** * Logs when something is plugged into or removed from the USB-C connector. @@ -2353,58 +2377,95 @@ message GenericAtom { } /** - * Logs when a fingerprint acquire event occurs. + * Logs when a biometric acquire event occurs. * * Logged from: - * frameworks/base/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java + * frameworks/base/services/core/java/com/android/server/biometrics */ -message FingerprintAcquired { - // The associated user. Eg: 0 for owners, 10+ for others. - // Defined in android/os/UserHandle.java - optional int32 user = 1; - // If this acquire is for a crypto fingerprint. - // e.g. Secure purchases, unlock password storage. - optional bool is_crypto = 2; +message BiometricAcquired { + // Biometric modality that was acquired. + optional android.hardware.biometrics.ModalityEnum modality = 1; + // The associated user. Eg: 0 for owners, 10+ for others. Defined in android/os/UserHandle.java. + optional int32 user = 2; + // If this acquire is for a crypto operation. e.g. Secure purchases, unlock password storage. + optional bool is_crypto = 3; + // Action that the device is performing. Acquired messages are only expected for enroll and + // authenticate. Other actions may indicate an error. + optional android.hardware.biometrics.ActionEnum action = 4; + // The client that this acquisition was received for. + optional android.hardware.biometrics.ClientEnum client = 5; + // Acquired constants, e.g. ACQUIRED_GOOD. See constants defined by <Biometric>Manager. + optional int32 acquire_info = 6; + // Vendor-specific acquire info. Valid only if acquire_info == ACQUIRED_VENDOR. + optional int32 acquire_info_vendor = 7; } /** - * Logs when a fingerprint authentication event occurs. + * Logs when a biometric authentication event occurs. * * Logged from: - * frameworks/base/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java - */ -message FingerprintAuthenticated { - // The associated user. Eg: 0 for owners, 10+ for others. - // Defined in android/os/UserHandle.java - optional int32 user = 1; - // If this authentication is for a crypto fingerprint. - // e.g. Secure purchases, unlock password storage. - optional bool is_crypto = 2; - // Whether or not this authentication was successful. - optional bool is_authenticated = 3; + * frameworks/base/services/core/java/com/android/server/biometrics + */ +message BiometricAuthenticated { + // Biometric modality that was used. + optional android.hardware.biometrics.ModalityEnum modality = 1; + // The associated user. Eg: 0 for owners, 10+ for others. Defined in android/os/UserHandle.java + optional int32 user = 2; + // If this authentication is for a crypto operation. e.g. Secure purchases, unlock password + // storage. + optional bool is_crypto = 3; + // The client that this acquisition was received for. + optional android.hardware.biometrics.ClientEnum client = 4; + + enum State { + UNKNOWN = 0; + REJECTED = 1; + PENDING_CONFIRMATION = 2; + CONFIRMED = 3; + } + + // State of the current auth attempt. + optional State state = 5; + // Time it took to authenticate. For BiometricPrompt where setRequireConfirmation(false) is + // specified and supported by the biometric modality, this is from the first ACQUIRED_GOOD to + // AUTHENTICATED. for setRequireConfirmation(true), this is from PENDING_CONFIRMATION to + // CONFIRMED. + optional int64 latency_millis = 6; } /** - * Logs when a fingerprint error occurs. + * Logs when a biometric error occurs. * * Logged from: - * frameworks/base/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java + * frameworks/base/services/core/java/com/android/server/biometrics + */ +message BiometricErrorOccurred { + // Biometric modality that was used. + optional android.hardware.biometrics.ModalityEnum modality = 1; + // The associated user. Eg: 0 for owners, 10+ for others. Defined in android/os/UserHandle.java + optional int32 user = 2; + // If this error is for a crypto operation. e.g. Secure purchases, unlock password storage. + optional bool is_crypto = 3; + // Action that the device is performing. + optional android.hardware.biometrics.ActionEnum action = 4; + // The client that this acquisition was received for. + optional android.hardware.biometrics.ClientEnum client = 5; + // Error constants. See constants defined by <Biometric>Manager. Enums won't work since errors + // are unique to modality. + optional int32 error_info = 6; + // Vendor-specific error info. Valid only if acquire_info == ACQUIRED_VENDOR. These are defined + // by the vendor and not specified by the HIDL interface. + optional int32 error_info_vendor = 7; +} + +/** + * Logs when a biometric HAL has crashed. + * Logged from: + * frameworks/base/services/core/java/com/android/server/biometrics */ -message FingerprintErrorOccurred { - // The associated user. Eg: 0 for owners, 10+ for others. - // Defined in android/os/UserHandle.java - optional int32 user = 1; - // If this error is for a crypto fingerprint. - // e.g. Secure purchases, unlock password storage. - optional bool is_crypto = 2; - - enum Error { - UNKNOWN = 0; - LOCKOUT = 1; - PERMANENT_LOCKOUT = 2; - } - // The type of error. - optional Error error = 3; +message BiometricHalDeathReported { + // Biometric modality. + optional android.hardware.biometrics.ModalityEnum modality = 1; } message Notification { @@ -2478,18 +2539,14 @@ message Notification { } /* - * Logs when a flag flip state changes. - * Logged in P/h. + * Logs when a flag flip update occurrs. Used for mainline modules that update via flag flips. */ -message PhenotypeFlagStateChanged { - // Mendel configuration name. - optional string mendel_config_name = 1; - // State - enum State { - STATE_UNKNOWN = 0; - COMMITTED = 1; - } - optional State state = 2; +message FlagFlipUpdateOccurred { + // If the event is from a flag config package, specify the package name. + optional string flag_flip_package_name = 1; + + // The order id of the package + optional int64 order_id = 2; } /* @@ -3351,14 +3408,14 @@ message DiskIo { /** * Pulls the number of fingerprints for each user. * - * Pulled from StatsCompanionService, which queries FingerprintManager. + * Pulled from StatsCompanionService, which queries <Biometric>Manager. */ -message NumFingerprints { +message NumBiometricsEnrolled { // The associated user. Eg: 0 for owners, 10+ for others. // Defined in android/os/UserHandle.java optional int32 user = 1; // Number of fingerprints registered to that user. - optional int32 num_fingerprints = 2; + optional int32 num_enrolled = 2; } message AggStats { @@ -4595,3 +4652,50 @@ message DebugFailingElapsedClock { // Diff between current elapsed time and elapsed time from previous pull. optional int64 elapsed_clock_diff_millis = 4; } + +/** Logs System UI bubbles event changed. + * + * Logged from: + * frameworks/base/packages/SystemUI/src/com/android/systemui/bubbles + */ +message BubbleUIChanged { + + // The app package that is posting the bubble. + optional string package_name = 1; + + // The notification channel that is posting the bubble. + optional string notification_channel = 2; + + // The notification id associated with the posted bubble. + optional int32 notification_id = 3; + + // The position of the bubble within the bubble stack. + optional int32 position = 4; + + // The total number of bubbles within the bubble stack. + optional int32 total_number = 5; + + // User interactions with the bubble. + enum Action { + UNKNOWN = 0; + POSTED = 1; + UPDATED = 2; + EXPANDED = 3; + COLLAPSED = 4; + DISMISSED = 5; + STACK_DISMISSED = 6; + STACK_MOVED = 7; + HEADER_GO_TO_APP = 8; + HEADER_GO_TO_SETTINGS = 9; + PERMISSION_OPT_IN = 10; + PERMISSION_OPT_OUT = 11; + PERMISSION_IGNORED = 12; + SWIPE_LEFT = 13; + SWIPE_RIGHT = 14; + } + optional Action action = 6; + + // Normalized screen position of the bubble stack. The range is between 0 and 1. + optional float normalized_x_position = 7; + optional float normalized_y_position = 8; +} diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index 5fea90b61c80..aa22333ab26c 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -128,6 +128,17 @@ void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) { mConfigReceivers.erase(key); } +void ConfigManager::SetActiveConfigsChangedReceiver(const int uid, + const sp<IBinder>& intentSender) { + lock_guard<mutex> lock(mMutex); + mActiveConfigsChangedReceivers[uid] = intentSender; +} + +void ConfigManager::RemoveActiveConfigsChangedReceiver(const int uid) { + lock_guard<mutex> lock(mMutex); + mActiveConfigsChangedReceivers.erase(uid); +} + void ConfigManager::RemoveConfig(const ConfigKey& key) { vector<sp<ConfigListener>> broadcastList; { @@ -181,6 +192,11 @@ void ConfigManager::RemoveConfigs(int uid) { mConfigReceivers.erase(*it); } + auto itActiveConfigsChangedReceiver = mActiveConfigsChangedReceivers.find(uid); + if (itActiveConfigsChangedReceiver != mActiveConfigsChangedReceivers.end()) { + mActiveConfigsChangedReceivers.erase(itActiveConfigsChangedReceiver); + } + mConfigs.erase(uidIt); for (const sp<ConfigListener>& listener : mListeners) { @@ -213,6 +229,7 @@ void ConfigManager::RemoveAllConfigs() { } mConfigReceivers.clear(); + mActiveConfigsChangedReceivers.clear(); for (const sp<ConfigListener>& listener : mListeners) { broadcastList.push_back(listener); } @@ -250,6 +267,17 @@ const sp<android::IBinder> ConfigManager::GetConfigReceiver(const ConfigKey& key } } +const sp<android::IBinder> ConfigManager::GetActiveConfigsChangedReceiver(const int uid) const { + lock_guard<mutex> lock(mMutex); + + auto it = mActiveConfigsChangedReceivers.find(uid); + if (it == mActiveConfigsChangedReceivers.end()) { + return nullptr; + } else { + return it->second; + } +} + void ConfigManager::Dump(FILE* out) { lock_guard<mutex> lock(mMutex); diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h index 122e669057b0..c064a519f597 100644 --- a/cmds/statsd/src/config/ConfigManager.h +++ b/cmds/statsd/src/config/ConfigManager.h @@ -82,6 +82,23 @@ public: void RemoveConfigReceiver(const ConfigKey& key); /** + * Sets the broadcast receiver that is notified whenever the list of active configs + * changes for this uid. + */ + void SetActiveConfigsChangedReceiver(const int uid, const sp<IBinder>& intentSender); + + /** + * Returns the broadcast receiver for active configs changed for this uid. + */ + + const sp<IBinder> GetActiveConfigsChangedReceiver(const int uid) const; + + /** + * Erase any active configs changed broadcast receiver associated with this uid. + */ + void RemoveActiveConfigsChangedReceiver(const int uid); + + /** * A configuration was removed. * * Reports this to listeners. @@ -130,6 +147,12 @@ private: std::map<ConfigKey, sp<android::IBinder>> mConfigReceivers; /** + * Each uid can be subscribed by up to one receiver to notify that the list of active configs + * for this uid has changed. The receiver is specified as IBinder from PendingIntent. + */ + std::map<int, sp<android::IBinder>> mActiveConfigsChangedReceivers; + + /** * The ConfigListeners that will be told about changes. */ std::vector<sp<ConfigListener>> mListeners; diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 4585a09176ce..ba7bcc437d74 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -175,8 +175,8 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { {android::util::CATEGORY_SIZE, {.puller = new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}}, // Number of fingerprints registered to each user. - {android::util::NUM_FINGERPRINTS, - {.puller = new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}}, + {android::util::NUM_FINGERPRINTS_ENROLLED, + {.puller = new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS_ENROLLED)}}, // ProcStats. {android::util::PROC_STATS, {.puller = new StatsCompanionServicePuller(android::util::PROC_STATS)}}, diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt index ee2fe8f5750f..8e7a58b40d0c 100644 --- a/config/hiddenapi-greylist.txt +++ b/config/hiddenapi-greylist.txt @@ -901,7 +901,6 @@ Landroid/os/ParcelableParcel;->getClassLoader()Ljava/lang/ClassLoader; Landroid/os/ParcelableParcel;->getParcel()Landroid/os/Parcel; Landroid/os/ParcelFileDescriptor;-><init>(Ljava/io/FileDescriptor;)V Landroid/os/ParcelFileDescriptor;->fromData([BLjava/lang/String;)Landroid/os/ParcelFileDescriptor; -Landroid/os/ParcelFileDescriptor;->getFile(Ljava/io/FileDescriptor;)Ljava/io/File; Landroid/os/ParcelFileDescriptor;->seekTo(J)J Landroid/os/PerformanceCollector;-><init>()V Landroid/os/PerformanceCollector;->beginSnapshot(Ljava/lang/String;)V diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index 181accea2e6a..f522d71afd08 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -29,6 +29,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.hardware.biometrics.BiometricPrompt; import android.os.Binder; import android.os.Build; import android.os.Handler; @@ -87,6 +88,12 @@ public class KeyguardManager { "android.app.action.CONFIRM_FRP_CREDENTIAL"; /** + * @hide + */ + public static final String EXTRA_BIOMETRIC_PROMPT_BUNDLE = + "android.app.extra.BIOMETRIC_PROMPT_BUNDLE"; + + /** * A CharSequence dialog title to show to the user when used with a * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}. * @hide @@ -101,12 +108,6 @@ public class KeyguardManager { public static final String EXTRA_DESCRIPTION = "android.app.extra.DESCRIPTION"; /** - * A boolean value to forward to {@link android.hardware.biometrics.BiometricPrompt}. - * @hide - */ - public static final String EXTRA_USE_IMPLICIT = "android.app.extra.USE_IMPLICIT"; - - /** * A CharSequence description to show to the user on the alternate button when used with * {@link #ACTION_CONFIRM_FRP_CREDENTIAL}. * @hide @@ -124,44 +125,23 @@ public class KeyguardManager { public static final int RESULT_ALTERNATE = 1; /** - * Get an intent to prompt the user to confirm credentials (pin, pattern or password) - * for the current user of the device. The caller is expected to launch this activity using - * {@link android.app.Activity#startActivityForResult(Intent, int)} and check for + * @deprecated see {@link BiometricPrompt.Builder#setEnableFallback(boolean)} + * + * Get an intent to prompt the user to confirm credentials (pin, pattern, password or biometrics + * if enrolled) for the current user of the device. The caller is expected to launch this + * activity using {@link android.app.Activity#startActivityForResult(Intent, int)} and check for * {@link android.app.Activity#RESULT_OK} if the user successfully completes the challenge. * - * @param title Title to be shown on the dialog. - * @param description Description to be shown on the dialog. * @return the intent for launching the activity or null if no password is required. **/ + @Deprecated @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN) - public Intent createConfirmDeviceCredentialIntent(CharSequence title, CharSequence description) { - return createConfirmDeviceCredentialIntent(title, description, false /* useImplicit */); - } - - /** - * Get an intent to prompt the user to confirm credentials (pin, pattern or password) - * for the current user of the device. The caller is expected to launch this activity using - * {@link android.app.Activity#startActivityForResult(Intent, int)} and check for - * {@link android.app.Activity#RESULT_OK} if the user successfully completes the challenge. - * - * @param title Title to be shown on the dialog. - * @param description Description to be shown on the dialog. - * @param useImplicit If useImplicit is set to true, ConfirmDeviceCredentials will invoke - * {@link android.hardware.biometrics.BiometricPrompt} with - * {@link android.hardware.biometrics.BiometricPrompt.Builder#setRequireConfirmation( - * boolean)} set to false. - * @return the intent for launching the activity or null if no password is required. - * @hide - */ - public Intent createConfirmDeviceCredentialIntent(CharSequence title, CharSequence description, - boolean useImplicit) { - if (!isDeviceSecure()) { - return null; - } + public Intent createConfirmDeviceCredentialIntent(CharSequence title, + CharSequence description) { + if (!isDeviceSecure()) return null; Intent intent = new Intent(ACTION_CONFIRM_DEVICE_CREDENTIAL); intent.putExtra(EXTRA_TITLE, title); intent.putExtra(EXTRA_DESCRIPTION, description); - intent.putExtra(EXTRA_USE_IMPLICIT, useImplicit); // explicitly set the package for security intent.setPackage(getSettingsPackageForIntent(intent)); diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java index 9dcd1228522b..9b66c928abc0 100644 --- a/core/java/android/app/StatsManager.java +++ b/core/java/android/app/StatsManager.java @@ -73,6 +73,11 @@ public final class StatsManager { */ public static final String EXTRA_STATS_DIMENSIONS_VALUE = "android.app.extra.STATS_DIMENSIONS_VALUE"; + /** + * Long array extra of the active configs for the uid that added those configs. + */ + public static final String EXTRA_STATS_ACTIVE_CONFIG_KEYS = + "android.app.extra.STATS_ACTIVE_CONFIG_KEYS"; /** * Broadcast Action: Statsd has started. @@ -274,6 +279,43 @@ public final class StatsManager { } } + /** + * Registers the operation that is called whenever there is a change in which configs are + * active. This must be called each time statsd starts. This operation allows + * statsd to inform clients that they should pull data of the configs that are currently + * active. The activeConfigsChangedOperation should set periodic alarms to pull data of configs + * that are active and stop pulling data of configs that are no longer active. + * + * @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber + * associated with the given subscriberId. May be null, in which case + * it removes any associated pending intent for this client. + * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service + */ + @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) + public void setActiveConfigsChangedOperation(@Nullable PendingIntent pendingIntent) + throws StatsUnavailableException { + synchronized (this) { + try { + IStatsManager service = getIStatsManagerLocked(); + if (pendingIntent == null) { + service.removeActiveConfigsChangedOperation(mContext.getOpPackageName()); + } else { + // Extracts IIntentSender from the PendingIntent and turns it into an IBinder. + IBinder intentSender = pendingIntent.getTarget().asBinder(); + service.setActiveConfigsChangedOperation(intentSender, + mContext.getOpPackageName()); + } + + } catch (RemoteException e) { + Slog.e(TAG, + "Failed to connect to statsd when registering active configs listener."); + throw new StatsUnavailableException("could not connect", e); + } catch (SecurityException e) { + throw new StatsUnavailableException(e.getMessage(), e); + } + } + } + // TODO: Temporary for backwards compatibility. Remove. /** * @deprecated Use {@link #setFetchReportsOperation(PendingIntent, long)} diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 2dc225af6482..ee13164d7564 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -102,6 +102,7 @@ import android.net.IConnectivityManager; 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 @@ final class SystemServiceRegistry { 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 2964fbc0084b..cf62e8d625db 100644 --- a/core/java/android/app/role/IRoleManager.aidl +++ b/core/java/android/app/role/IRoleManager.aidl @@ -18,6 +18,8 @@ package android.app.role; import android.app.role.IOnRoleHoldersChangedListener; import android.app.role.IRoleManagerCallback; +import android.os.Bundle; +import android.telephony.IFinancialSmsCallback; /** * @hide @@ -52,4 +54,8 @@ interface IRoleManager { 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/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 6704a45fb07a..47a4a2dca73d 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -87,6 +87,7 @@ import java.util.concurrent.atomic.AtomicBoolean; * <p>For more information about using a ContentResolver with content providers, read the * <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a> * developer guide.</p> + * </div> */ public abstract class ContentResolver implements ContentInterface { /** diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 280f1ac9c067..87f9e464cdc2 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3637,6 +3637,16 @@ public abstract class Context { /** * 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/Intent.java b/core/java/android/content/Intent.java index 9e7aaf652a4d..22f73dbd4644 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2375,8 +2375,7 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_PACKAGE_ENABLE_ROLLBACK = "android.intent.action.PACKAGE_ENABLE_ROLLBACK"; /** - * Broadcast Action: An existing version of an application package has been - * rolled back to a previous version. + * Broadcast Action: A rollback has been committed. * * <p class="note">This is a protected intent that can only be sent * by the system. @@ -2385,8 +2384,8 @@ public class Intent implements Parcelable, Cloneable { */ @SystemApi @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_PACKAGE_ROLLBACK_EXECUTED = - "android.intent.action.PACKAGE_ROLLBACK_EXECUTED"; + public static final String ACTION_ROLLBACK_COMMITTED = + "android.intent.action.ROLLBACK_COMMITTED"; /** * @hide * Broadcast Action: Ask system services if there is any reason to diff --git a/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl b/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl index 2faf3adb34b6..4f4c34b88cbd 100644 --- a/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl +++ b/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl @@ -27,5 +27,4 @@ import android.os.RemoteCallback; */ oneway interface IRuntimePermissionPresenter { void getAppPermissions(String packageName, in RemoteCallback callback); - void revokeRuntimePermission(String packageName, String permissionName); } diff --git a/core/java/android/content/rollback/IRollbackManager.aidl b/core/java/android/content/rollback/IRollbackManager.aidl index 420bcb69e0c4..226d76abc01c 100644 --- a/core/java/android/content/rollback/IRollbackManager.aidl +++ b/core/java/android/content/rollback/IRollbackManager.aidl @@ -17,20 +17,16 @@ package android.content.rollback; import android.content.pm.ParceledListSlice; -import android.content.pm.StringParceledListSlice; import android.content.rollback.RollbackInfo; import android.content.IntentSender; /** {@hide} */ interface IRollbackManager { - RollbackInfo getAvailableRollback(String packageName); - - StringParceledListSlice getPackagesWithAvailableRollbacks(); - + 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 0803a7c1d651..812f995675fc 100644 --- a/core/java/android/content/rollback/RollbackInfo.java +++ b/core/java/android/content/rollback/RollbackInfo.java @@ -17,9 +17,12 @@ package android.content.rollback; import android.annotation.SystemApi; +import android.content.pm.PackageInstaller; import android.os.Parcel; import android.os.Parcelable; +import java.util.List; + /** * Information about a set of packages that can be, or already have been * rolled back together. @@ -34,25 +37,17 @@ public final class RollbackInfo implements Parcelable { */ private final int mRollbackId; - /** - * The package that needs to be rolled back. - */ - public final PackageRollbackInfo targetPackage; - - // TODO: Add a list of additional packages rolled back due to atomic - // install dependencies when rollback of atomic installs is supported. - // TODO: Add a flag to indicate if reboot is required, when rollback of - // staged installs is supported. + private final List<PackageRollbackInfo> mPackages; /** @hide */ - public RollbackInfo(int rollbackId, PackageRollbackInfo targetPackage) { + public RollbackInfo(int rollbackId, List<PackageRollbackInfo> packages) { this.mRollbackId = rollbackId; - this.targetPackage = targetPackage; + this.mPackages = packages; } private RollbackInfo(Parcel in) { mRollbackId = in.readInt(); - targetPackage = PackageRollbackInfo.CREATOR.createFromParcel(in); + mPackages = in.createTypedArrayList(PackageRollbackInfo.CREATOR); } /** @@ -62,6 +57,31 @@ public final class RollbackInfo implements Parcelable { return mRollbackId; } + /** + * Returns the list of package that are rolled back. + */ + public List<PackageRollbackInfo> getPackages() { + 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; @@ -70,7 +90,7 @@ public final class RollbackInfo implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(mRollbackId); - targetPackage.writeToParcel(out, flags); + out.writeTypedList(mPackages); } public static final Parcelable.Creator<RollbackInfo> CREATOR = diff --git a/core/java/android/content/rollback/RollbackManager.java b/core/java/android/content/rollback/RollbackManager.java index c1c0bc1d3e07..f8abcfc8fc76 100644 --- a/core/java/android/content/rollback/RollbackManager.java +++ b/core/java/android/content/rollback/RollbackManager.java @@ -17,7 +17,6 @@ package android.content.rollback; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; @@ -50,55 +49,26 @@ public final class RollbackManager { } /** - * Returns the rollback currently available to be executed for the given - * package. - * <p> - * The returned RollbackInfo describes what packages would be rolled back, - * including package version codes before and after rollback. The rollback - * can be initiated using {@link #executeRollback(RollbackInfo,IntentSender)}. - * <p> - * TODO: What if there is no package installed on device for packageName? + * Returns a list of all currently available rollbacks. * - * @param packageName name of the package to get the availble RollbackInfo for. - * @return the rollback available for the package, or null if no rollback - * is available for the package. * @throws SecurityException if the caller does not have the * MANAGE_ROLLBACKS permission. */ @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) - public @Nullable RollbackInfo getAvailableRollback(@NonNull String packageName) { + public List<RollbackInfo> getAvailableRollbacks() { try { - return mBinder.getAvailableRollback(packageName); + return mBinder.getAvailableRollbacks().getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** - * Gets the names of packages that are available for rollback. - * Call {@link #getAvailableRollback(String)} to get more information - * about the rollback available for a particular package. - * - * @return the names of packages that are available for rollback. - * @throws SecurityException if the caller does not have the - * MANAGE_ROLLBACKS permission. - */ - @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) - public @NonNull List<String> getPackagesWithAvailableRollbacks() { - try { - return mBinder.getPackagesWithAvailableRollbacks().getList(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - - /** - * Gets the list of all recently executed rollbacks. + * Gets the list of all recently committed rollbacks. * This is for the purposes of preventing re-install of a bad version of a - * package. + * package and monitoring the status of a staged rollback. * <p> - * Returns an empty list if there are no recently executed rollbacks. + * Returns an empty list if there are no recently committed rollbacks. * <p> * To avoid having to keep around complete rollback history forever on a * device, the returned list of rollbacks is only guaranteed to include @@ -107,12 +77,12 @@ public final class RollbackManager { * (without the possibility of rollback) to a higher version code than was * rolled back from. * - * @return the recently executed rollbacks + * @return the recently committed rollbacks * @throws SecurityException if the caller does not have the * MANAGE_ROLLBACKS permission. */ @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) - public @NonNull List<RollbackInfo> getRecentlyExecutedRollbacks() { + public @NonNull List<RollbackInfo> getRecentlyCommittedRollbacks() { try { return mBinder.getRecentlyExecutedRollbacks().getList(); } catch (RemoteException e) { @@ -121,28 +91,26 @@ public final class RollbackManager { } /** - * Execute the given rollback, rolling back all versions of the packages - * to the last good versions previously installed on the device as - * specified in the given rollback object. The rollback will fail if any - * of the installed packages or available rollbacks are inconsistent with - * the versions specified in the given rollback object, which can happen - * if a package has been updated or a rollback expired since the rollback - * object was retrieved from {@link #getAvailableRollback(String)}. + * Commit the rollback with given id, rolling back all versions of the + * packages to the last good versions previously installed on the device + * as specified in the corresponding RollbackInfo object. The + * rollback will fail if any of the installed packages or available + * rollbacks are inconsistent with the versions specified in the given + * rollback object, which can happen if a package has been updated or a + * rollback expired since the rollback object was retrieved from + * {@link #getAvailableRollbacks()}. * <p> * TODO: Specify the returns status codes. - * TODO: What happens in case reboot is required for the rollback to take - * effect for staged installs? * - * @param rollback to execute + * @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 executeRollback(@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/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java index 8bcaa4526fa7..b44458a7d449 100644 --- a/core/java/android/database/AbstractCursor.java +++ b/core/java/android/database/AbstractCursor.java @@ -16,17 +16,20 @@ package android.database; +import android.annotation.NonNull; import android.annotation.UnsupportedAppUsage; import android.content.ContentResolver; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.os.UserHandle; import android.util.Log; +import com.android.internal.util.Preconditions; + import java.lang.ref.WeakReference; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; @@ -72,6 +75,7 @@ public abstract class AbstractCursor implements CrossProcessCursor { @UnsupportedAppUsage private Uri mNotifyUri; + private List<Uri> mNotifyUris; private final Object mSelfObserverLock = new Object(); private ContentObserver mSelfObserver; @@ -155,7 +159,11 @@ public abstract class AbstractCursor implements CrossProcessCursor { @Override public boolean requery() { if (mSelfObserver != null && mSelfObserverRegistered == false) { - mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver); + final int size = mNotifyUris.size(); + for (int i = 0; i < size; ++i) { + final Uri notifyUri = mNotifyUris.get(i); + mContentResolver.registerContentObserver(notifyUri, true, mSelfObserver); + } mSelfObserverRegistered = true; } mDataSetObservable.notifyChanged(); @@ -384,8 +392,12 @@ public abstract class AbstractCursor implements CrossProcessCursor { protected void onChange(boolean selfChange) { synchronized (mSelfObserverLock) { mContentObservable.dispatchChange(selfChange, null); - if (mNotifyUri != null && selfChange) { - mContentResolver.notifyChange(mNotifyUri, mSelfObserver); + if (mNotifyUris != null && selfChange) { + final int size = mNotifyUris.size(); + for (int i = 0; i < size; ++i) { + final Uri notifyUri = mNotifyUris.get(i); + mContentResolver.notifyChange(notifyUri, mSelfObserver); + } } } } @@ -399,19 +411,33 @@ public abstract class AbstractCursor implements CrossProcessCursor { */ @Override public void setNotificationUri(ContentResolver cr, Uri notifyUri) { - setNotificationUri(cr, notifyUri, cr.getUserId()); + setNotificationUris(cr, Arrays.asList(notifyUri)); + } + + @Override + public void setNotificationUris(@NonNull ContentResolver cr, @NonNull List<Uri> notifyUris) { + Preconditions.checkNotNull(cr); + Preconditions.checkNotNull(notifyUris); + + setNotificationUris(cr, notifyUris, cr.getUserId()); } /** @hide - set the notification uri but with an observer for a particular user's view */ - public void setNotificationUri(ContentResolver cr, Uri notifyUri, int userHandle) { + public void setNotificationUris(ContentResolver cr, List<Uri> notifyUris, int userHandle) { synchronized (mSelfObserverLock) { - mNotifyUri = notifyUri; + mNotifyUris = notifyUris; + mNotifyUri = mNotifyUris.get(0); mContentResolver = cr; if (mSelfObserver != null) { mContentResolver.unregisterContentObserver(mSelfObserver); } mSelfObserver = new SelfContentObserver(this); - mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver, userHandle); + final int size = mNotifyUris.size(); + for (int i = 0; i < size; ++i) { + final Uri notifyUri = mNotifyUris.get(i); + mContentResolver.registerContentObserver( + notifyUri, true, mSelfObserver, userHandle); + } mSelfObserverRegistered = true; } } @@ -424,6 +450,13 @@ public abstract class AbstractCursor implements CrossProcessCursor { } @Override + public List<Uri> getNotificationUris() { + synchronized (mSelfObserverLock) { + return mNotifyUris; + } + } + + @Override public boolean getWantsAllOnMoveCalls() { return false; } diff --git a/core/java/android/database/Cursor.java b/core/java/android/database/Cursor.java index 5a4280e997cd..1379138b37c0 100644 --- a/core/java/android/database/Cursor.java +++ b/core/java/android/database/Cursor.java @@ -16,11 +16,14 @@ package android.database; +import android.annotation.NonNull; import android.content.ContentResolver; import android.net.Uri; import android.os.Bundle; import java.io.Closeable; +import java.util.Arrays; +import java.util.List; /** * This interface provides random read-write access to the result set returned @@ -421,7 +424,10 @@ public interface Cursor extends Closeable { /** * Register to watch a content URI for changes. This can be the URI of a specific data row (for * example, "content://my_provider_type/23"), or a a generic URI for a content type. - * + * + * <p>Calling this overrides any previous call to + * {@link #setNotificationUris(ContentResolver, List)}. + * * @param cr The content resolver from the caller's context. The listener attached to * this resolver will be notified. * @param uri The content URI to watch. @@ -429,6 +435,24 @@ public interface Cursor extends Closeable { void setNotificationUri(ContentResolver cr, Uri uri); /** + * Similar to {@link #setNotificationUri(ContentResolver, Uri)}, except this version allows + * to watch multiple content URIs for changes. + * + * <p>If this is not implemented, this is equivalent to calling + * {@link #setNotificationUri(ContentResolver, Uri)} with the first URI in {@code uris}. + * + * <p>Calling this overrides any previous call to + * {@link #setNotificationUri(ContentResolver, Uri)}. + * + * @param cr The content resolver from the caller's context. The listener attached to + * this resolver will be notified. + * @param uris The content URIs to watch. + */ + default void setNotificationUris(@NonNull ContentResolver cr, @NonNull List<Uri> uris) { + setNotificationUri(cr, uris.get(0)); + } + + /** * Return the URI at which notifications of changes in this Cursor's data * will be delivered, as previously set by {@link #setNotificationUri}. * @return Returns a URI that can be used with @@ -439,6 +463,22 @@ public interface Cursor extends Closeable { Uri getNotificationUri(); /** + * Return the URIs at which notifications of changes in this Cursor's data + * will be delivered, as previously set by {@link #setNotificationUris}. + * + * <p>If this is not implemented, this is equivalent to calling {@link #getNotificationUri()}. + * + * @return Returns URIs that can be used with + * {@link ContentResolver#registerContentObserver(android.net.Uri, boolean, ContentObserver) + * ContentResolver.registerContentObserver} to find out about changes to this Cursor's + * data. May be null if no notification URI has been set. + */ + default List<Uri> getNotificationUris() { + final Uri notifyUri = getNotificationUri(); + return notifyUri == null ? null : Arrays.asList(notifyUri); + } + + /** * onMove() will only be called across processes if this method returns true. * @return whether all cursor movement should result in a call to onMove(). */ diff --git a/core/java/android/database/CursorWrapper.java b/core/java/android/database/CursorWrapper.java index 0d27dfb872f0..c9cafafe4041 100644 --- a/core/java/android/database/CursorWrapper.java +++ b/core/java/android/database/CursorWrapper.java @@ -21,6 +21,8 @@ import android.content.ContentResolver; import android.net.Uri; import android.os.Bundle; +import java.util.List; + /** * Wrapper class for Cursor that delegates all calls to the actual cursor object. The primary * use for this class is to extend a cursor while overriding only a subset of its methods. @@ -241,11 +243,21 @@ public class CursorWrapper implements Cursor { } @Override + public void setNotificationUris(ContentResolver cr, List<Uri> uris) { + mCursor.setNotificationUris(cr, uris); + } + + @Override public Uri getNotificationUri() { return mCursor.getNotificationUri(); } @Override + public List<Uri> getNotificationUris() { + return mCursor.getNotificationUris(); + } + + @Override public void unregisterContentObserver(ContentObserver observer) { mCursor.unregisterContentObserver(observer); } diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java index 9d37d9939127..209afb88ccd3 100644 --- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java @@ -252,12 +252,55 @@ public interface BiometricFaceConstants { public static final int FACE_ACQUIRED_TOO_SIMILAR = 15; /** + * The magnitude of the pan angle of the user’s face with respect to the sensor’s + * capture plane is too high. + * + * The pan angle is defined as the angle swept out by the user’s face turning + * their neck left and right. The pan angle would be zero if the user faced the + * camera directly. + * + * The user should be informed to look more directly at the camera. + */ + public static final int FACE_ACQUIRED_PAN_TOO_EXTREME = 16; + + /** + * The magnitude of the tilt angle of the user’s face with respect to the sensor’s + * capture plane is too high. + * + * The tilt angle is defined as the angle swept out by the user’s face looking up + * and down. The pan angle would be zero if the user faced the camera directly. + * + * The user should be informed to look more directly at the camera. + */ + public static final int FACE_ACQUIRED_TILT_TOO_EXTREME = 17; + + /** + * The magnitude of the roll angle of the user’s face with respect to the sensor’s + * capture plane is too high. + * + * The roll angle is defined as the angle swept out by the user’s face tilting their head + * towards their shoulders to the left and right. The pan angle would be zero if the user + * faced the camera directly. + * + * The user should be informed to look more directly at the camera. + */ + public static final int FACE_ACQUIRED_ROLL_TOO_EXTREME = 18; + + /** + * The user’s face has been obscured by some object. + * + * The user should be informed to remove any objects from the line of sight from + * the sensor to the user’s face. + */ + public static final int FACE_ACQUIRED_FACE_OBSCURED = 19; + + /** * Hardware vendors may extend this list if there are conditions that do not fall under one of * the above categories. Vendors are responsible for providing error strings for these errors. * * @hide */ - public static final int FACE_ACQUIRED_VENDOR = 16; + public static final int FACE_ACQUIRED_VENDOR = 20; /** * @hide diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index 8aac1bfa1f8d..5afe1a9309f4 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -171,5 +171,39 @@ public class BiometricManager { Slog.w(TAG, "resetTimeout(): Service not connected"); } } + + /** + * TODO(b/123378871): Remove when moved. + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public void onConfirmDeviceCredentialSuccess() { + if (mService != null) { + try { + mService.onConfirmDeviceCredentialSuccess(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } else { + Slog.w(TAG, "onConfirmDeviceCredentialSuccess(): Service not connected"); + } + } + + /** + * TODO(b/123378871): Remove when moved. + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public void onConfirmDeviceCredentialError(int error, String message) { + if (mService != null) { + try { + mService.onConfirmDeviceCredentialError(error, message); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } else { + Slog.w(TAG, "onConfirmDeviceCredentialError(): Service not connected"); + } + } } diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index c69b68e4360a..ec62abaab6a2 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -77,6 +77,10 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * @hide */ public static final String KEY_REQUIRE_CONFIRMATION = "require_confirmation"; + /** + * @hide + */ + public static final String KEY_ENABLE_FALLBACK = "enable_fallback"; /** * Error/help message will show for this amount of time. @@ -242,6 +246,18 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } /** + * The user will first be prompted to authenticate with biometrics, but also given the + * option to authenticate with their device PIN, pattern, or password. + * @param enable When true, the prompt will fall back to ask for the user's device + * credentials (PIN, pattern, or password). + * @return + */ + public Builder setEnableFallback(boolean enable) { + mBundle.putBoolean(KEY_ENABLE_FALLBACK, enable); + return this; + } + + /** * Creates a {@link BiometricPrompt}. * @return a {@link BiometricPrompt} * @throws IllegalArgumentException if any of the required fields are not set. @@ -250,11 +266,15 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan final CharSequence title = mBundle.getCharSequence(KEY_TITLE); final CharSequence negative = mBundle.getCharSequence(KEY_NEGATIVE_TEXT); final boolean useDefaultTitle = mBundle.getBoolean(KEY_USE_DEFAULT_TITLE); + final boolean enableFallback = mBundle.getBoolean(KEY_ENABLE_FALLBACK); if (TextUtils.isEmpty(title) && !useDefaultTitle) { throw new IllegalArgumentException("Title must be set and non-empty"); - } else if (TextUtils.isEmpty(negative)) { + } else if (TextUtils.isEmpty(negative) && !enableFallback) { throw new IllegalArgumentException("Negative text must be set and non-empty"); + } else if (!TextUtils.isEmpty(negative) && enableFallback) { + throw new IllegalArgumentException("Can't have both negative button behavior" + + " and fallback enabled"); } return new BiometricPrompt(mContext, mBundle, mPositiveButtonInfo, mNegativeButtonInfo); } @@ -514,6 +534,9 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan if (callback == null) { throw new IllegalArgumentException("Must supply a callback"); } + if (mBundle.getBoolean(KEY_ENABLE_FALLBACK)) { + throw new IllegalArgumentException("Fallback not supported with crypto"); + } authenticateInternal(crypto, cancel, executor, callback, mContext.getUserId()); } diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl index de828f2d6757..e4336d171ab6 100644 --- a/core/java/android/hardware/biometrics/IBiometricService.aidl +++ b/core/java/android/hardware/biometrics/IBiometricService.aidl @@ -51,4 +51,12 @@ interface IBiometricService { // Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password) void resetTimeout(in byte [] token); + + // TODO(b/123378871): Remove when moved. + // CDCA needs to send results to BiometricService if it was invoked using BiometricPrompt's + // setEnableFallback method, since there's no way for us to intercept onActivityResult. + // CDCA is launched from BiometricService (startActivityAsUser) instead of *ForResult. + void onConfirmDeviceCredentialSuccess(); + // TODO(b/123378871): Remove when moved. + void onConfirmDeviceCredentialError(int error, String message); } diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 7e45441c804a..f3ebd7f36fd6 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -20,6 +20,7 @@ import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.ParceledListSlice; import android.content.res.Resources; +import android.graphics.ColorSpace; import android.graphics.Point; import android.hardware.display.DisplayManager.DisplayListener; import android.media.projection.IMediaProjection; @@ -80,12 +81,20 @@ public final class DisplayManagerGlobal { new ArrayList<DisplayListenerDelegate>(); private final SparseArray<DisplayInfo> mDisplayInfoCache = new SparseArray<DisplayInfo>(); + private final ColorSpace mWideColorSpace; private int[] mDisplayIdCache; private int mWifiDisplayScanNestCount; private DisplayManagerGlobal(IDisplayManager dm) { mDm = dm; + try { + mWideColorSpace = + ColorSpace.get( + ColorSpace.Named.values()[mDm.getPreferredWideGamutColorSpaceId()]); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } } /** @@ -495,6 +504,17 @@ public final class DisplayManagerGlobal { } /** + * Gets the preferred wide gamut color space for all displays. + * The wide gamut color space is returned from composition pipeline + * based on hardware capability. + * + * @hide + */ + public ColorSpace getPreferredWideGamutColorSpace() { + return mWideColorSpace; + } + + /** * Sets the global brightness configuration for a given user. * * @hide diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index aae8afbcad49..5ea8bd67ce75 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -116,4 +116,9 @@ interface IDisplayManager { // Get the minimum brightness curve. Curve getMinimumBrightnessCurve(); + + // Gets the id of the preferred wide gamut color space for all displays. + // The wide gamut color space is returned from composition pipeline + // based on hardware capability. + int getPreferredWideGamutColorSpaceId(); } diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java index b5d822672e70..6c291c25dbdf 100644 --- a/core/java/android/net/DhcpResults.java +++ b/core/java/android/net/DhcpResults.java @@ -17,24 +17,39 @@ package android.net; import android.annotation.UnsupportedAppUsage; -import android.net.NetworkUtils; import android.os.Parcel; +import android.os.Parcelable; import android.text.TextUtils; import android.util.Log; import java.net.Inet4Address; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; /** * A simple object for retrieving the results of a DHCP request. * Optimized (attempted) for that jni interface - * TODO - remove when DhcpInfo is deprecated. Move the remaining api to LinkProperties. + * TODO: remove this class and replace with other existing constructs * @hide */ -public class DhcpResults extends StaticIpConfiguration { +public final class DhcpResults implements Parcelable { private static final String TAG = "DhcpResults"; @UnsupportedAppUsage + public LinkAddress ipAddress; + + @UnsupportedAppUsage + public InetAddress gateway; + + @UnsupportedAppUsage + public final ArrayList<InetAddress> dnsServers = new ArrayList<>(); + + @UnsupportedAppUsage + public String domains; + + @UnsupportedAppUsage public Inet4Address serverAddress; /** Vendor specific information (from RFC 2132). */ @@ -48,23 +63,36 @@ public class DhcpResults extends StaticIpConfiguration { @UnsupportedAppUsage public int mtu; - @UnsupportedAppUsage public DhcpResults() { super(); } - @UnsupportedAppUsage + /** + * Create a {@link StaticIpConfiguration} based on the DhcpResults. + */ + public StaticIpConfiguration toStaticIpConfiguration() { + final StaticIpConfiguration s = new StaticIpConfiguration(); + // All these except dnsServers are immutable, so no need to make copies. + s.ipAddress = ipAddress; + s.gateway = gateway; + s.dnsServers.addAll(dnsServers); + s.domains = domains; + return s; + } + public DhcpResults(StaticIpConfiguration source) { - super(source); + if (source != null) { + ipAddress = source.ipAddress; + gateway = source.gateway; + dnsServers.addAll(source.dnsServers); + domains = source.domains; + } } /** copy constructor */ - @UnsupportedAppUsage public DhcpResults(DhcpResults source) { - super(source); - + this(source == null ? null : source.toStaticIpConfiguration()); if (source != null) { - // All these are immutable, so no need to make copies. serverAddress = source.serverAddress; vendorInfo = source.vendorInfo; leaseDuration = source.leaseDuration; @@ -73,6 +101,14 @@ public class DhcpResults extends StaticIpConfiguration { } /** + * @see StaticIpConfiguration#getRoutes(String) + * @hide + */ + public List<RouteInfo> getRoutes(String iface) { + return toStaticIpConfiguration().getRoutes(iface); + } + + /** * Test if this DHCP lease includes vendor hint that network link is * metered, and sensitive to heavy data transfers. */ @@ -85,7 +121,11 @@ public class DhcpResults extends StaticIpConfiguration { } public void clear() { - super.clear(); + ipAddress = null; + gateway = null; + dnsServers.clear(); + domains = null; + serverAddress = null; vendorInfo = null; leaseDuration = 0; mtu = 0; @@ -111,20 +151,20 @@ public class DhcpResults extends StaticIpConfiguration { DhcpResults target = (DhcpResults)obj; - return super.equals((StaticIpConfiguration) obj) && - Objects.equals(serverAddress, target.serverAddress) && - Objects.equals(vendorInfo, target.vendorInfo) && - leaseDuration == target.leaseDuration && - mtu == target.mtu; + return toStaticIpConfiguration().equals(target.toStaticIpConfiguration()) + && Objects.equals(serverAddress, target.serverAddress) + && Objects.equals(vendorInfo, target.vendorInfo) + && leaseDuration == target.leaseDuration + && mtu == target.mtu; } - /** Implement the Parcelable interface */ + /** + * Implement the Parcelable interface + */ public static final Creator<DhcpResults> CREATOR = new Creator<DhcpResults>() { public DhcpResults createFromParcel(Parcel in) { - DhcpResults dhcpResults = new DhcpResults(); - readFromParcel(dhcpResults, in); - return dhcpResults; + return readFromParcel(in); } public DhcpResults[] newArray(int size) { @@ -134,19 +174,26 @@ public class DhcpResults extends StaticIpConfiguration { /** Implement the Parcelable interface */ public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); + toStaticIpConfiguration().writeToParcel(dest, flags); dest.writeInt(leaseDuration); dest.writeInt(mtu); NetworkUtils.parcelInetAddress(dest, serverAddress, flags); dest.writeString(vendorInfo); } - private static void readFromParcel(DhcpResults dhcpResults, Parcel in) { - StaticIpConfiguration.readFromParcel(dhcpResults, in); + @Override + public int describeContents() { + return 0; + } + + private static DhcpResults readFromParcel(Parcel in) { + final StaticIpConfiguration s = StaticIpConfiguration.CREATOR.createFromParcel(in); + final DhcpResults dhcpResults = new DhcpResults(s); dhcpResults.leaseDuration = in.readInt(); dhcpResults.mtu = in.readInt(); dhcpResults.serverAddress = (Inet4Address) NetworkUtils.unparcelInetAddress(in); dhcpResults.vendorInfo = in.readString(); + return dhcpResults; } // Utils for jni population - false on success @@ -184,25 +231,70 @@ public class DhcpResults extends StaticIpConfiguration { return false; } - public boolean setServerAddress(String addrString) { - try { - serverAddress = (Inet4Address) NetworkUtils.numericToInetAddress(addrString); - } catch (IllegalArgumentException|ClassCastException e) { - Log.e(TAG, "setServerAddress failed with addrString " + addrString); - return true; - } - return false; + public LinkAddress getIpAddress() { + return ipAddress; + } + + public void setIpAddress(LinkAddress ipAddress) { + this.ipAddress = ipAddress; + } + + public InetAddress getGateway() { + return gateway; + } + + public void setGateway(InetAddress gateway) { + this.gateway = gateway; + } + + public List<InetAddress> getDnsServers() { + return dnsServers; + } + + /** + * Add a DNS server to this configuration. + */ + public void addDnsServer(InetAddress server) { + dnsServers.add(server); + } + + public String getDomains() { + return domains; + } + + public void setDomains(String domains) { + this.domains = domains; + } + + public Inet4Address getServerAddress() { + return serverAddress; + } + + public void setServerAddress(Inet4Address addr) { + serverAddress = addr; + } + + public int getLeaseDuration() { + return leaseDuration; } public void setLeaseDuration(int duration) { leaseDuration = duration; } + public String getVendorInfo() { + return vendorInfo; + } + public void setVendorInfo(String info) { vendorInfo = info; } - public void setDomains(String newDomains) { - domains = newDomains; + public int getMtu() { + return mtu; + } + + public void setMtu(int mtu) { + this.mtu = mtu; } } diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java index 23c8aa368d87..e519fdf65e50 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -257,7 +257,6 @@ public final class IpSecTransform implements AutoCloseable { * * @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 @@ public final class IpSecTransform implements AutoCloseable { * * @hide */ - @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.MANAGE_IPSEC_TUNNELS, android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD @@ -331,7 +329,6 @@ public final class IpSecTransform implements AutoCloseable { * * @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 c996d01fe8e4..39db4fef78b5 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -21,8 +21,10 @@ import static android.system.OsConstants.AF_INET6; 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.Collection; import java.util.Locale; import java.util.TreeSet; -import android.system.ErrnoException; - /** * Native methods for managing network interfaces. * @@ -177,119 +177,37 @@ public class NetworkUtils { 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); - } - - /** - * 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)); + return Inet4AddressUtils.intToInet4AddressHTL(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); - } - - /** - * 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)); + return Inet4AddressUtils.inet4AddressToIntHTL(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 @@ public class NetworkUtils { * @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 @@ public class NetworkUtils { */ @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 @@ public class NetworkUtils { } /** - * 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/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java index 3aa56b90251f..25bae3c57423 100644 --- a/core/java/android/net/StaticIpConfiguration.java +++ b/core/java/android/net/StaticIpConfiguration.java @@ -16,10 +16,11 @@ package android.net; +import android.annotation.SystemApi; +import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; -import android.net.LinkAddress; -import android.os.Parcelable; import android.os.Parcel; +import android.os.Parcelable; import java.net.InetAddress; import java.util.ArrayList; @@ -46,17 +47,22 @@ import java.util.Objects; * * @hide */ -public class StaticIpConfiguration implements Parcelable { +@SystemApi +@TestApi +public final class StaticIpConfiguration implements Parcelable { + /** @hide */ @UnsupportedAppUsage public LinkAddress ipAddress; + /** @hide */ @UnsupportedAppUsage public InetAddress gateway; + /** @hide */ @UnsupportedAppUsage public final ArrayList<InetAddress> dnsServers; + /** @hide */ @UnsupportedAppUsage public String domains; - @UnsupportedAppUsage public StaticIpConfiguration() { dnsServers = new ArrayList<InetAddress>(); } @@ -79,6 +85,41 @@ public class StaticIpConfiguration implements Parcelable { domains = null; } + public LinkAddress getIpAddress() { + return ipAddress; + } + + public void setIpAddress(LinkAddress ipAddress) { + this.ipAddress = ipAddress; + } + + public InetAddress getGateway() { + return gateway; + } + + public void setGateway(InetAddress gateway) { + this.gateway = gateway; + } + + public List<InetAddress> getDnsServers() { + return dnsServers; + } + + public String getDomains() { + return domains; + } + + public void setDomains(String newDomains) { + domains = newDomains; + } + + /** + * Add a DNS server to this configuration. + */ + public void addDnsServer(InetAddress server) { + dnsServers.add(server); + } + /** * Returns the network routes specified by this object. Will typically include a * directly-connected route for the IP address's local subnet and a default route. If the @@ -86,7 +127,6 @@ public class StaticIpConfiguration implements Parcelable { * route to the gateway as well. This configuration is arguably invalid, but it used to work * in K and earlier, and other OSes appear to accept it. */ - @UnsupportedAppUsage public List<RouteInfo> getRoutes(String iface) { List<RouteInfo> routes = new ArrayList<RouteInfo>(3); if (ipAddress != null) { @@ -107,6 +147,7 @@ public class StaticIpConfiguration implements Parcelable { * contained in the LinkProperties will not be a complete picture of the link's configuration, * because any configuration information that is obtained dynamically by the network (e.g., * IPv6 configuration) will not be included. + * @hide */ public LinkProperties toLinkProperties(String iface) { LinkProperties lp = new LinkProperties(); @@ -124,6 +165,7 @@ public class StaticIpConfiguration implements Parcelable { return lp; } + @Override public String toString() { StringBuffer str = new StringBuffer(); @@ -143,6 +185,7 @@ public class StaticIpConfiguration implements Parcelable { return str.toString(); } + @Override public int hashCode() { int result = 13; result = 47 * result + (ipAddress == null ? 0 : ipAddress.hashCode()); @@ -168,12 +211,10 @@ public class StaticIpConfiguration implements Parcelable { } /** Implement the Parcelable interface */ - public static Creator<StaticIpConfiguration> CREATOR = + public static final Creator<StaticIpConfiguration> CREATOR = new Creator<StaticIpConfiguration>() { public StaticIpConfiguration createFromParcel(Parcel in) { - StaticIpConfiguration s = new StaticIpConfiguration(); - readFromParcel(s, in); - return s; + return readFromParcel(in); } public StaticIpConfiguration[] newArray(int size) { @@ -182,11 +223,13 @@ public class StaticIpConfiguration implements Parcelable { }; /** Implement the Parcelable interface */ + @Override public int describeContents() { return 0; } /** Implement the Parcelable interface */ + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeParcelable(ipAddress, flags); NetworkUtils.parcelInetAddress(dest, gateway, flags); @@ -197,7 +240,9 @@ public class StaticIpConfiguration implements Parcelable { dest.writeString(domains); } - protected static void readFromParcel(StaticIpConfiguration s, Parcel in) { + /** @hide */ + public static StaticIpConfiguration readFromParcel(Parcel in) { + final StaticIpConfiguration s = new StaticIpConfiguration(); s.ipAddress = in.readParcelable(null); s.gateway = NetworkUtils.unparcelInetAddress(in); s.dnsServers.clear(); @@ -206,5 +251,6 @@ public class StaticIpConfiguration implements Parcelable { s.dnsServers.add(NetworkUtils.unparcelInetAddress(in)); } s.domains = in.readString(); + return s; } } diff --git a/core/java/android/net/apf/ApfCapabilities.java b/core/java/android/net/apf/ApfCapabilities.java index f28cdc902848..73cf94b785a7 100644 --- a/core/java/android/net/apf/ApfCapabilities.java +++ b/core/java/android/net/apf/ApfCapabilities.java @@ -16,11 +16,16 @@ package android.net.apf; +import android.annotation.SystemApi; +import android.annotation.TestApi; + /** * APF program support capabilities. * * @hide */ +@SystemApi +@TestApi public class ApfCapabilities { /** * Version of APF instruction set supported for packet filtering. 0 indicates no support for diff --git a/core/java/android/net/captiveportal/CaptivePortalProbeResult.java b/core/java/android/net/captiveportal/CaptivePortalProbeResult.java index 1634694cc71f..7432687e136f 100644 --- a/core/java/android/net/captiveportal/CaptivePortalProbeResult.java +++ b/core/java/android/net/captiveportal/CaptivePortalProbeResult.java @@ -17,11 +17,15 @@ package android.net.captiveportal; import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.annotation.TestApi; /** * Result of calling isCaptivePortal(). * @hide */ +@SystemApi +@TestApi public final class CaptivePortalProbeResult { public static final int SUCCESS_CODE = 204; public static final int FAILED_CODE = 599; diff --git a/core/java/android/net/captiveportal/CaptivePortalProbeSpec.java b/core/java/android/net/captiveportal/CaptivePortalProbeSpec.java index 57a926afec54..7ad4ecf2264c 100644 --- a/core/java/android/net/captiveportal/CaptivePortalProbeSpec.java +++ b/core/java/android/net/captiveportal/CaptivePortalProbeSpec.java @@ -21,21 +21,26 @@ import static android.net.captiveportal.CaptivePortalProbeResult.SUCCESS_CODE; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.annotation.TestApi; import android.text.TextUtils; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; + import java.net.MalformedURLException; import java.net.URL; import java.text.ParseException; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; /** @hide */ +@SystemApi +@TestApi public abstract class CaptivePortalProbeSpec { - public static final String HTTP_LOCATION_HEADER_NAME = "Location"; - private static final String TAG = CaptivePortalProbeSpec.class.getSimpleName(); private static final String REGEX_SEPARATOR = "@@/@@"; private static final String SPEC_SEPARATOR = "@@,@@"; @@ -55,7 +60,9 @@ public abstract class CaptivePortalProbeSpec { * @throws MalformedURLException The URL has invalid format for {@link URL#URL(String)}. * @throws ParseException The string is empty, does not match the above format, or a regular * expression is invalid for {@link Pattern#compile(String)}. + * @hide */ + @VisibleForTesting @NonNull public static CaptivePortalProbeSpec parseSpec(String spec) throws ParseException, MalformedURLException { @@ -113,7 +120,8 @@ public abstract class CaptivePortalProbeSpec { * <p>Each spec is separated by @@,@@ and follows the format for {@link #parseSpec(String)}. * <p>This method does not throw but ignores any entry that could not be parsed. */ - public static CaptivePortalProbeSpec[] parseCaptivePortalProbeSpecs(String settingsVal) { + public static Collection<CaptivePortalProbeSpec> parseCaptivePortalProbeSpecs( + String settingsVal) { List<CaptivePortalProbeSpec> specs = new ArrayList<>(); if (settingsVal != null) { for (String spec : TextUtils.split(settingsVal, SPEC_SEPARATOR)) { @@ -128,7 +136,7 @@ public abstract class CaptivePortalProbeSpec { if (specs.isEmpty()) { Log.e(TAG, String.format("could not create any validation spec from %s", settingsVal)); } - return specs.toArray(new CaptivePortalProbeSpec[specs.size()]); + return specs; } /** diff --git a/core/java/android/net/shared/Inet4AddressUtils.java b/core/java/android/net/shared/Inet4AddressUtils.java new file mode 100644 index 000000000000..bec0c84fa689 --- /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 6f5f8072f471..3a5b8a86204e 100644 --- a/core/java/android/os/BugreportManager.java +++ b/core/java/android/os/BugreportManager.java @@ -21,6 +21,7 @@ import android.annotation.IntDef; 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 @@ import java.util.concurrent.Executor; * * @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 4e696aed1fa8..3871375cfced 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 @@ import java.lang.annotation.RetentionPolicy; * * @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/ExternalVibration.aidl b/core/java/android/os/ExternalVibration.aidl new file mode 100644 index 000000000000..2629a401541d --- /dev/null +++ b/core/java/android/os/ExternalVibration.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +parcelable ExternalVibration cpp_header "vibrator/ExternalVibration.h"; diff --git a/core/java/android/os/ExternalVibration.java b/core/java/android/os/ExternalVibration.java new file mode 100644 index 000000000000..69ab1d94a9e2 --- /dev/null +++ b/core/java/android/os/ExternalVibration.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.annotation.NonNull; +import android.media.AudioAttributes; +import android.util.Slog; + +import com.android.internal.util.Preconditions; + +/** + * An ExternalVibration represents an on-going vibration being controlled by something other than + * the core vibrator service. + * + * @hide + */ +public class ExternalVibration implements Parcelable { + private static final String TAG = "ExternalVibration"; + private int mUid; + @NonNull + private String mPkg; + @NonNull + private AudioAttributes mAttrs; + @NonNull + private IExternalVibrationController mController; + // A token used to maintain equality comparisons when passing objects across process + // boundaries. + @NonNull + private IBinder mToken; + + public ExternalVibration(int uid, @NonNull String pkg, @NonNull AudioAttributes attrs, + @NonNull IExternalVibrationController controller) { + mUid = uid; + mPkg = Preconditions.checkNotNull(pkg); + mAttrs = Preconditions.checkNotNull(attrs); + mController = Preconditions.checkNotNull(controller); + mToken = new Binder(); + } + + private ExternalVibration(Parcel in) { + mUid = in.readInt(); + mPkg = in.readString(); + mAttrs = readAudioAttributes(in); + mController = IExternalVibrationController.Stub.asInterface(in.readStrongBinder()); + mToken = in.readStrongBinder(); + } + + private AudioAttributes readAudioAttributes(Parcel in) { + int usage = in.readInt(); + int contentType = in.readInt(); + int capturePreset = in.readInt(); + int flags = in.readInt(); + AudioAttributes.Builder builder = new AudioAttributes.Builder(); + return builder.setUsage(usage) + .setContentType(contentType) + .setCapturePreset(capturePreset) + .setFlags(flags) + .build(); + } + + public int getUid() { + return mUid; + } + + public String getPackage() { + return mPkg; + } + + public AudioAttributes getAudioAttributes() { + return mAttrs; + } + + /** + * Mutes the external vibration if it's playing and unmuted. + * + * @return whether the muting operation was successful + */ + public boolean mute() { + try { + mController.mute(); + } catch (RemoteException e) { + Slog.wtf(TAG, "Failed to mute vibration stream: " + this, e); + return false; + } + return true; + } + + /** + * Unmutes the external vibration if it's playing and muted. + * + * @return whether the unmuting operation was successful + */ + public boolean unmute() { + try { + mController.unmute(); + } catch (RemoteException e) { + Slog.wtf(TAG, "Failed to unmute vibration stream: " + this, e); + return false; + } + return true; + } + + @Override + public boolean equals(Object o) { + if (o == null || !(o instanceof ExternalVibration)) { + return false; + } + ExternalVibration other = (ExternalVibration) o; + return mToken.equals(other.mToken); + } + + @Override + public String toString() { + return "ExternalVibration{" + + "uid=" + mUid + ", " + + "pkg=" + mPkg + ", " + + "attrs=" + mAttrs + ", " + + "controller=" + mController + + "token=" + mController + + "}"; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mUid); + out.writeString(mPkg); + writeAudioAttributes(mAttrs, out, flags); + out.writeParcelable(mAttrs, flags); + out.writeStrongBinder(mController.asBinder()); + out.writeStrongBinder(mToken); + } + + private static void writeAudioAttributes(AudioAttributes attrs, Parcel out, int flags) { + out.writeInt(attrs.getUsage()); + out.writeInt(attrs.getContentType()); + out.writeInt(attrs.getCapturePreset()); + out.writeInt(attrs.getAllFlags()); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator<ExternalVibration> CREATOR = + new Parcelable.Creator<ExternalVibration>() { + @Override + public ExternalVibration createFromParcel(Parcel in) { + return new ExternalVibration(in); + } + + @Override + public ExternalVibration[] newArray(int size) { + return new ExternalVibration[size]; + } + }; +} diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 51c3c4c25ce0..629289bb7a45 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -39,6 +39,7 @@ import static android.system.OsConstants.W_OK; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.content.ContentResolver; import android.provider.DocumentsContract.Document; import android.system.ErrnoException; @@ -852,6 +853,7 @@ public class FileUtils { * * @hide */ + @TestApi public static boolean contains(File dir, File file) { if (dir == null || file == null) return false; return contains(dir.getAbsolutePath(), file.getAbsolutePath()); diff --git a/core/java/android/os/IExternalVibrationController.aidl b/core/java/android/os/IExternalVibrationController.aidl new file mode 100644 index 000000000000..56da8c7cd46c --- /dev/null +++ b/core/java/android/os/IExternalVibrationController.aidl @@ -0,0 +1,45 @@ +/* + * 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. + */ + +package android.os; + +/** + * {@hide} + */ + +interface IExternalVibrationController { + /** + * A method to ask a currently playing vibration to mute (i.e. not vibrate). + * + * This method is only valid from the time that + * {@link IExternalVibratorService#onExternalVibrationStart} returns until + * {@link IExternalVibratorService#onExternalVibrationStop} returns. + * + * @return whether the mute operation was successful + */ + boolean mute(); + + /** + * A method to ask a currently playing vibration to unmute (i.e. start vibrating). + * + * This method is only valid from the time that + * {@link IExternalVibratorService#onExternalVibrationStart} returns until + * {@link IExternalVibratorService#onExternalVibrationStop} returns. + * + * @return whether the unmute operation was successful + */ + boolean unmute(); +} diff --git a/core/java/android/os/IExternalVibratorService.aidl b/core/java/android/os/IExternalVibratorService.aidl new file mode 100644 index 000000000000..666171fcbcb3 --- /dev/null +++ b/core/java/android/os/IExternalVibratorService.aidl @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.os.ExternalVibration; + +/** + * The communication channel by which an external system that wants to control the system + * vibrator can notify the vibrator subsystem. + * + * Some vibrators can be driven via multiple paths (e.g. as an audio channel) in addition to + * the usual interface, but we typically only want one vibration at a time playing because they + * don't mix well. In order to synchronize the two places where vibration might be controlled, + * we provide this interface so the vibrator subsystem has a chance to: + * + * 1) Decide whether the current vibration should play based on the current system policy. + * 2) Stop any currently on-going vibrations. + * {@hide} + */ +interface IExternalVibratorService { + const int SCALE_MUTE = -100; + const int SCALE_VERY_LOW = -2; + const int SCALE_LOW = -1; + const int SCALE_NONE = 0; + const int SCALE_HIGH = 1; + const int SCALE_VERY_HIGH = 2; + + /** + * A method called by the external system to start a vibration. + * + * If this returns {@code SCALE_MUTE}, then the vibration should <em>not</em> play. If this + * returns any other scale level, then any currently playing vibration controlled by the + * requesting system must be muted and this vibration can begin playback. + * + * Note that the IExternalVibratorService implementation will not call mute on any currently + * playing external vibrations in order to avoid re-entrancy with the system on the other side. + * + * @param vibration An ExternalVibration + * + * @return {@code SCALE_MUTE} if the external vibration should not play, and any other scale + * level if it should. + */ + int onExternalVibrationStart(in ExternalVibration vib); + + /** + * A method called by the external system when a vibration no longer wants to play. + */ + void onExternalVibrationStop(in ExternalVibration vib); +} diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl index 74d434c51781..93d6f4c12128 100644 --- a/core/java/android/os/IStatsManager.aidl +++ b/core/java/android/os/IStatsManager.aidl @@ -119,6 +119,23 @@ interface IStatsManager { void removeDataFetchOperation(long configKey, in String packageName); /** + * Registers the given pending intent for this packagename. This intent is invoked when the + * active status of any of the configs sent by this package changes and will contain a list of + * config ids that are currently active. It also returns the list of configs that are currently + * active. There can be at most one active configs changed listener per package. + * + * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS. + */ + long[] setActiveConfigsChangedOperation(in IBinder intentSender, in String packageName); + + /** + * Removes the active configs changed operation for the specified package name. + * + * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS. + */ + void removeActiveConfigsChangedOperation(in String packageName); + + /** * Removes the configuration with the matching config key. No-op if this config key does not * exist. * diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index 630bd2e509ff..d68eeeda2eb9 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -31,6 +31,7 @@ import static android.system.OsConstants.S_ISLNK; import static android.system.OsConstants.S_ISREG; import static android.system.OsConstants.S_IWOTH; +import android.annotation.TestApi; import android.content.BroadcastReceiver; import android.content.ContentProvider; import android.os.MessageQueue.OnFileDescriptorEventListener; @@ -580,6 +581,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { * * @hide */ + @TestApi public static File getFile(FileDescriptor fd) throws IOException { try { final String path = Os.readlink("/proc/self/fd/" + fd.getInt$()); diff --git a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java index 8d568c84b915..33795f81ded9 100644 --- a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java +++ b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java @@ -78,15 +78,6 @@ public abstract class RuntimePermissionPresenterService extends Service { public abstract List<RuntimePermissionPresentationInfo> onGetAppPermissions( @NonNull String packageName); - /** - * Revokes the permission {@code permissionName} for app {@code packageName} - * - * @param packageName The package for which to revoke - * @param permissionName The permission to revoke - */ - public abstract void onRevokeRuntimePermission(@NonNull String packageName, - @NonNull String permissionName); - @Override public final IBinder onBind(Intent intent) { return new IRuntimePermissionPresenter.Stub() { @@ -99,17 +90,6 @@ public abstract class RuntimePermissionPresenterService extends Service { obtainMessage(RuntimePermissionPresenterService::getAppPermissions, RuntimePermissionPresenterService.this, packageName, callback)); } - - @Override - public void revokeRuntimePermission(String packageName, String permissionName) { - checkNotNull(packageName, "packageName"); - checkNotNull(permissionName, "permissionName"); - - mHandler.sendMessage( - obtainMessage(RuntimePermissionPresenterService::onRevokeRuntimePermission, - RuntimePermissionPresenterService.this, packageName, - permissionName)); - } }; } diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java index 8bd75d779154..8a52f1f0eec0 100644 --- a/core/java/android/provider/CalendarContract.java +++ b/core/java/android/provider/CalendarContract.java @@ -19,6 +19,7 @@ package android.provider; import android.annotation.NonNull; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.app.Activity; import android.app.AlarmManager; @@ -805,6 +806,7 @@ public final class CalendarContract { * * @hide */ + @TestApi public static final String[] SYNC_WRITABLE_COLUMNS = new String[] { ACCOUNT_NAME, ACCOUNT_TYPE, @@ -1832,6 +1834,7 @@ public final class CalendarContract { * * @hide */ + @TestApi public static final String[] SYNC_WRITABLE_COLUMNS = new String[] { _SYNC_ID, DIRTY, diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 25554b937065..81e1eb99336b 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -126,6 +126,7 @@ public final class ContactsContract { * Prefix for column names that are not visible to client apps. * @hide */ + @TestApi public static final String HIDDEN_COLUMN_PREFIX = "x_"; /** @@ -8444,6 +8445,7 @@ public final class ContactsContract { * nothing will be done. * @hide */ + @TestApi public static final String UNDEMOTE_METHOD = "undemote"; /** diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index f5c442f194ba..887175a80421 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -104,6 +104,11 @@ public final class MediaStore { */ public static final String VOLUME_EXTERNAL = "external"; + /** {@hide} */ @TestApi + public static final String SCAN_FILE_CALL = "scan_file"; + /** {@hide} */ @TestApi + public static final String SCAN_VOLUME_CALL = "scan_volume"; + /** * The method name used by the media scanner and mtp to tell the media provider to * rescan and reclassify that have become unhidden because of renaming folders or @@ -2992,6 +2997,7 @@ public final class MediaStore { * * @hide */ + @TestApi public static @NonNull File getVolumePath(@NonNull String volumeName) throws FileNotFoundException { if (TextUtils.isEmpty(volumeName)) { @@ -3022,6 +3028,7 @@ public final class MediaStore { * * @hide */ + @TestApi public static @NonNull Collection<File> getVolumeScanPaths(@NonNull String volumeName) throws FileNotFoundException { if (TextUtils.isEmpty(volumeName)) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 852b65a05034..195e72c71e55 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8786,6 +8786,7 @@ public final class Settings { CLONE_TO_MANAGED_PROFILE.add(LOCATION_CHANGER); CLONE_TO_MANAGED_PROFILE.add(LOCATION_MODE); CLONE_TO_MANAGED_PROFILE.add(LOCATION_PROVIDERS_ALLOWED); + CLONE_TO_MANAGED_PROFILE.add(SHOW_IME_WITH_HARD_KEYBOARD); if (!InputMethodSystemProperty.PER_PROFILE_IME_ENABLED) { CLONE_TO_MANAGED_PROFILE.add(DEFAULT_INPUT_METHOD); CLONE_TO_MANAGED_PROFILE.add(ENABLED_INPUT_METHODS); @@ -11722,6 +11723,7 @@ public final class Settings { * entity_list_default (String[]) * entity_list_not_editable (String[]) * entity_list_editable (String[]) + * lang_id_threshold_override (float) * </pre> * * <p> diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java index 140336e93357..dce5d56e24a4 100644 --- a/core/java/android/provider/VoicemailContract.java +++ b/core/java/android/provider/VoicemailContract.java @@ -18,6 +18,7 @@ package android.provider; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.TestApi; import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentValues; @@ -289,6 +290,7 @@ public class VoicemailContract { * Path to the media content file. Internal only field. * @hide */ + @TestApi public static final String _DATA = "_data"; // Note: PHONE_ACCOUNT_* constant values are "subscription_*" due to a historic naming diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java index b6788f578bd6..93189b310485 100644 --- a/core/java/android/service/notification/Adjustment.java +++ b/core/java/android/service/notification/Adjustment.java @@ -63,15 +63,17 @@ public final class Adjustment implements Parcelable { /** * 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/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 4052ed782671..db9351b030df 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -52,7 +52,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_mobile_network_v2", "true"); DEFAULT_FLAGS.put("settings_network_and_internet_v2", "false"); DEFAULT_FLAGS.put("settings_seamless_transfer", "false"); - DEFAULT_FLAGS.put("settings_slice_injection", "false"); + DEFAULT_FLAGS.put("settings_slice_injection", "true"); DEFAULT_FLAGS.put("settings_systemui_theme", "true"); DEFAULT_FLAGS.put("settings_wifi_dpp", "true"); DEFAULT_FLAGS.put("settings_wifi_mac_randomization", "true"); diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index f58efc900427..e3a6bd7a6949 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -26,6 +26,7 @@ import android.app.KeyguardManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.ColorSpace; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; @@ -953,6 +954,24 @@ public final class Display { } /** + * Returns the preferred wide color space of the Display. + * The returned wide gamut color space is based on hardware capability and + * is preferred by the composition pipeline. + * Returns null if the display doesn't support wide color gamut. + * {@link Display#isWideColorGamut()}. + */ + @Nullable + public ColorSpace getPreferredWideGamutColorSpace() { + synchronized (this) { + updateDisplayInfoLocked(); + if (mDisplayInfo.isWideColorGamut()) { + return mGlobal.getPreferredWideGamutColorSpace(); + } + return null; + } + } + + /** * Gets the supported color modes of this device. * @hide */ diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java index ce680ecbd119..7f928f74da19 100644 --- a/core/java/android/view/textclassifier/TextClassificationConstants.java +++ b/core/java/android/view/textclassifier/TextClassificationConstants.java @@ -46,6 +46,7 @@ import java.util.StringJoiner; * entity_list_default (String[]) * entity_list_not_editable (String[]) * entity_list_editable (String[]) + * lang_id_threshold_override (float) * </pre> * * <p> @@ -94,6 +95,8 @@ public final class TextClassificationConstants { "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 @@ public final class TextClassificationConstants { 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 @@ public final class TextClassificationConstants { .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 @@ public final class TextClassificationConstants { .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 @@ public final class TextClassificationConstants { 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 @@ public final class TextClassificationConstants { 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 @@ public final class TextClassificationConstants { 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 @@ public final class TextClassificationConstants { 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 a5b7c621be38..7782079213e7 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -567,8 +567,9 @@ public final class TextClassifierImpl implements TextClassifier { } } - // 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 @@ public final class TextClassifierImpl implements TextClassifier { } 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 b1609fcfe56a..735c3eba226e 100644 --- a/core/java/android/view/textclassifier/TextLanguage.java +++ b/core/java/android/view/textclassifier/TextLanguage.java @@ -89,9 +89,10 @@ public final class TextLanguage implements Parcelable { /** * 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 @@ public final class TextLanguage implements Parcelable { } /** - * 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 afe467012307..5147306d277c 100644 --- a/core/java/android/widget/Magnifier.java +++ b/core/java/android/widget/Magnifier.java @@ -359,7 +359,7 @@ public final class Magnifier { /** * @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 @@ public final class Magnifier { /** * @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 @@ public final class Magnifier { * 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 @@ public final class Magnifier { * (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/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 6a28059d3fd0..943c726b173e 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -160,4 +160,9 @@ oneway interface IStatusBar 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/jni/Android.bp b/core/jni/Android.bp index be127009a21d..0466aaaee9fd 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -275,6 +275,7 @@ cc_library_shared { "libselinux", "libandroidicu", "libmedia", + "libmedia_helper", "libmediametrics", "libmeminfo", "libaudioclient", diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index acb34ba3dfec..cc22ff02e338 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -669,12 +669,12 @@ namespace PaintGlue { } static jint getHinting(jlong paintHandle) { - return (SkFontHinting)reinterpret_cast<Paint*>(paintHandle)->getHinting() + return (SkFontHinting)reinterpret_cast<Paint*>(paintHandle)->getSkFont().getHinting() == kNo_SkFontHinting ? 0 : 1; } static void setHinting(jlong paintHandle, jint mode) { - reinterpret_cast<Paint*>(paintHandle)->setHinting( + reinterpret_cast<Paint*>(paintHandle)->getSkFont().setHinting( mode == 0 ? kNo_SkFontHinting : kNormal_SkFontHinting); } diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 1065738466e7..a0d99ec95b55 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> @@ -1311,6 +1312,33 @@ static jint android_media_AudioTrack_get_port_id(JNIEnv *env, jobject thiz) { } // ---------------------------------------------------------------------------- +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 +1413,8 @@ static const JNINativeMethod gMethods[] = { (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}, }; diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp index 4e486630adae..4b37f13cbb33 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -423,13 +423,13 @@ bool FileDescriptorInfo::GetSocketName(const int fd, std::string* result) { } void FileDescriptorInfo::DetachSocket(fail_fn_t fail_fn) const { - const int dev_null_fd = open("/dev/null", O_RDWR); + const int dev_null_fd = open("/dev/null", O_RDWR | O_CLOEXEC); if (dev_null_fd < 0) { fail_fn(std::string("Failed to open /dev/null: ").append(strerror(errno))); } - if (dup2(dev_null_fd, fd) == -1) { - fail_fn(android::base::StringPrintf("Failed dup2 on socket descriptor %d: %s", + if (dup3(dev_null_fd, fd, O_CLOEXEC) == -1) { + fail_fn(android::base::StringPrintf("Failed dup3 on socket descriptor %d: %s", fd, strerror(errno))); } diff --git a/core/proto/android/hardware/biometrics/enums.proto b/core/proto/android/hardware/biometrics/enums.proto new file mode 100644 index 000000000000..91f2acbbaf03 --- /dev/null +++ b/core/proto/android/hardware/biometrics/enums.proto @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package android.hardware.biometrics; + +option java_outer_classname = "BiometricsProtoEnums"; +option java_multiple_files = true; + +// Logging constants for <Biometric>Service and BiometricService + +enum ModalityEnum { + MODALITY_UNKNOWN = 0; + MODALITY_FINGERPRINT = 1; // 1 << 0 + MODALITY_IRIS = 2; // 1 << 1 + MODALITY_FACE = 4; // 1 << 2 +} + +enum ClientEnum { + CLIENT_UNKNOWN = 0; + CLIENT_KEYGUARD = 1; + CLIENT_BIOMETRIC_PROMPT = 2; + CLIENT_FINGERPRINT_MANAGER = 3; // Deprecated API before BiometricPrompt was introduced +} + +enum ActionEnum { + ACTION_UNKNOWN = 0; + ACTION_ENROLL = 1; + ACTION_AUTHENTICATE = 2; + ACTION_ENUMERATE = 3; + ACTION_REMOVE = 4; +}
\ 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 aaf6c63b2978..f3733fdb3810 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -72,6 +72,9 @@ message SecureSettingsProto { // 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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 4a54bd775387..25baa921e8c9 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -43,7 +43,7 @@ <protected-broadcast android:name="android.intent.action.PACKAGE_FULLY_REMOVED" /> <protected-broadcast android:name="android.intent.action.PACKAGE_CHANGED" /> <protected-broadcast android:name="android.intent.action.PACKAGE_ENABLE_ROLLBACK" /> - <protected-broadcast android:name="android.intent.action.PACKAGE_ROLLBACK_EXECUTED" /> + <protected-broadcast android:name="android.intent.action.ROLLBACK_COMMITTED" /> <protected-broadcast android:name="android.intent.action.PACKAGE_RESTARTED" /> <protected-broadcast android:name="android.intent.action.PACKAGE_DATA_CLEARED" /> <protected-broadcast android:name="android.intent.action.PACKAGE_FIRST_LAUNCH" /> @@ -3329,7 +3329,7 @@ <permission android:name="com.android.permission.INSTALL_EXISTING_PACKAGES" android:protectionLevel="signature|privileged" /> - <!-- @SystemApi Allows an application to clear user data. + <!-- @SystemApi @TestApi Allows an application to clear user data. <p>Not for use by third-party applications @hide --> diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java index 9662182b8cf8..32bafec4081a 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 @@ import org.junit.runner.RunWith; @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 @@ public class TextClassificationConstantsTest { + "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 @@ public class TextClassificationConstantsTest { + "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/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index 4755d45cd434..c9e46942a51a 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -1821,6 +1821,8 @@ public abstract class ColorSpace { * @param cct The correlated color temperature, in Kelvin * @return Corresponding XYZ values * @throws IllegalArgumentException If cct is invalid + * + * @hide */ @NonNull @Size(3) @@ -1851,6 +1853,8 @@ public abstract class ColorSpace { * @param srcWhitePoint The white point to adapt from * @param dstWhitePoint The white point to adapt to * @return A 3x3 matrix as a non-null array of 9 floats + * + * @hide */ @NonNull @Size(9) diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 5e77fdf0a121..3440cde415d5 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -996,6 +996,50 @@ public class AudioTrack extends PlayerBase } /** + * 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 @@ public class AudioTrack extends PlayerBase 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/MediaScanner.java b/media/java/android/media/MediaScanner.java index 4eed12f428bc..33e7d8ea1b94 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -335,7 +335,7 @@ public class MediaScanner implements AutoCloseable { private final Uri mPlaylistsUri; @UnsupportedAppUsage private final Uri mFilesUri; - private final Uri mFilesUriNoNotify; + private final Uri mFilesFullUri; private final boolean mProcessPlaylists; private final boolean mProcessGenres; private int mMtpObjectHandle; @@ -445,7 +445,11 @@ public class MediaScanner implements AutoCloseable { mVideoUri = Video.Media.getContentUri(volumeName); mImagesUri = Images.Media.getContentUri(volumeName); mFilesUri = Files.getContentUri(volumeName); - mFilesUriNoNotify = mFilesUri.buildUpon().appendQueryParameter("nonotify", "1").build(); + + Uri filesFullUri = mFilesUri.buildUpon().appendQueryParameter("nonotify", "1").build(); + filesFullUri = MediaStore.setIncludePending(filesFullUri); + filesFullUri = MediaStore.setIncludeTrashed(filesFullUri); + mFilesFullUri = filesFullUri; if (!volumeName.equals("internal")) { // we only support playlists on external media @@ -1625,7 +1629,7 @@ public class MediaScanner implements AutoCloseable { try { where = Files.FileColumns.DATA + "=?"; selectionArgs = new String[] { path }; - c = mMediaProvider.query(mFilesUriNoNotify, FILES_PRESCAN_PROJECTION, + c = mMediaProvider.query(mFilesFullUri, FILES_PRESCAN_PROJECTION, where, selectionArgs, null, null); if (c != null && c.moveToFirst()) { long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX); diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp index a45aa90f5f19..417a427b6c7c 100644 --- a/media/jni/android_media_ImageReader.cpp +++ b/media/jni/android_media_ImageReader.cpp @@ -405,6 +405,11 @@ static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz, jint w nativeFormat, consumerUsage); return; } + + if (consumerUsage & GRALLOC_USAGE_PROTECTED) { + gbConsumer->setConsumerIsProtected(true); + } + ctx->setBufferConsumer(bufferConsumer); bufferConsumer->setName(consumerName); diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp index 5fae9d5a7974..3156732ef02b 100644 --- a/native/android/surface_control.cpp +++ b/native/android/surface_control.cpp @@ -47,32 +47,13 @@ using Transaction = SurfaceComposerClient::Transaction; static bool getWideColorSupport(const sp<SurfaceControl>& surfaceControl) { sp<SurfaceComposerClient> client = surfaceControl->getClient(); sp<IBinder> display(client->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); - - Vector<ui::ColorMode> colorModes; - status_t err = client->getDisplayColorModes(display, &colorModes); + bool isWideColorDisplay = false; + status_t err = client->isWideColorDisplay(display, &isWideColorDisplay); if (err) { ALOGE("unable to get wide color support"); return false; } - - bool wideColorBoardConfig = - getBool<ISurfaceFlingerConfigs, - &ISurfaceFlingerConfigs::hasWideColorDisplay>(false); - - for (android::ui::ColorMode colorMode : colorModes) { - switch (colorMode) { - case ui::ColorMode::DISPLAY_P3: - case ui::ColorMode::ADOBE_RGB: - case ui::ColorMode::DCI_P3: - if (wideColorBoardConfig) { - return true; - } - break; - default: - break; - } - } - return false; + return isWideColorDisplay; } static bool getHdrSupport(const sp<SurfaceControl>& surfaceControl) { diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java index 83084c519f19..7eaf04bfb775 100644 --- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java +++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java @@ -17,7 +17,6 @@ package com.android.captiveportallogin; import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_PROBE_SPEC; -import static android.net.captiveportal.CaptivePortalProbeSpec.HTTP_LOCATION_HEADER_NAME; import android.app.Activity; import android.app.AlertDialog; @@ -40,8 +39,6 @@ import android.net.http.SslError; import android.net.wifi.WifiInfo; import android.os.Build; import android.os.Bundle; -import android.provider.Settings; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; @@ -61,16 +58,17 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; -import java.lang.InterruptedException; -import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.util.Objects; import java.util.Random; import java.util.concurrent.atomic.AtomicBoolean; @@ -81,6 +79,7 @@ public class CaptivePortalLoginActivity extends Activity { private static final boolean VDBG = false; private static final int SOCKET_TIMEOUT_MS = 10000; + public static final String HTTP_LOCATION_HEADER_NAME = "Location"; private enum Result { DISMISSED(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_DISMISSED), diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java index f346b00f85b8..ce58d4ea84aa 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 @@ public class Assistant extends NotificationAssistantService { 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 a2da0a079550..9b0d896e483e 100644 --- a/packages/NetworkStack/Android.bp +++ b/packages/NetworkStack/Android.bp @@ -21,6 +21,7 @@ java_library { 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 000000000000..55ba5916b7d8 --- /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 50c4dfc8d700..08452bbbe433 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_ICMPV6; 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 @@ public class ApfFilter { // 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 04ac9a301813..12eecc070a06 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_BROADCAST; 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 @@ public class DhcpClient extends StateMachine { 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 0d298de4f5f8..0a15cd7d3bd9 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 @@ class DhcpLeaseRepository { 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 ce8b7e78d0f8..d7ff98b1f501 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 abstract class DhcpPacket { 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 abstract class DhcpPacket { */ 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 @@ public abstract class DhcpPacket { 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 7b112dfc125c..beabd3eb3152 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.DHCP_SERVER; 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_BROADCAST; 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 @@ public class DhcpServer extends IDhcpServer.Stub { 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 @@ public class DhcpServer extends IDhcpServer.Stub { 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 @@ public class DhcpServer extends IDhcpServer.Stub { } 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 @@ public class DhcpServer extends IDhcpServer.Stub { 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 868f3bed4eed..31ce95b11ba9 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.NonNull; 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 @@ public class DhcpServingParams { */ @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/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java index ad7f85d0a30a..f20e01636d72 100644 --- a/packages/NetworkStack/src/android/net/ip/IpClient.java +++ b/packages/NetworkStack/src/android/net/ip/IpClient.java @@ -29,7 +29,6 @@ import android.net.INetd; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; -import android.net.Network; import android.net.ProvisioningConfigurationParcelable; import android.net.ProxyInfo; import android.net.ProxyInfoParcelable; @@ -1000,7 +999,9 @@ public class IpClient extends StateMachine { // mDhcpResults is never shared with any other owner so we don't have // to worry about concurrent modification. if (mDhcpResults != null) { - for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) { + final List<RouteInfo> routes = + mDhcpResults.toStaticIpConfiguration().getRoutes(mInterfaceName); + for (RouteInfo route : routes) { newLp.addRoute(route); } addAllReachableDnsServers(newLp, mDhcpResults.dnsServers); diff --git a/packages/NetworkStack/src/android/net/ip/IpNeighborMonitor.java b/packages/NetworkStack/src/android/net/ip/IpNeighborMonitor.java index eb993a4243a9..2e6ff243a628 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.Os; 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 @@ public class IpNeighborMonitor extends PacketReader { 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 761db6822fb4..76a03387a12d 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.metrics.IpReachabilityEvent; 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 class IpReachabilityMonitor { } 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 000000000000..6dcf0c0d1626 --- /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 000000000000..d3b40a67633d --- /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 000000000000..14e6c5fdadb9 --- /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 4080ddf9e73f..631ee453671a 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.app.Service; 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 @@ import java.util.Iterator; */ 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 @@ public class NetworkStackService extends Service { * <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 @@ public class NetworkStackService extends Service { 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 @@ public class NetworkStackService extends Service { 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 6b31b82ec3cc..96eaa505389d 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_FAILURE; 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.text.TextUtils; 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; @@ -93,6 +93,7 @@ import java.net.URL; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -1146,7 +1147,10 @@ public class NetworkMonitor extends StateMachine { return null; } - return CaptivePortalProbeSpec.parseCaptivePortalProbeSpecs(settingsValue); + final Collection<CaptivePortalProbeSpec> specs = + CaptivePortalProbeSpec.parseCaptivePortalProbeSpecs(settingsValue); + final CaptivePortalProbeSpec[] specsArray = new CaptivePortalProbeSpec[specs.size()]; + return specs.toArray(specsArray); } catch (Exception e) { // Don't let a misconfiguration bootloop the system. Log.e(TAG, "Error parsing configured fallback probe specs", e); @@ -1169,7 +1173,7 @@ public class NetworkMonitor extends StateMachine { } 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 eedaf30c055f..804765e33a87 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 final class NetworkStackConstants { 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 51d50d9eb13a..4abd77e9cfde 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.DhcpLease.HOSTNAME_NONE; 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 @@ import java.util.Set; @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 @@ public class DhcpLeaseRepositoryTest { 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 @@ public class DhcpLeaseRepositoryTest { 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 class DhcpLeaseRepositoryTest { 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 @@ public class DhcpLeaseRepositoryTest { 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 @@ public class DhcpLeaseRepositoryTest { 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 @@ public class DhcpLeaseRepositoryTest { @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 class DhcpLeaseRepositoryTest { 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 @@ public class DhcpLeaseRepositoryTest { @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 @@ public class DhcpLeaseRepositoryTest { @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 @@ public class DhcpLeaseRepositoryTest { @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 @@ public class DhcpLeaseRepositoryTest { @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 @@ public class DhcpLeaseRepositoryTest { @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 class DhcpLeaseRepositoryTest { 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 @@ public class DhcpLeaseRepositoryTest { @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 @@ public class DhcpLeaseRepositoryTest { // 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 @@ public class DhcpLeaseRepositoryTest { 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 @@ public class DhcpLeaseRepositoryTest { 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 @@ public class DhcpLeaseRepositoryTest { */ 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 a592809618e6..7544e72da02e 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.DhcpResults; 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.Arrays; 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 @@ public class DhcpPacketTest { 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 3ca0564f24d6..1004382b3adf 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 static junit.framework.Assert.assertTrue; 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 @@ public class DhcpServingParamsTest { } 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/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 4f6a4ad94479..a1aefabfc7f2 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 @@ class SettingsProtoDumpUtil { 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/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 904478efb568..015085205460 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 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< 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 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< 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 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } } + @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 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< 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 9740d1dd6d1c..e11ec2d24e29 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 @@ package com.android.systemui.statusbar; 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 android.view.WindowManagerGlobal; 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 @@ import javax.inject.Singleton; /** 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 @@ public class NavigationBarController implements DisplayListener { 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 @@ public class NavigationBarController implements DisplayListener { } @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/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java index 73f3b43aad62..6177344a3ef4 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 @@ public class NavigationBarButtonTest extends SysuiTestCase { @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/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 8adc416fda95..84577f125f1d 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.FileNotFoundException; 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 @@ import java.util.Set; /** * 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 @@ public class PackageWatchdog { 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 @@ public class PackageWatchdog { 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 @@ public class PackageWatchdog { 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 @@ public class PackageWatchdog { /** * 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; + } + } } - } - // 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; + // Execute action with least user impact + if (currentObserverToNotify != null) { + currentObserverToNotify.execute(packageToReport); + } } } - } + }); } // TODO(zezeozue): Optimize write? Maybe only write a separate smaller file? @@ -278,21 +281,46 @@ public class PackageWatchdog { /** 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 @@ public class PackageWatchdog { } private void saveToFileAsync() { - mIoHandler.removeCallbacks(this::saveToFile); - mIoHandler.post(this::saveToFile); + mWorkerHandler.removeCallbacks(this::saveToFile); + mWorkerHandler.post(this::saveToFile); } /** @@ -606,7 +634,11 @@ public class PackageWatchdog { } 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/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java index d09823efb6fa..a5a515f93e75 100644 --- a/services/core/java/com/android/server/ServiceWatcher.java +++ b/services/core/java/com/android/server/ServiceWatcher.java @@ -34,11 +34,11 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; import android.util.Slog; -import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.util.Preconditions; @@ -48,6 +48,9 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; /** * Find the best Service, and bind to it. @@ -61,16 +64,20 @@ public class ServiceWatcher implements ServiceConnection { public static final String EXTRA_SERVICE_VERSION = "serviceVersion"; public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser"; + + /** Function to run on binder interface. */ + public interface BinderRunner { + /** Called to run client code with the binder. */ + void run(IBinder binder) throws RemoteException; + } + /** - * The runner that runs on the binder retrieved from {@link ServiceWatcher}. + * Function to run on binder interface. + * @param <T> Type to return. */ - public interface BinderRunner { - /** - * Runs on the retrieved binder. - * - * @param binder the binder retrieved from the {@link ServiceWatcher}. - */ - void run(IBinder binder); + public interface BlockingBinderRunner<T> { + /** Called to run client code with the binder. */ + T run(IBinder binder) throws RemoteException; } public static ArrayList<HashSet<Signature>> getSignatureSets(Context context, @@ -120,18 +127,14 @@ public class ServiceWatcher implements ServiceConnection { private final Handler mHandler; - // this lock is held to ensure the service binder is not exposed (via runOnBinder) until after - // the new service initialization work has completed - private final Object mBindLock = new Object(); - // read/write from handler thread + private IBinder mBestService; private int mCurrentUserId; // read from any thread, write from handler thread private volatile ComponentName mBestComponent; private volatile int mBestVersion; private volatile int mBestUserId; - private volatile IBinder mBestService; public ServiceWatcher(Context context, String logTag, String action, int overlaySwitchResId, int defaultServicePackageNameResId, @@ -163,17 +166,9 @@ public class ServiceWatcher implements ServiceConnection { mBestService = null; } - // called on handler thread - @GuardedBy("mBindLock") - protected void onBind() { - Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); - } + protected void onBind() {} - // called on handler thread - @GuardedBy("mBindLock") - protected void onUnbind() { - Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); - } + protected void onUnbind() {} /** * Start this watcher, including binding to the current best match and @@ -248,25 +243,6 @@ public class ServiceWatcher implements ServiceConnection { return bestComponent == null ? null : bestComponent.getPackageName(); } - /** - * Runs the given BinderRunner if currently connected. All invocations to runOnBinder are run - * serially. - */ - public final void runOnBinder(BinderRunner runner) { - synchronized (mBindLock) { - IBinder service = mBestService; - if (service != null) { - try { - runner.run(service); - } catch (Exception e) { - // remote exceptions cannot be allowed to crash system server - Log.e(TAG, "exception while while running " + runner + " on " + service - + " from " + this, e); - } - } - } - } - private boolean isServiceMissing() { return mContext.getPackageManager().queryIntentServicesAsUser(new Intent(mAction), PackageManager.MATCH_DIRECT_BOOT_AWARE @@ -380,28 +356,66 @@ public class ServiceWatcher implements ServiceConnection { mBestUserId = UserHandle.USER_NULL; } + /** + * Runs the given function asynchronously if currently connected. Suppresses any RemoteException + * thrown during execution. + */ + public final void runOnBinder(BinderRunner runner) { + runOnHandler(() -> { + if (mBestService == null) { + return; + } + + try { + runner.run(mBestService); + } catch (RuntimeException e) { + // the code being run is privileged, but may be outside the system server, and thus + // we cannot allow runtime exceptions to crash the system server + Log.e(TAG, "exception while while running " + runner + " on " + mBestService + + " from " + this, e); + } catch (RemoteException e) { + // do nothing + } + }); + } + + /** + * Runs the given function synchronously if currently connected, and returns the default value + * if not currently connected or if any exception is thrown. + */ + public final <T> T runOnBinderBlocking(BlockingBinderRunner<T> runner, T defaultValue) { + try { + return runOnHandlerBlocking(() -> { + if (mBestService == null) { + return defaultValue; + } + + try { + return runner.run(mBestService); + } catch (RemoteException e) { + return defaultValue; + } + }); + } catch (InterruptedException e) { + return defaultValue; + } + } + @Override public final void onServiceConnected(ComponentName component, IBinder binder) { - mHandler.post(() -> { + runOnHandler(() -> { if (D) Log.d(mTag, component + " connected"); - - // hold the lock so that mBestService cannot be used by runOnBinder until complete - synchronized (mBindLock) { - mBestService = binder; - onBind(); - } + mBestService = binder; + onBind(); }); } @Override public final void onServiceDisconnected(ComponentName component) { - mHandler.post(() -> { + runOnHandler(() -> { if (D) Log.d(mTag, component + " disconnected"); - mBestService = null; - synchronized (mBindLock) { - onUnbind(); - } + onUnbind(); }); } @@ -410,4 +424,32 @@ public class ServiceWatcher implements ServiceConnection { ComponentName bestComponent = mBestComponent; return bestComponent == null ? "null" : bestComponent.toShortString() + "@" + mBestVersion; } + + private void runOnHandler(Runnable r) { + if (Looper.myLooper() == mHandler.getLooper()) { + r.run(); + } else { + mHandler.post(r); + } + } + + private <T> T runOnHandlerBlocking(Callable<T> c) throws InterruptedException { + if (Looper.myLooper() == mHandler.getLooper()) { + try { + return c.call(); + } catch (Exception e) { + // Function cannot throw exception, this should never happen + throw new IllegalStateException(e); + } + } else { + FutureTask<T> task = new FutureTask<>(c); + mHandler.post(task); + try { + return task.get(); + } catch (ExecutionException e) { + // Function cannot throw exception, this should never happen + throw new IllegalStateException(e); + } + } + } } diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index ea6d435c7ba5..f6e698ff7839 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -33,8 +33,10 @@ import android.media.AudioAttributes; import android.media.AudioManager; import android.os.BatteryStats; import android.os.Binder; +import android.os.ExternalVibration; import android.os.Handler; import android.os.IBinder; +import android.os.IExternalVibratorService; import android.os.IVibratorService; import android.os.PowerManager; import android.os.PowerManager.ServiceType; @@ -75,16 +77,18 @@ public class VibratorService extends IVibratorService.Stub private static final String TAG = "VibratorService"; private static final boolean DEBUG = false; private static final String SYSTEM_UI_PACKAGE = "com.android.systemui"; + private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service"; private static final long[] DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS = { 0, 30, 100, 30 }; - // Scale levels. Each level is defined as the delta between the current setting and the default - // intensity for that type of vibration (i.e. current - default). - private static final int SCALE_VERY_LOW = -2; - private static final int SCALE_LOW = -1; - private static final int SCALE_NONE = 0; - private static final int SCALE_HIGH = 1; - private static final int SCALE_VERY_HIGH = 2; + // Scale levels. Each level, except MUTE, is defined as the delta between the current setting + // and the default intensity for that type of vibration (i.e. current - default). + private static final int SCALE_MUTE = IExternalVibratorService.SCALE_MUTE; // -100 + private static final int SCALE_VERY_LOW = IExternalVibratorService.SCALE_VERY_LOW; // -2 + private static final int SCALE_LOW = IExternalVibratorService.SCALE_LOW; // -1 + private static final int SCALE_NONE = IExternalVibratorService.SCALE_NONE; // 0 + private static final int SCALE_HIGH = IExternalVibratorService.SCALE_HIGH; // 1 + private static final int SCALE_VERY_HIGH = IExternalVibratorService.SCALE_VERY_HIGH; // 2 // Gamma adjustments for scale levels. private static final float SCALE_VERY_LOW_GAMMA = 2.0f; @@ -111,6 +115,7 @@ public class VibratorService extends IVibratorService.Stub private final int mPreviousVibrationsLimit; private final boolean mAllowPriorityVibrationsInLowPowerMode; private final boolean mSupportsAmplitudeControl; + private final boolean mSupportsExternalControl; private final int mDefaultVibrationAmplitude; private final SparseArray<VibrationEffect> mFallbackEffects; private final SparseArray<Integer> mProcStatesCache = new SparseArray(); @@ -138,18 +143,20 @@ public class VibratorService extends IVibratorService.Stub @GuardedBy("mLock") private Vibration mCurrentVibration; private int mCurVibUid = -1; + private ExternalVibration mCurrentExternalVibration; + private boolean mVibratorUnderExternalControl; private boolean mLowPowerMode; private int mHapticFeedbackIntensity; private int mNotificationIntensity; private int mRingIntensity; - native static boolean vibratorExists(); - native static void vibratorInit(); - native static void vibratorOn(long milliseconds); - native static void vibratorOff(); - native static boolean vibratorSupportsAmplitudeControl(); - native static void vibratorSetAmplitude(int amplitude); - native static long vibratorPerformEffect(long effect, long strength); + static native boolean vibratorExists(); + static native void vibratorInit(); + static native void vibratorOn(long milliseconds); + static native void vibratorOff(); + static native boolean vibratorSupportsAmplitudeControl(); + static native void vibratorSetAmplitude(int amplitude); + static native long vibratorPerformEffect(long effect, long strength); static native boolean vibratorSupportsExternalControl(); static native void vibratorSetExternalControl(boolean enabled); @@ -218,6 +225,9 @@ public class VibratorService extends IVibratorService.Stub } public boolean isHapticFeedback() { + if (VibratorService.this.isHapticFeedback(usageHint)) { + return true; + } if (effect instanceof VibrationEffect.Prebaked) { VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect; switch (prebaked.getId()) { @@ -239,19 +249,11 @@ public class VibratorService extends IVibratorService.Stub } public boolean isNotification() { - switch (usageHint) { - case AudioAttributes.USAGE_NOTIFICATION: - case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST: - case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT: - case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED: - return true; - default: - return false; - } + return VibratorService.this.isNotification(usageHint); } public boolean isRingtone() { - return usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE; + return VibratorService.this.isRingtone(usageHint); } public boolean isFromSystem() { @@ -332,6 +334,7 @@ public class VibratorService extends IVibratorService.Stub vibratorOff(); mSupportsAmplitudeControl = vibratorSupportsAmplitudeControl(); + mSupportsExternalControl = vibratorSupportsExternalControl(); mContext = context; PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); @@ -379,6 +382,8 @@ public class VibratorService extends IVibratorService.Stub mScaleLevels.put(SCALE_NONE, new ScaleLevel(SCALE_NONE_GAMMA)); mScaleLevels.put(SCALE_HIGH, new ScaleLevel(SCALE_HIGH_GAMMA)); mScaleLevels.put(SCALE_VERY_HIGH, new ScaleLevel(SCALE_VERY_HIGH_GAMMA)); + + ServiceManager.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService()); } private VibrationEffect createEffectFromResource(int resId) { @@ -562,6 +567,16 @@ public class VibratorService extends IVibratorService.Stub } } + + // If something has external control of the vibrator, assume that it's more + // important for now. + if (mCurrentExternalVibration != null) { + if (DEBUG) { + Slog.d(TAG, "Ignoring incoming vibration for current external vibration"); + } + return; + } + // If the current vibration is repeating and the incoming one is non-repeating, // then ignore the non-repeating vibration. This is so that we don't cancel // vibrations that are meant to grab the attention of the user, like ringtones and @@ -648,6 +663,11 @@ public class VibratorService extends IVibratorService.Stub mThread.cancel(); mThread = null; } + if (mCurrentExternalVibration != null) { + mCurrentExternalVibration.mute(); + mCurrentExternalVibration = null; + setVibratorUnderExternalControl(false); + } doVibratorOff(); reportFinishVibrationLocked(); } finally { @@ -1095,6 +1115,26 @@ public class VibratorService extends IVibratorService.Stub } } + private static boolean isNotification(int usageHint) { + switch (usageHint) { + case AudioAttributes.USAGE_NOTIFICATION: + case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST: + case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT: + case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED: + return true; + default: + return false; + } + } + + private static boolean isRingtone(int usageHint) { + return usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE; + } + + private static boolean isHapticFeedback(int usageHint) { + return usageHint == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION; + } + private void noteVibratorOnLocked(int uid, long millis) { try { mBatteryStatsService.noteVibratorOn(uid, millis); @@ -1116,6 +1156,18 @@ public class VibratorService extends IVibratorService.Stub } } + private void setVibratorUnderExternalControl(boolean externalControl) { + if (DEBUG) { + if (externalControl) { + Slog.d(TAG, "Vibrator going under external control."); + } else { + Slog.d(TAG, "Taking back control of vibrator."); + } + } + mVibratorUnderExternalControl = externalControl; + vibratorSetExternalControl(externalControl); + } + private class VibrateThread extends Thread { private final VibrationEffect.Waveform mWaveform; private final int mUid; @@ -1290,6 +1342,13 @@ public class VibratorService extends IVibratorService.Stub } else { pw.println("null"); } + pw.print(" mCurrentExternalVibration="); + if (mCurrentExternalVibration != null) { + pw.println(mCurrentExternalVibration.toString()); + } else { + pw.println("null"); + } + pw.println(" mVibratorUnderExternalControl=" + mVibratorUnderExternalControl); pw.println(" mLowPowerMode=" + mLowPowerMode); pw.println(" mHapticFeedbackIntensity=" + mHapticFeedbackIntensity); pw.println(" mNotificationIntensity=" + mNotificationIntensity); @@ -1310,6 +1369,87 @@ public class VibratorService extends IVibratorService.Stub new VibratorShellCommand(this).exec(this, in, out, err, args, callback, resultReceiver); } + final class ExternalVibratorService extends IExternalVibratorService.Stub { + @Override + public int onExternalVibrationStart(ExternalVibration vib) { + if (!mSupportsExternalControl) { + return SCALE_MUTE; + } + if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE, + vib.getUid(), -1 /*owningUid*/, true /*exported*/) + != PackageManager.PERMISSION_GRANTED) { + Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid() + + " tried to play externally controlled vibration" + + " without VIBRATE permission, ignoring."); + return SCALE_MUTE; + } + + final int scaleLevel; + synchronized (mLock) { + if (!vib.equals(mCurrentExternalVibration)) { + if (mCurrentExternalVibration == null) { + // If we're not under external control right now, then cancel any normal + // vibration that may be playing and ready the vibrator for external + // control. + doCancelVibrateLocked(); + setVibratorUnderExternalControl(true); + } + // At this point we either have an externally controlled vibration playing, or + // no vibration playing. Since the interface defines that only one externally + // controlled vibration can play at a time, by returning something other than + // SCALE_MUTE from this function we can be assured that if we are currently + // playing vibration, it will be muted in favor of the new vibration. + // + // Note that this doesn't support multiple concurrent external controls, as we + // would need to mute the old one still if it came from a different controller. + mCurrentExternalVibration = vib; + if (DEBUG) { + Slog.e(TAG, "Playing external vibration: " + vib); + } + } + final int usage = vib.getAudioAttributes().getUsage(); + final int defaultIntensity; + final int currentIntensity; + if (isRingtone(usage)) { + defaultIntensity = mVibrator.getDefaultRingVibrationIntensity(); + currentIntensity = mRingIntensity; + } else if (isNotification(usage)) { + defaultIntensity = mVibrator.getDefaultNotificationVibrationIntensity(); + currentIntensity = mNotificationIntensity; + } else if (isHapticFeedback(usage)) { + defaultIntensity = mVibrator.getDefaultHapticFeedbackIntensity(); + currentIntensity = mHapticFeedbackIntensity; + } else { + defaultIntensity = 0; + currentIntensity = 0; + } + scaleLevel = currentIntensity - defaultIntensity; + } + if (scaleLevel >= SCALE_VERY_LOW && scaleLevel <= SCALE_VERY_HIGH) { + return scaleLevel; + } else { + // Presumably we want to play this but something about our scaling has gone + // wrong, so just play with no scaling. + Slog.w(TAG, "Error in scaling calculations, ended up with invalid scale level " + + scaleLevel + " for vibration " + vib); + return SCALE_NONE; + } + } + + @Override + public void onExternalVibrationStop(ExternalVibration vib) { + synchronized (mLock) { + if (vib.equals(mCurrentExternalVibration)) { + mCurrentExternalVibration = null; + setVibratorUnderExternalControl(false); + if (DEBUG) { + Slog.e(TAG, "Stopping external vibration" + vib); + } + } + } + } + } + private final class VibratorShellCommand extends ShellCommand { private final IBinder mToken; diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index 41fedc5fab45..15d66e6e646f 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -29,10 +29,12 @@ import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.AppOpsManager; import android.app.IActivityTaskManager; +import android.app.KeyguardManager; import android.app.TaskStackListener; import android.app.UserSwitchObserver; import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageManager; import android.database.ContentObserver; import android.hardware.biometrics.BiometricAuthenticator; @@ -377,6 +379,13 @@ public class BiometricService extends SystemService { new BiometricTaskStackListener(); private final Random mRandom = new Random(); + // TODO(b/123378871): Remove when moved. + // When BiometricPrompt#setEnableFallback is set to true, we need to store the client (app) + // receiver. BiometricService internally launches CDCA which invokes BiometricService to + // start authentication (normal path). When auth is success/rejected, CDCA will use an aidl + // method to poke BiometricService - the result will then be forwarded to this receiver. + private IBiometricServiceReceiver mConfirmDeviceCredentialReceiver; + // The current authentication session, null if idle/done. We need to track both the current // and pending sessions since errors may be sent to either. private AuthSession mCurrentAuthSession; @@ -705,6 +714,22 @@ public class BiometricService extends SystemService { } } + // Launch CDC instead if necessary. CDC will return results through an AIDL call, since + // we can't get activity results. Store the receiver somewhere so we can forward the + // result back to the client. + // TODO(b/123378871): Remove when moved. + if (bundle.getBoolean(BiometricPrompt.KEY_ENABLE_FALLBACK)) { + mConfirmDeviceCredentialReceiver = receiver; + final KeyguardManager kgm = getContext().getSystemService(KeyguardManager.class); + // Use this so we don't need to duplicate logic.. + final Intent intent = kgm.createConfirmDeviceCredentialIntent(null /* title */, + null /* description */); + // Then give it the bundle to do magic behavior.. + intent.putExtra(KeyguardManager.EXTRA_BIOMETRIC_PROMPT_BUNDLE, bundle); + getContext().startActivityAsUser(intent, UserHandle.CURRENT); + return; + } + mHandler.post(() -> { final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId); final int modality = result.first; @@ -745,6 +770,36 @@ public class BiometricService extends SystemService { }); } + @Override // Binder call + public void onConfirmDeviceCredentialSuccess() { + checkInternalPermission(); + if (mConfirmDeviceCredentialReceiver == null) { + Slog.w(TAG, "onCDCASuccess null!"); + return; + } + try { + mConfirmDeviceCredentialReceiver.onAuthenticationSucceeded(); + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException", e); + } + mConfirmDeviceCredentialReceiver = null; + } + + @Override // Binder call + public void onConfirmDeviceCredentialError(int error, String message) { + checkInternalPermission(); + if (mConfirmDeviceCredentialReceiver == null) { + Slog.w(TAG, "onCDCAError null! Error: " + error + " " + message); + return; + } + try { + mConfirmDeviceCredentialReceiver.onError(error, message); + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException", e); + } + mConfirmDeviceCredentialReceiver = null; + } + /** * authenticate() (above) which is called from BiometricPrompt determines which * modality/modalities to start authenticating with. authenticateInternal() should only be diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java index 3db6a74a1c6c..fcf821f23ae9 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java @@ -48,14 +48,12 @@ import android.os.SELinux; import android.os.UserHandle; import android.os.UserManager; import android.util.Slog; -import android.util.StatsLog; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.internal.util.DumpUtils; import com.android.server.SystemServerInitThreadPool; -import com.android.server.biometrics.AuthenticationClient; import com.android.server.biometrics.BiometricServiceBase; import com.android.server.biometrics.BiometricUtils; import com.android.server.biometrics.ClientMonitor; @@ -588,11 +586,6 @@ public class FingerprintService extends BiometricServiceBase { public void onAcquired(final long deviceId, final int acquiredInfo, final int vendorCode) { mHandler.post(() -> { FingerprintService.super.handleAcquired(deviceId, acquiredInfo, vendorCode); - if (getLockoutMode() == AuthenticationClient.LOCKOUT_NONE - && getCurrentClient() instanceof AuthenticationClient) { - // Ignore enrollment acquisitions or acquisitions when we are locked out. - StatsLog.write(StatsLog.FINGERPRINT_ACQUIRED, mCurrentUserId, mIsCrypto); - } }); } @@ -602,22 +595,6 @@ public class FingerprintService extends BiometricServiceBase { mHandler.post(() -> { Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId); FingerprintService.super.handleAuthenticated(fp, token); - // Send authentication to statsd. - final boolean authenticated = fingerId != 0; - StatsLog.write(StatsLog.FINGERPRINT_AUTHENTICATED, mCurrentUserId, mIsCrypto, - authenticated); - if (!authenticated) { - // If we failed to authenticate because of a lockout, inform statsd. - final int lockoutMode = getLockoutMode(); - if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) { - StatsLog.write(StatsLog.FINGERPRINT_ERROR_OCCURRED, mCurrentUserId, - mIsCrypto, StatsLog.FINGERPRINT_ERROR_OCCURRED__ERROR__LOCKOUT); - } else if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) { - StatsLog.write(StatsLog.FINGERPRINT_ERROR_OCCURRED, mCurrentUserId, - mIsCrypto, - StatsLog.FINGERPRINT_ERROR_OCCURRED__ERROR__PERMANENT_LOCKOUT); - } - } }); } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index cb3f91b7b6bb..b89768ac4b68 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -36,6 +36,7 @@ import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.res.Resources; import android.content.res.TypedArray; +import android.graphics.ColorSpace; import android.graphics.Point; import android.hardware.SensorManager; import android.hardware.display.AmbientBrightnessDayStats; @@ -93,9 +94,9 @@ import com.android.server.DisplayThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.UiThread; +import com.android.server.display.ColorDisplayService.ColorDisplayServiceInternal; import com.android.server.wm.SurfaceAnimationThread; import com.android.server.wm.WindowManagerInternal; -import com.android.server.display.ColorDisplayService.ColorDisplayServiceInternal; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -294,6 +295,7 @@ public final class DisplayManagerService extends SystemService { // is rejected by the system. private final Curve mMinimumBrightnessCurve; private final Spline mMinimumBrightnessSpline; + private final ColorSpace mWideColorSpace; public DisplayManagerService(Context context) { this(context, new Injector()); @@ -322,6 +324,8 @@ public final class DisplayManagerService extends SystemService { PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting(); mCurrentUserId = UserHandle.USER_SYSTEM; + ColorSpace[] colorSpaces = SurfaceControl.getCompositionColorSpaces(); + mWideColorSpace = colorSpaces[1]; } public void setupSchedulerPolicies() { @@ -1073,6 +1077,10 @@ public final class DisplayManagerService extends SystemService { return mMinimumBrightnessCurve; } + int getPreferredWideGamutColorSpaceIdInternal() { + return mWideColorSpace.getId(); + } + private void setBrightnessConfigurationForUserInternal( @Nullable BrightnessConfiguration c, @UserIdInt int userId, @Nullable String packageName) { @@ -2128,6 +2136,16 @@ public final class DisplayManagerService extends SystemService { } } + @Override // Binder call + public int getPreferredWideGamutColorSpaceId() { + final long token = Binder.clearCallingIdentity(); + try { + return getPreferredWideGamutColorSpaceIdInternal(); + } finally { + Binder.restoreCallingIdentity(token); + } + } + void setBrightness(int brightness) { Settings.System.putIntForUser(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, brightness, UserHandle.USER_CURRENT); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java index cfc85dac8a53..4349b4aa3603 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java @@ -34,6 +34,7 @@ import android.os.UserManagerInternal; import android.provider.Settings; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.IntArray; import android.util.Pair; import android.util.Printer; @@ -756,6 +757,14 @@ final class InputMethodUtils { */ private final ArrayMap<String, String> mCopyOnWriteDataStore = new ArrayMap<>(); + private static final ArraySet<String> CLONE_TO_MANAGED_PROFILE = new ArraySet<>(); + static { + Settings.Secure.getCloneToManagedProfileSettings(CLONE_TO_MANAGED_PROFILE); + } + + private static final UserManagerInternal sUserManagerInternal = + LocalServices.getService(UserManagerInternal.class); + private boolean mCopyOnWrite = false; @NonNull private String mEnabledInputMethodsStrCache = ""; @@ -833,7 +842,9 @@ final class InputMethodUtils { if (mCopyOnWrite) { mCopyOnWriteDataStore.put(key, str); } else { - Settings.Secure.putStringForUser(mResolver, key, str, mCurrentUserId); + final int userId = CLONE_TO_MANAGED_PROFILE.contains(key) + ? sUserManagerInternal.getProfileParentId(mCurrentUserId) : mCurrentUserId; + Settings.Secure.putStringForUser(mResolver, key, str, userId); } } @@ -852,14 +863,16 @@ final class InputMethodUtils { if (mCopyOnWrite) { mCopyOnWriteDataStore.put(key, String.valueOf(value)); } else { - Settings.Secure.putIntForUser(mResolver, key, value, mCurrentUserId); + final int userId = CLONE_TO_MANAGED_PROFILE.contains(key) + ? sUserManagerInternal.getProfileParentId(mCurrentUserId) : mCurrentUserId; + Settings.Secure.putIntForUser(mResolver, key, value, userId); } } 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/location/GeocoderProxy.java b/services/core/java/com/android/server/location/GeocoderProxy.java index f1de37188510..e6f0ed9d14b0 100644 --- a/services/core/java/com/android/server/location/GeocoderProxy.java +++ b/services/core/java/com/android/server/location/GeocoderProxy.java @@ -20,8 +20,6 @@ import android.content.Context; import android.location.Address; import android.location.GeocoderParams; import android.location.IGeocodeProvider; -import android.os.RemoteException; -import android.util.Log; import com.android.internal.os.BackgroundThread; import com.android.server.ServiceWatcher; @@ -68,35 +66,22 @@ public class GeocoderProxy { public String getFromLocation(double latitude, double longitude, int maxResults, GeocoderParams params, List<Address> addrs) { - final String[] result = new String[]{"Service not Available"}; - mServiceWatcher.runOnBinder(binder -> { + return mServiceWatcher.runOnBinderBlocking(binder -> { IGeocodeProvider provider = IGeocodeProvider.Stub.asInterface(binder); - try { - result[0] = provider.getFromLocation( - latitude, longitude, maxResults, params, addrs); - } catch (RemoteException e) { - Log.w(TAG, e); - } - }); - return result[0]; + return provider.getFromLocation(latitude, longitude, maxResults, params, addrs); + }, "Service not Available"); } public String getFromLocationName(String locationName, double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude, double upperRightLongitude, int maxResults, GeocoderParams params, List<Address> addrs) { - final String[] result = new String[]{"Service not Available"}; - mServiceWatcher.runOnBinder(binder -> { + return mServiceWatcher.runOnBinderBlocking(binder -> { IGeocodeProvider provider = IGeocodeProvider.Stub.asInterface(binder); - try { - result[0] = provider.getFromLocationName(locationName, lowerLeftLatitude, - lowerLeftLongitude, upperRightLatitude, upperRightLongitude, - maxResults, params, addrs); - } catch (RemoteException e) { - Log.w(TAG, e); - } - }); - return result[0]; + return provider.getFromLocationName(locationName, lowerLeftLatitude, + lowerLeftLongitude, upperRightLatitude, upperRightLongitude, + maxResults, params, addrs); + }, "Service not Available"); } } diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java index a6da8c5e7713..6b5b1bebd20f 100644 --- a/services/core/java/com/android/server/location/LocationProviderProxy.java +++ b/services/core/java/com/android/server/location/LocationProviderProxy.java @@ -127,20 +127,16 @@ public class LocationProviderProxy extends AbstractLocationProvider { return mServiceWatcher.start(); } - private void initializeService(IBinder binder) { + private void initializeService(IBinder binder) throws RemoteException { ILocationProvider service = ILocationProvider.Stub.asInterface(binder); if (D) Log.d(TAG, "applying state to connected service " + mServiceWatcher); - try { - service.setLocationProviderManager(mManager); + service.setLocationProviderManager(mManager); - synchronized (mRequestLock) { - if (mRequest != null) { - service.setRequest(mRequest, mWorkSource); - } + synchronized (mRequestLock) { + if (mRequest != null) { + service.setRequest(mRequest, mWorkSource); } - } catch (RemoteException e) { - Log.w(TAG, e); } } @@ -157,63 +153,44 @@ public class LocationProviderProxy extends AbstractLocationProvider { } mServiceWatcher.runOnBinder(binder -> { ILocationProvider service = ILocationProvider.Stub.asInterface(binder); - try { - service.setRequest(request, source); - } catch (RemoteException e) { - Log.w(TAG, e); - } + service.setRequest(request, source); }); } @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println(" service=" + mServiceWatcher); - mServiceWatcher.runOnBinder(binder -> { + mServiceWatcher.runOnBinderBlocking(binder -> { try { TransferPipe.dumpAsync(binder, fd, args); } catch (IOException | RemoteException e) { - pw.println(" failed to dump location provider: " + e); + pw.println(" failed to dump location provider"); } - }); + return null; + }, null); } @Override public int getStatus(Bundle extras) { - int[] status = new int[] {LocationProvider.TEMPORARILY_UNAVAILABLE}; - mServiceWatcher.runOnBinder(binder -> { + return mServiceWatcher.runOnBinderBlocking(binder -> { ILocationProvider service = ILocationProvider.Stub.asInterface(binder); - try { - status[0] = service.getStatus(extras); - } catch (RemoteException e) { - Log.w(TAG, e); - } - }); - return status[0]; + return service.getStatus(extras); + }, LocationProvider.TEMPORARILY_UNAVAILABLE); } @Override public long getStatusUpdateTime() { - long[] updateTime = new long[] {0L}; - mServiceWatcher.runOnBinder(binder -> { + return mServiceWatcher.runOnBinderBlocking(binder -> { ILocationProvider service = ILocationProvider.Stub.asInterface(binder); - try { - updateTime[0] = service.getStatusUpdateTime(); - } catch (RemoteException e) { - Log.w(TAG, e); - } - }); - return updateTime[0]; + return service.getStatusUpdateTime(); + }, 0L); } @Override public void sendExtraCommand(String command, Bundle extras) { mServiceWatcher.runOnBinder(binder -> { ILocationProvider service = ILocationProvider.Stub.asInterface(binder); - try { - service.sendExtraCommand(command, extras); - } catch (RemoteException e) { - Log.w(TAG, e); - } + service.sendExtraCommand(command, extras); }); } } diff --git a/services/core/java/com/android/server/location/OWNERS b/services/core/java/com/android/server/location/OWNERS index 92b4d5fea113..c2c95e6042de 100644 --- a/services/core/java/com/android/server/location/OWNERS +++ b/services/core/java/com/android/server/location/OWNERS @@ -1,6 +1,8 @@ +aadmal@google.com arthuri@google.com bduddie@google.com gomo@google.com sooniln@google.com weiwa@google.com wyattriley@google.com +yuhany@google.com diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index a1646862de9f..de3f50ad8c2c 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1087,7 +1087,8 @@ public class NotificationManagerService extends SystemService { || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE) || action.equals(Intent.ACTION_PACKAGES_SUSPENDED) - || action.equals(Intent.ACTION_PACKAGES_UNSUSPENDED)) { + || action.equals(Intent.ACTION_PACKAGES_UNSUSPENDED) + || action.equals(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED)) { int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL); String pkgList[] = null; @@ -1108,6 +1109,23 @@ public class NotificationManagerService extends SystemService { uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST); cancelNotifications = false; unhideNotifications = true; + } else if (action.equals(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED)) { + final int distractionRestrictions = + intent.getIntExtra(Intent.EXTRA_DISTRACTION_RESTRICTIONS, + PackageManager.RESTRICTION_NONE); + if ((distractionRestrictions + & PackageManager.RESTRICTION_HIDE_NOTIFICATIONS) != 0) { + pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST); + cancelNotifications = false; + hideNotifications = true; + } else { + pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST); + cancelNotifications = false; + unhideNotifications = true; + } + } else if (queryRestart) { pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)}; @@ -1651,6 +1669,7 @@ public class NotificationManagerService extends SystemService { IntentFilter suspendedPkgFilter = new IntentFilter(); suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED); suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED); + suspendedPkgFilter.addAction(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED); getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, suspendedPkgFilter, null, null); @@ -7743,6 +7762,20 @@ public class NotificationManagerService extends SystemService { mPackageIntentReceiver.onReceive(getContext(), intent); } + @VisibleForTesting + protected void simulatePackageDistractionBroadcast(int flag, String[] pkgs) { + // only use for testing: mimic receive broadcast that package is (un)distracting + // but does not actually register that info with packagemanager + final Bundle extras = new Bundle(); + extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgs); + extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, flag); + + final Intent intent = new Intent(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED); + intent.putExtras(extras); + + mPackageIntentReceiver.onReceive(getContext(), intent); + } + /** * Wrapper for a StatusBarNotification object that allows transfer across a oneway * binder without sending large amounts of data over a oneway transaction. diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index ac965faedd34..ab49ebb66fbf 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 @@ public final class NotificationRecord { 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/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java index 3d88f20f0710..2aaa1edcfad9 100644 --- a/services/core/java/com/android/server/notification/NotificationShellCmd.java +++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java @@ -176,6 +176,14 @@ public class NotificationShellCmd extends ShellCommand { // only use for testing mDirectService.simulatePackageSuspendBroadcast(false, getNextArgRequired()); } + case "distract_package": { + // only use for testing + // Flag values are in + // {@link android.content.pm.PackageManager.DistractionRestriction}. + mDirectService.simulatePackageDistractionBroadcast( + Integer.parseInt(getNextArgRequired()), + getNextArgRequired().split(",")); + } break; case "post": case "notify": 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 000000000000..2ec39931789a --- /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 1c7596b80fd7..7d8c7c9ee0d5 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.BroadcastReceiver; 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 @@ public class RoleManagerService extends SystemService implements RoleUserState.C 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 8b4c410000bb..e59228a16fb1 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -28,7 +28,6 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageParser; import android.content.pm.ParceledListSlice; -import android.content.pm.StringParceledListSlice; import android.content.pm.VersionedPackage; import android.content.rollback.IRollbackManager; import android.content.rollback.PackageRollbackInfo; @@ -56,12 +55,10 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; -import java.util.Set; /** * Implementation of service that manages APK level rollbacks. @@ -200,48 +197,20 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } @Override - public RollbackInfo getAvailableRollback(String packageName) { + public ParceledListSlice getAvailableRollbacks() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.MANAGE_ROLLBACKS, - "getAvailableRollback"); + "getAvailableRollbacks"); - RollbackData data = getRollbackForPackage(packageName); - if (data == null) { - return null; - } - - // Note: The rollback for the package ought to be for the currently - // installed version, otherwise the rollback data is out of date. In - // that rare case, we'll check when we execute the rollback whether - // it's out of date or not, so no need to check package versions here. - - for (PackageRollbackInfo info : data.packages) { - if (info.getPackageName().equals(packageName)) { - // TODO: Once the RollbackInfo API supports info about - // dependant packages, add that info here. - return new RollbackInfo(data.rollbackId, info); - } - } - return null; - } - - @Override - public StringParceledListSlice getPackagesWithAvailableRollbacks() { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.MANAGE_ROLLBACKS, - "getPackagesWithAvailableRollbacks"); - - final Set<String> packageNames = new HashSet<>(); synchronized (mLock) { ensureRollbackDataLoadedLocked(); + List<RollbackInfo> rollbacks = new ArrayList<>(); for (int i = 0; i < mAvailableRollbacks.size(); ++i) { RollbackData data = mAvailableRollbacks.get(i); - for (PackageRollbackInfo info : data.packages) { - packageNames.add(info.getPackageName()); - } + rollbacks.add(new RollbackInfo(data.rollbackId, data.packages)); } + return new ParceledListSlice<>(rollbacks); } - return new StringParceledListSlice(new ArrayList<>(packageNames)); } @Override @@ -258,7 +227,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } @Override - public void executeRollback(RollbackInfo rollback, String callerPackageName, + public void commitRollback(int rollbackId, String callerPackageName, IntentSender statusReceiver) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.MANAGE_ROLLBACKS, @@ -269,28 +238,21 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { 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) { - String targetPackageName = rollback.targetPackage.getPackageName(); - Log.i(TAG, "Initiating rollback of " + targetPackageName); + Log.i(TAG, "Initiating rollback"); - // Get the latest RollbackData for the target package. - final RollbackData data = getRollbackForPackage(targetPackageName); + RollbackData data = getRollbackForId(rollbackId); if (data == null) { - sendFailure(statusReceiver, "No rollback available for package."); - return; - } - - if (data.rollbackId != rollback.getRollbackId()) { - sendFailure(statusReceiver, "Rollback for package is out of date."); + sendFailure(statusReceiver, "Rollback unavailable"); return; } @@ -335,14 +297,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { PackageManager pm = context.getPackageManager(); try { PackageInstaller packageInstaller = pm.getPackageInstaller(); - String installerPackageName = pm.getInstallerPackageName(targetPackageName); - if (installerPackageName == null) { - sendFailure(statusReceiver, "Cannot find installer package"); - return; - } PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); - parentParams.setInstallerPackageName(installerPackageName); parentParams.setAllowDowngrade(true); parentParams.setMultiPackage(); int parentSessionId = packageInstaller.createSession(parentParams); @@ -351,6 +307,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { for (PackageRollbackInfo info : data.packages) { PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); + String installerPackageName = pm.getInstallerPackageName(info.getPackageName()); + if (installerPackageName == null) { + sendFailure(statusReceiver, "Cannot find installer package"); + return; + } params.setInstallerPackageName(installerPackageName); params.setAllowDowngrade(true); int sessionId = packageInstaller.createSession(params); @@ -389,10 +350,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { return; } - addRecentlyExecutedRollback(rollback); + addRecentlyExecutedRollback( + new RollbackInfo(data.rollbackId, data.packages)); sendSuccess(statusReceiver); - Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED); + Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED); // TODO: This call emits the warning "Calling a method in the // system process without a qualified user". Fix that. @@ -406,7 +368,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { data.inProgress = true; parentSession.commit(receiver.getIntentSender()); } catch (IOException e) { - Log.e(TAG, "Unable to roll back " + targetPackageName, e); + Log.e(TAG, "Rollback failed", e); sendFailure(statusReceiver, "IOException: " + e.toString()); return; } @@ -537,9 +499,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { boolean changed = false; while (iter.hasNext()) { RollbackInfo rollback = iter.next(); - if (packageName.equals(rollback.targetPackage.getPackageName())) { - iter.remove(); - changed = true; + for (PackageRollbackInfo info : rollback.getPackages()) { + if (packageName.equals(info.getPackageName())) { + iter.remove(); + changed = true; + break; + } } } @@ -935,6 +900,25 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { return null; } + /* + * Returns the RollbackData, if any, for an available rollback with the + * given rollbackId. + */ + private RollbackData getRollbackForId(int rollbackId) { + synchronized (mLock) { + // TODO: Have ensureRollbackDataLoadedLocked return the list of + // available rollbacks, to hopefully avoid forgetting to call it? + ensureRollbackDataLoadedLocked(); + for (int i = 0; i < mAvailableRollbacks.size(); ++i) { + RollbackData data = mAvailableRollbacks.get(i); + if (data.rollbackId == rollbackId) { + return data; + } + } + } + return null; + } + @GuardedBy("mLock") private int allocateRollbackIdLocked() throws IOException { int n = 0; diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java index 1f2f1ccd7383..fcae61895b36 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -19,6 +19,7 @@ package com.android.server.rollback; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInstaller; +import android.content.rollback.PackageRollbackInfo; import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; import android.os.Handler; @@ -26,6 +27,7 @@ import android.os.HandlerThread; import com.android.server.PackageWatchdog; import com.android.server.PackageWatchdog.PackageHealthObserver; +import com.android.server.PackageWatchdog.PackageHealthObserverImpact; import java.util.List; @@ -38,10 +40,12 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve 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(); @@ -49,16 +53,48 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } @Override - public boolean onHealthCheckFailed(String packageName) { - RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); - RollbackInfo rollback = rollbackManager.getAvailableRollback(packageName); - if (rollback != null) { - // 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; } /** @@ -69,26 +105,15 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve 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.executeRollback(rollback, rollbackReceiver.getIntentSender()); - } - - @Override - public String getName() { - return NAME; + } + } + return null; } } diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java index 7738be9d32bb..3b24b3e9a444 100644 --- a/services/core/java/com/android/server/rollback/RollbackStore.java +++ b/services/core/java/com/android/server/rollback/RollbackStore.java @@ -114,13 +114,9 @@ class RollbackStore { for (int i = 0; i < array.length(); ++i) { JSONObject element = array.getJSONObject(i); int rollbackId = element.getInt("rollbackId"); - String packageName = element.getString("packageName"); - long higherVersionCode = element.getLong("higherVersionCode"); - long lowerVersionCode = element.getLong("lowerVersionCode"); - PackageRollbackInfo target = new PackageRollbackInfo( - new VersionedPackage(packageName, higherVersionCode), - new VersionedPackage(packageName, lowerVersionCode)); - RollbackInfo rollback = new RollbackInfo(rollbackId, target); + List<PackageRollbackInfo> packages = packageRollbackInfosFromJson( + element.getJSONArray("packages")); + RollbackInfo rollback = new RollbackInfo(rollbackId, packages); recentlyExecutedRollbacks.add(rollback); } } catch (IOException | JSONException e) { @@ -155,18 +151,8 @@ class RollbackStore { void saveAvailableRollback(RollbackData data) throws IOException { try { JSONObject dataJson = new JSONObject(); - JSONArray packagesJson = new JSONArray(); - for (PackageRollbackInfo info : data.packages) { - JSONObject infoJson = new JSONObject(); - infoJson.put("packageName", info.getPackageName()); - infoJson.put("higherVersionCode", - info.getVersionRolledBackFrom().getLongVersionCode()); - infoJson.put("lowerVersionCode", - info.getVersionRolledBackTo().getVersionCode()); - packagesJson.put(infoJson); - } dataJson.put("rollbackId", data.rollbackId); - dataJson.put("packages", packagesJson); + dataJson.put("packages", toJson(data.packages)); dataJson.put("timestamp", data.timestamp.toString()); PrintWriter pw = new PrintWriter(new File(data.backupDir, "rollback.json")); @@ -200,11 +186,7 @@ class RollbackStore { RollbackInfo rollback = recentlyExecutedRollbacks.get(i); JSONObject element = new JSONObject(); element.put("rollbackId", rollback.getRollbackId()); - element.put("packageName", rollback.targetPackage.getPackageName()); - element.put("higherVersionCode", - rollback.targetPackage.getVersionRolledBackFrom().getLongVersionCode()); - element.put("lowerVersionCode", - rollback.targetPackage.getVersionRolledBackTo().getLongVersionCode()); + element.put("packages", toJson(rollback.getPackages())); array.put(element); } @@ -229,18 +211,7 @@ class RollbackStore { int rollbackId = dataJson.getInt("rollbackId"); RollbackData data = new RollbackData(rollbackId, backupDir); - - JSONArray packagesJson = dataJson.getJSONArray("packages"); - for (int i = 0; i < packagesJson.length(); ++i) { - JSONObject infoJson = packagesJson.getJSONObject(i); - String packageName = infoJson.getString("packageName"); - long higherVersionCode = infoJson.getLong("higherVersionCode"); - long lowerVersionCode = infoJson.getLong("lowerVersionCode"); - data.packages.add(new PackageRollbackInfo( - new VersionedPackage(packageName, higherVersionCode), - new VersionedPackage(packageName, lowerVersionCode))); - } - + data.packages.addAll(packageRollbackInfosFromJson(dataJson.getJSONArray("packages"))); data.timestamp = Instant.parse(dataJson.getString("timestamp")); return data; } catch (JSONException | DateTimeParseException e) { @@ -248,6 +219,40 @@ class RollbackStore { } } + private JSONObject toJson(PackageRollbackInfo info) throws JSONException { + JSONObject json = new JSONObject(); + json.put("packageName", info.getPackageName()); + json.put("higherVersionCode", info.getVersionRolledBackFrom().getLongVersionCode()); + json.put("lowerVersionCode", info.getVersionRolledBackTo().getLongVersionCode()); + return json; + } + + private PackageRollbackInfo packageRollbackInfoFromJson(JSONObject json) throws JSONException { + String packageName = json.getString("packageName"); + long higherVersionCode = json.getLong("higherVersionCode"); + long lowerVersionCode = json.getLong("lowerVersionCode"); + return new PackageRollbackInfo( + new VersionedPackage(packageName, higherVersionCode), + new VersionedPackage(packageName, lowerVersionCode)); + } + + private JSONArray toJson(List<PackageRollbackInfo> infos) throws JSONException { + JSONArray json = new JSONArray(); + for (PackageRollbackInfo info : infos) { + json.put(toJson(info)); + } + return json; + } + + private List<PackageRollbackInfo> packageRollbackInfosFromJson(JSONArray json) + throws JSONException { + List<PackageRollbackInfo> infos = new ArrayList<>(); + for (int i = 0; i < json.length(); ++i) { + infos.add(packageRollbackInfoFromJson(json.getJSONObject(i))); + } + return infos; + } + /** * Deletes a file completely. * If the file is a directory, its contents are deleted as well. diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 9135d1db7676..c6d2870a24c9 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -1897,7 +1897,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pullCategorySize(tagId, elapsedNanos, wallClockNanos, ret); break; } - case StatsLog.NUM_FINGERPRINTS: { + case StatsLog.NUM_FINGERPRINTS_ENROLLED: { pullNumFingerprints(tagId, elapsedNanos, wallClockNanos, ret); break; } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java index a66f0caee56c..b9b5aae03587 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 @@ public interface StatusBarManagerInternal { * @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 8d2bab43875d..7e87c29e7a58 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 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } 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/testharness/TestHarnessModeService.java b/services/core/java/com/android/server/testharness/TestHarnessModeService.java index 23c042a57ac8..4adce586e9a5 100644 --- a/services/core/java/com/android/server/testharness/TestHarnessModeService.java +++ b/services/core/java/com/android/server/testharness/TestHarnessModeService.java @@ -66,6 +66,7 @@ public class TestHarnessModeService extends SystemService { private static final String TEST_HARNESS_MODE_PROPERTY = "persist.sys.test_harness"; private PersistentDataBlockManagerInternal mPersistentDataBlockManagerInternal; + private boolean mShouldSetUpTestHarnessMode; public TestHarnessModeService(Context context) { super(context); @@ -96,6 +97,7 @@ public class TestHarnessModeService extends SystemService { // There's no data to apply, so leave it as-is. return; } + mShouldSetUpTestHarnessMode = true; PersistentData persistentData = PersistentData.fromBytes(testHarnessModeData); SystemProperties.set(TEST_HARNESS_MODE_PROPERTY, persistentData.mEnabled ? "1" : "0"); @@ -124,6 +126,9 @@ public class TestHarnessModeService extends SystemService { } private void disableAutoSync() { + if (!mShouldSetUpTestHarnessMode) { + return; + } UserInfo primaryUser = UserManager.get(getContext()).getPrimaryUser(); ContentResolver .setMasterSyncAutomaticallyAsUser(false, primaryUser.getUserHandle().getIdentifier()); diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 69040a2ea974..b0ef8a0d4209 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -469,6 +469,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub */ private void extractColors(WallpaperData wallpaper) { String cropFile = null; + boolean defaultImageWallpaper = false; int wallpaperId; if (wallpaper.equals(mFallbackWallpaper)) { @@ -482,6 +483,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub || wallpaper.wallpaperComponent == null; if (imageWallpaper && wallpaper.cropFile != null && wallpaper.cropFile.exists()) { cropFile = wallpaper.cropFile.getAbsolutePath(); + } else if (imageWallpaper && !wallpaper.cropExists() && !wallpaper.sourceExists()) { + defaultImageWallpaper = true; } wallpaperId = wallpaper.wallpaperId; } @@ -493,6 +496,25 @@ public class WallpaperManagerService extends IWallpaperManager.Stub colors = WallpaperColors.fromBitmap(bitmap); bitmap.recycle(); } + } else if (defaultImageWallpaper) { + // There is no crop and source file because this is default image wallpaper. + try (final InputStream is = + WallpaperManager.openDefaultWallpaper(mContext, FLAG_SYSTEM)) { + if (is != null) { + try { + final BitmapFactory.Options options = new BitmapFactory.Options(); + final Bitmap bitmap = BitmapFactory.decodeStream(is, null, options); + if (bitmap != null) { + colors = WallpaperColors.fromBitmap(bitmap); + bitmap.recycle(); + } + } catch (OutOfMemoryError e) { + Slog.w(TAG, "Can't decode default wallpaper stream", e); + } + } + } catch (IOException e) { + Slog.w(TAG, "Can't close default wallpaper stream", e); + } } if (colors == null) { diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java index 65d66f44b5dd..e817dd47e756 100644 --- a/services/core/java/com/android/server/wm/ActivityDisplay.java +++ b/services/core/java/com/android/server/wm/ActivityDisplay.java @@ -558,22 +558,26 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> } /** - * Pause all activities in either all of the stacks or just the back stacks. + * Pause all activities in either all of the stacks or just the back stacks. This is done before + * resuming a new activity and to make sure that previously active activities are + * paused in stacks that are no longer visible or in pinned windowing mode. This does not + * pause activities in visible stacks, so if an activity is launched within the same stack/task, + * then we should explicitly pause that stack's top activity. * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving(). * @param resuming The resuming activity. * @param dontWait The resuming activity isn't going to wait for all activities to be paused * before resuming. - * @return true if any activity was paused as a result of this call. + * @return {@code true} if any activity was paused as a result of this call. */ boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) { boolean someActivityPaused = false; for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { final ActivityStack stack = mStacks.get(stackNdx); - // TODO(b/111541062): Check if resumed activity on this display instead - if (!mRootActivityContainer.isTopDisplayFocusedStack(stack) - && stack.getResumedActivity() != null) { + final ActivityRecord resumedActivity = stack.getResumedActivity(); + if (resumedActivity != null + && (!stack.shouldBeVisible(resuming) || !stack.isFocusable())) { if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack + - " mResumedActivity=" + stack.getResumedActivity()); + " mResumedActivity=" + resumedActivity); someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming, dontWait); } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b8634d88319a..b7c35c083174 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1946,30 +1946,90 @@ final class ActivityRecord extends ConfigurationContainer { try { mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, WindowVisibilityItem.obtain(true /* showWindow */)); - if (shouldPauseWhenBecomingVisible()) { - // An activity must be in the {@link PAUSING} state for the system to validate - // the move to {@link PAUSED}. - setState(PAUSING, "makeVisibleIfNeeded"); + makeActiveIfNeeded(null /* activeActivity*/); + } catch (Exception e) { + Slog.w(TAG, "Exception thrown sending visibility update: " + intent.getComponent(), e); + } + } + + /** + * Make activity resumed or paused if needed. + * @param activeActivity an activity that is resumed or just completed pause action. + * We won't change the state of this activity. + */ + boolean makeActiveIfNeeded(ActivityRecord activeActivity) { + if (shouldResumeActivity(activeActivity)) { + if (DEBUG_VISIBILITY) { + Slog.v("TAG_VISIBILITY", "Resume visible activity, " + this); + } + return getActivityStack().resumeTopActivityUncheckedLocked(activeActivity /* prev */, + null /* options */); + } else if (shouldPauseActivity(activeActivity)) { + if (DEBUG_VISIBILITY) { + Slog.v("TAG_VISIBILITY", "Pause visible activity, " + this); + } + // An activity must be in the {@link PAUSING} state for the system to validate + // the move to {@link PAUSED}. + setState(PAUSING, "makeVisibleIfNeeded"); + try { mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, PauseActivityItem.obtain(finishing, false /* userLeaving */, configChangeFlags, false /* dontReport */)); + } catch (Exception e) { + Slog.w(TAG, "Exception thrown sending pause: " + intent.getComponent(), e); } - } catch (Exception e) { - Slog.w(TAG, "Exception thrown sending visibility update: " + intent.getComponent(), e); } + return false; } - /** Check if activity should be moved to PAUSED state when it becomes visible. */ - private boolean shouldPauseWhenBecomingVisible() { - // If the activity is stopped or stopping, cycle to the paused state. We avoid doing + /** + * Check if activity should be moved to PAUSED state. The activity: + * - should be eligible to be made active (see {@link #shouldMakeActive(ActivityRecord)}) + * - should be non-focusable + * - should not be currently pausing or paused + * @param activeActivity the activity that is active or just completed pause action. We won't + * resume if this activity is active. + */ + private boolean shouldPauseActivity(ActivityRecord activeActivity) { + return shouldMakeActive(activeActivity) && !isFocusable() && !isState(PAUSING, PAUSED); + } + + /** + * Check if activity should be moved to RESUMED state. The activity: + * - should be eligible to be made active (see {@link #shouldMakeActive(ActivityRecord)}) + * - should be focusable + * @param activeActivity the activity that is active or just completed pause action. We won't + * resume if this activity is active. + */ + private boolean shouldResumeActivity(ActivityRecord activeActivity) { + return shouldMakeActive(activeActivity) && isFocusable() && !isState(RESUMED); + } + + /** + * Check if activity is eligible to be made active (resumed of paused). The activity: + * - should be paused, stopped or stopping + * - should not be the currently active one or launching behind other tasks + * - should be either the topmost in task, or right below the top activity that is finishing + * If all of these conditions are not met at the same time, the activity cannot be made active. + */ + private boolean shouldMakeActive(ActivityRecord activeActivity) { + // If the activity is stopped, stopping, cycle to an active state. We avoid doing // this when there is an activity waiting to become translucent as the extra binder // calls will lead to noticeable jank. A later call to - // ActivityStack#ensureActivitiesVisibleLocked will bring the activity to the proper - // paused state. We also avoid doing this for the activity the stack supervisor - // considers the resumed activity, as normal means will bring the activity from STOPPED - // to RESUMED. Adding PAUSING in this scenario will lead to double lifecycles. - if (!isState(STOPPED, STOPPING) || getActivityStack().mTranslucentActivityWaiting != null - || isResumedActivityOnDisplay()) { + // ActivityStack#ensureActivitiesVisibleLocked will bring the activity to a proper + // active state. + if (!isState(RESUMED, PAUSED, STOPPED, STOPPING) + || getActivityStack().mTranslucentActivityWaiting != null) { + return false; + } + + if (this == activeActivity) { + return false; + } + + if (this.mLaunchTaskBehind) { + // This activity is being launched from behind, which means that it's not intended to be + // presented to user right now, even if it's set to be visible. return false; } @@ -1979,14 +2039,14 @@ final class ActivityRecord extends ConfigurationContainer { throw new IllegalStateException("Activity not found in its task"); } if (positionInTask == task.mActivities.size() - 1) { - // It's the topmost activity in the task - should become paused now + // It's the topmost activity in the task - should become resumed now return true; } // Check if activity above is finishing now and this one becomes the topmost in task. final ActivityRecord activityAbove = task.mActivities.get(positionInTask + 1); if (activityAbove.finishing && results == null) { - // We will only allow pausing if activity above wasn't launched for result. Otherwise it - // will cause this activity to resume before getting result. + // We will only allow making active if activity above wasn't launched for result. + // Otherwise it will cause this activity to resume before getting result. return true; } return false; diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 3a754c411684..c97e4e8a9bc0 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -357,6 +357,11 @@ class ActivityStack extends ConfigurationContainer { */ boolean mForceHidden = false; + /** + * Used to keep resumeTopActivityUncheckedLocked() from being entered recursively + */ + boolean mInResumeTopActivity = false; + private boolean mUpdateBoundsDeferred; private boolean mUpdateBoundsDeferredCalled; private boolean mUpdateDisplayedBoundsDeferredCalled; @@ -1732,6 +1737,7 @@ class ActivityStack extends ConfigurationContainer { "Activity paused: token=" + token + ", timeout=" + timeout); final ActivityRecord r = isInStackLocked(token); + if (r != null) { mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); if (mPausingActivity == r) { @@ -2088,8 +2094,7 @@ class ActivityStack extends ConfigurationContainer { boolean aboveTop = top != null; final boolean stackShouldBeVisible = shouldBeVisible(starting); boolean behindFullscreenActivity = !stackShouldBeVisible; - boolean resumeNextActivity = mRootActivityContainer.isTopDisplayFocusedStack(this) - && (isInStackLocked(starting) == null); + boolean resumeNextActivity = isFocusable() && isInStackLocked(starting) == null; final boolean isTopNotPinnedStack = isAttached() && getDisplay().isTopNotPinnedStack(this); for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { @@ -2150,6 +2155,10 @@ class ActivityStack extends ConfigurationContainer { if (r.handleAlreadyVisible()) { resumeNextActivity = false; } + + if (notifyClients) { + r.makeActiveIfNeeded(starting); + } } else { r.makeVisibleIfNeeded(starting, notifyClients); } @@ -2327,7 +2336,7 @@ class ActivityStack extends ConfigurationContainer { r.setVisible(true); } if (r != starting) { - mStackSupervisor.startSpecificActivityLocked(r, andResume, false); + mStackSupervisor.startSpecificActivityLocked(r, andResume, true /* checkConfig */); return true; } } @@ -2505,7 +2514,7 @@ class ActivityStack extends ConfigurationContainer { */ @GuardedBy("mService") boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) { - if (mStackSupervisor.inResumeTopActivity) { + if (mInResumeTopActivity) { // Don't even start recursing. return false; } @@ -2513,7 +2522,7 @@ class ActivityStack extends ConfigurationContainer { boolean result = false; try { // Protect against recursion. - mStackSupervisor.inResumeTopActivity = true; + mInResumeTopActivity = true; result = resumeTopActivityInnerLocked(prev, options); // When resuming the top activity, it may be necessary to pause the top activity (for @@ -2528,7 +2537,7 @@ class ActivityStack extends ConfigurationContainer { checkReadyForSleep(); } } finally { - mStackSupervisor.inResumeTopActivity = false; + mInResumeTopActivity = false; } return result; @@ -2561,7 +2570,7 @@ class ActivityStack extends ConfigurationContainer { // Find the next top-most activity to resume in this stack that is not finishing and is // focusable. If it is not focusable, we will fall into the case below to resume the // top activity in the next focusable task. - final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */); + ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */); final boolean hasRunningActivity = next != null; @@ -2649,6 +2658,12 @@ class ActivityStack extends ConfigurationContainer { if (!mRootActivityContainer.allPausedActivitiesComplete()) { if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE, "resumeTopActivityLocked: Skip resume: some activity pausing."); + + // Adding previous activity to the waiting visible list, or it would be stopped + // before top activity being visible. + if (prev != null && !next.nowVisible) { + mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(prev); + } return false; } @@ -2858,7 +2873,9 @@ class ActivityStack extends ConfigurationContainer { // the screen based on the new activity order. boolean notUpdated = true; - if (isFocusedStackOnDisplay()) { + // Activity should also be visible if set mLaunchTaskBehind to true (see + // ActivityRecord#shouldBeVisibleIgnoringKeyguard()). + if (shouldBeVisible(next)) { // We have special rotation behavior when here is some active activity that // requests specific orientation or Keyguard is locked. Make sure all activity // visibilities are set correctly as well as the transition is updated if needed @@ -4087,6 +4104,12 @@ class ActivityStack extends ConfigurationContainer { mStackSupervisor.mFinishingActivities.add(r); r.resumeKeyDispatchingLocked(); mRootActivityContainer.resumeFocusedStacksTopActivities(); + // If activity was not paused at this point - explicitly pause it to start finishing + // process. Finishing will be completed once it reports pause back. + if (r.isState(RESUMED) && mPausingActivity != null) { + startPausingLocked(false /* userLeaving */, false /* uiSleeping */, next /* resuming */, + false /* dontWait */); + } return r; } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 3a288ca5560d..a83ef34f1cac 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -327,9 +327,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { */ PowerManager.WakeLock mGoingToSleep; - /** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */ - boolean inResumeTopActivity; - /** * Temporary rect used during docked stack resize calculation so we don't need to create a new * object each time. diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index 280709461c98..43c12064a3c1 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -179,7 +179,10 @@ public class ActivityStartController { .setActivityOptions(options.toBundle()) .execute(); mLastHomeActivityStartRecord = tmpOutRecord[0]; - if (mSupervisor.inResumeTopActivity) { + final ActivityDisplay display = + mService.mRootActivityContainer.getActivityDisplay(displayId); + final ActivityStack homeStack = display != null ? display.getHomeStack() : null; + if (homeStack != null && homeStack.mInResumeTopActivity) { // If we are in resume section already, home activity will be initialized, but not // resumed (to avoid recursive resume) and will stay that way until something pokes it // again. We need to schedule another resume. diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 38580bc41f74..b0e581134a95 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -982,7 +982,8 @@ class ActivityStarter { /** Returns true if uid is in a persistent state. */ private boolean isUidPersistentSystemProcess(int uid) { - return (mService.getUidStateLocked(uid) <= ActivityManager.PROCESS_STATE_PERSISTENT_UI); + return (uid == Process.SYSTEM_UID) + || (mService.getUidStateLocked(uid) <= ActivityManager.PROCESS_STATE_PERSISTENT_UI); } private void maybeLogActivityStart(int callingUid, String callingPackage, int realCallingUid, @@ -1632,7 +1633,7 @@ class ActivityStarter { // Also, we don't want to resume activities in a task that currently has an overlay // as the starting activity just needs to be in the visible paused state until the // over is removed. - mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mTargetStack.ensureActivitiesVisibleLocked(mStartActivity, 0, !PRESERVE_WINDOWS); // Go ahead and tell window manager to execute app transition for this activity // since the app transition will not be triggered through the resume channel. mTargetStack.getDisplay().mDisplayContent.executeAppTransition(); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 40063326e76e..bbf115f17969 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 @@ public class DisplayPolicy { } } + 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/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java index 9b7214120aed..e95ac5c43315 100644 --- a/services/core/java/com/android/server/wm/RootActivityContainer.java +++ b/services/core/java/com/android/server/wm/RootActivityContainer.java @@ -1108,28 +1108,41 @@ class RootActivityContainer extends ConfigurationContainer return false; } + boolean result = false; if (targetStack != null && (targetStack.isTopStackOnDisplay() || getTopDisplayFocusedStack() == targetStack)) { - return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions); + result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions); } - // Resume all top activities in focused stacks on all displays. for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + boolean resumedOnDisplay = false; final ActivityDisplay display = mActivityDisplays.get(displayNdx); - final ActivityStack focusedStack = display.getFocusedStack(); - if (focusedStack == null) { - continue; + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + final ActivityRecord topRunningActivity = stack.topRunningActivityLocked(); + if (!stack.isFocusableAndVisible() || topRunningActivity == null) { + continue; + } + if (topRunningActivity.isState(RESUMED)) { + // Kick off any lingering app transitions form the MoveTaskToFront operation. + stack.executeAppTransition(targetOptions); + } else { + resumedOnDisplay |= topRunningActivity.makeActiveIfNeeded(target); + } } - final ActivityRecord r = focusedStack.topRunningActivityLocked(); - if (r == null || !r.isState(RESUMED)) { - focusedStack.resumeTopActivityUncheckedLocked(null, null); - } else if (r.isState(RESUMED)) { - // Kick off any lingering app transitions form the MoveTaskToFront operation. - focusedStack.executeAppTransition(targetOptions); + if (!resumedOnDisplay) { + // In cases when there are no valid activities (e.g. device just booted or launcher + // crashed) it's possible that nothing was resumed on a display. Requesting resume + // of top activity in focused stack explicitly will make sure that at least home + // activity is started and resumed, and no recursion occurs. + final ActivityStack focusedStack = display.getFocusedStack(); + if (focusedStack != null) { + focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions); + } } } - return false; + return result; } void applySleepTokens(boolean applyToStacks) { @@ -1283,16 +1296,21 @@ class RootActivityContainer extends ConfigurationContainer 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); diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index 69dcaf473b12..0529ed128130 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -698,6 +698,14 @@ class TaskRecord extends ConfigurationContainer { return false; } + final boolean toTopOfStack = position == MAX_VALUE; + if (toTopOfStack && toStack.getResumedActivity() != null + && toStack.topRunningActivityLocked() != null) { + // Pause the resumed activity on the target stack while re-parenting task on top of it. + toStack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */, + null /* resuming */, false /* pauseImmediately */); + } + final int toStackWindowingMode = toStack.getWindowingMode(); final ActivityRecord topActivity = getTopActivity(); 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 e99dd4f1cbae..bbecc6359a40 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.SQLiteDatabase; 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 @@ public class IpMemoryStoreDatabase { 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 @@ public class IpMemoryStoreDatabase { 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 f068c3ac16e2..1fe2328f1cdb 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/IpConfigurationParcelableUtil.java b/services/net/java/android/net/shared/IpConfigurationParcelableUtil.java index 2c368c81523e..00073503886a 100644 --- a/services/net/java/android/net/shared/IpConfigurationParcelableUtil.java +++ b/services/net/java/android/net/shared/IpConfigurationParcelableUtil.java @@ -73,7 +73,7 @@ public final class IpConfigurationParcelableUtil { public static DhcpResultsParcelable toStableParcelable(@Nullable DhcpResults results) { if (results == null) return null; final DhcpResultsParcelable p = new DhcpResultsParcelable(); - p.baseConfiguration = toStableParcelable((StaticIpConfiguration) results); + p.baseConfiguration = toStableParcelable(results.toStaticIpConfiguration()); p.leaseDuration = results.leaseDuration; p.mtu = results.mtu; p.serverAddress = parcelAddress(results.serverAddress); diff --git a/services/net/java/android/net/shared/NetdService.java b/services/net/java/android/net/shared/NetdService.java index be0f5f2d4f34..f5ae72587294 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 @@ import android.util.Log; */ 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 @@ public class NetdService { // 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/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java index 410ab8732a08..e658d179f4f3 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 @@ public class NotificationAdjustmentExtractorTest extends UiServiceTestCase { 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/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 62229235a026..9c6ab0ab9aa9 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -3410,6 +3410,77 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testHideAndUnhideNotificationsOnDistractingPackageBroadcast() { + // Post 2 notifications from 2 packages + NotificationRecord pkgA = new NotificationRecord(mContext, + generateSbn("a", 1000, 9, 0), mTestNotificationChannel); + mService.addNotification(pkgA); + NotificationRecord pkgB = new NotificationRecord(mContext, + generateSbn("b", 1001, 9, 0), mTestNotificationChannel); + mService.addNotification(pkgB); + + // on broadcast, hide one of the packages + mService.simulatePackageDistractionBroadcast( + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a"}); + ArgumentCaptor<List<NotificationRecord>> captorHide = ArgumentCaptor.forClass(List.class); + verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture()); + assertEquals(1, captorHide.getValue().size()); + assertEquals("a", captorHide.getValue().get(0).sbn.getPackageName()); + + // on broadcast, unhide the package + mService.simulatePackageDistractionBroadcast( + PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a"}); + ArgumentCaptor<List<NotificationRecord>> captorUnhide = ArgumentCaptor.forClass(List.class); + verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture()); + assertEquals(1, captorUnhide.getValue().size()); + assertEquals("a", captorUnhide.getValue().get(0).sbn.getPackageName()); + } + + @Test + public void testHideAndUnhideNotificationsOnDistractingPackageBroadcast_multiPkg() { + // Post 2 notifications from 2 packages + NotificationRecord pkgA = new NotificationRecord(mContext, + generateSbn("a", 1000, 9, 0), mTestNotificationChannel); + mService.addNotification(pkgA); + NotificationRecord pkgB = new NotificationRecord(mContext, + generateSbn("b", 1001, 9, 0), mTestNotificationChannel); + mService.addNotification(pkgB); + + // on broadcast, hide one of the packages + mService.simulatePackageDistractionBroadcast( + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a", "b"}); + ArgumentCaptor<List<NotificationRecord>> captorHide = ArgumentCaptor.forClass(List.class); + verify(mListeners, times(2)).notifyHiddenLocked(captorHide.capture()); + assertEquals(2, captorHide.getValue().size()); + assertEquals("a", captorHide.getValue().get(0).sbn.getPackageName()); + assertEquals("b", captorHide.getValue().get(1).sbn.getPackageName()); + + // on broadcast, unhide the package + mService.simulatePackageDistractionBroadcast( + PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a", "b"}); + ArgumentCaptor<List<NotificationRecord>> captorUnhide = ArgumentCaptor.forClass(List.class); + verify(mListeners, times(2)).notifyUnhiddenLocked(captorUnhide.capture()); + assertEquals(2, captorUnhide.getValue().size()); + assertEquals("a", captorUnhide.getValue().get(0).sbn.getPackageName()); + assertEquals("b", captorUnhide.getValue().get(1).sbn.getPackageName()); + } + + @Test + public void testNoNotificationsHiddenOnDistractingPackageBroadcast() { + // post notification from this package + final NotificationRecord notif1 = generateNotificationRecord( + mTestNotificationChannel, 1, null, true); + mService.addNotification(notif1); + + // on broadcast, nothing is hidden since no notifications are of package "test_package" + mService.simulatePackageDistractionBroadcast( + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"test_package"}); + ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class); + verify(mListeners, times(1)).notifyHiddenLocked(captor.capture()); + assertEquals(0, captor.getValue().size()); + } + + @Test public void testCanUseManagedServicesLowRamNoWatchNullPkg() { when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false); when(mActivityManager.isLowRamDevice()).thenReturn(true); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 8be63fc43adb..319ffed3778c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -31,6 +31,7 @@ import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT; import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING; import static com.android.server.wm.ActivityStack.ActivityState.PAUSING; +import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; import static com.android.server.wm.ActivityStack.ActivityState.STOPPED; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING; @@ -75,6 +76,9 @@ public class ActivityRecordTests extends ActivityTestsBase { mStack = (TestActivityStack) new StackBuilder(mRootActivityContainer).build(); mTask = mStack.getChildAt(0); mActivity = mTask.getTopActivity(); + + doReturn(false).when(mService).isBooting(); + doReturn(true).when(mService).isBooted(); } @Test @@ -117,22 +121,23 @@ public class ActivityRecordTests extends ActivityTestsBase { mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped"); - // The activity is in the focused stack so it should not move to paused. + // The activity is in the focused stack so it should be resumed. mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); - assertTrue(mActivity.isState(STOPPED)); + assertTrue(mActivity.isState(RESUMED)); assertFalse(pauseFound.value); - // Clear focused stack - final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); - when(display.getFocusedStack()).thenReturn(null); + // Make the activity non focusable + mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped"); + doReturn(false).when(mActivity).isFocusable(); - // In the unfocused stack, the activity should move to paused. + // If the activity is not focusable, it should move to paused. mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); assertTrue(mActivity.isState(PAUSING)); assertTrue(pauseFound.value); // Make sure that the state does not change for current non-stopping states. mActivity.setState(INITIALIZING, "testPausingWhenVisibleFromStopped"); + doReturn(true).when(mActivity).isFocusable(); mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index 68df87e3e27d..ea8f33f0c630 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -55,6 +55,7 @@ import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; import android.os.Handler; import android.os.Looper; +import android.os.PowerManager; import android.os.Process; import android.os.UserHandle; import android.service.voice.IVoiceInteractionSession; @@ -425,6 +426,7 @@ class ActivityTestsBase { doReturn(mock(IPackageManager.class)).when(this).getPackageManager(); // allow background activity starts by default doReturn(true).when(this).isBackgroundActivityStartsEnabled(); + doNothing().when(this).updateCpuStats(); } void setup(IntentFirewall intentFirewall, PendingIntentController intentController, @@ -580,6 +582,8 @@ class ActivityTestsBase { doNothing().when(this).acquireLaunchWakelock(); doReturn(mKeyguardController).when(this).getKeyguardController(); + mLaunchingActivity = mock(PowerManager.WakeLock.class); + initialize(); } diff --git a/telephony/java/android/telephony/IFinancialSmsCallback.aidl b/telephony/java/android/telephony/IFinancialSmsCallback.aidl new file mode 100644 index 000000000000..aa88615c15cf --- /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/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index d777bf123b67..fae7920d1ab8 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -16,6 +16,9 @@ package android.telephony; +import android.annotation.CallbackExecutor; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressAutoDoc; import android.annotation.SystemApi; @@ -30,8 +33,10 @@ import android.content.ActivityNotFoundException; 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; @@ -49,6 +54,7 @@ import java.util.ArrayList; 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 @@ -2144,6 +2150,116 @@ public final class SmsManager { } } + /** 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. + * @hide + */ + public static final String REGEX_PREFIX_DELIMITER = ","; + /** + * @see #createAppSpecificSmsTokenWithPackageInfo(). + * The success status to be added into the intent to be sent to the calling package. + * @hide + */ + public static final int RESULT_STATUS_SUCCESS = 0; + /** + * @see #createAppSpecificSmsTokenWithPackageInfo(). + * The timeout status to be added into the intent to be sent to the calling package. + * @hide + */ + public static final int RESULT_STATUS_TIMEOUT = 1; + /** + * @see #createAppSpecificSmsTokenWithPackageInfo(). + * Intent extra key of the retrieved SMS message as a {@code String}. + * @hide + */ + public static final String EXTRA_SMS_MESSAGE = "android.telephony.extra.SMS_MESSAGE"; + /** + * @see #createAppSpecificSmsTokenWithPackageInfo(). + * Intent extra key of SMS retriever status, which indicates whether the request for the + * coming SMS message is SUCCESS or TIMEOUT + * @hide + */ + public static final String EXTRA_STATUS = "android.telephony.extra.STATUS"; + /** + * @see #createAppSpecificSmsTokenWithPackageInfo(). + * [Optional] Intent extra key of the retrieved Sim card subscription Id if any. {@code int} + * @hide + */ + public static final String EXTRA_SIM_SUBSCRIPTION_ID = + "android.telephony.extra.SIM_SUBSCRIPTION_ID"; + + /** + * Create a single use app specific incoming SMS request for the calling package. + * + * This method returns a token that if included in a subsequent incoming SMS message, and the + * SMS message has a prefix from the given prefixes list, the provided {@code intent} will be + * sent with the SMS data to the calling package. + * + * The token is only good for one use within a reasonable amount of time. After an SMS has been + * received containing the token all subsequent SMS messages with the token will be routed as + * normal. + * + * An app can only have one request at a time, if the app already has a request pending it will + * be replaced with a new request. + * + * @param prefixes this is a list of prefixes string separated by REGEX_PREFIX_DELIMITER. The + * matching SMS message should have at least one of the prefixes in the beginning of the + * message. + * @param intent this intent is sent when the matching SMS message is received. + * @return Token to include in an SMS message. + */ + @Nullable + public String createAppSpecificSmsTokenWithPackageInfo( + @Nullable String prefixes, @NonNull PendingIntent intent) { + try { + ISms iccSms = getISmsServiceOrThrow(); + return iccSms.createAppSpecificSmsTokenWithPackageInfo(getSubscriptionId(), + ActivityThread.currentPackageName(), prefixes, intent); + + } catch (RemoteException ex) { + ex.rethrowFromSystemServer(); + return null; + } + } + /** * Filters a bundle to only contain MMS config variables. * diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index e710e0e02c46..98fc7251e9a8 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -6848,13 +6848,13 @@ public class TelephonyManager { /** * Values used to return status for hasCarrierPrivileges call. */ - /** @hide */ @SystemApi + /** @hide */ @SystemApi @TestApi public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; - /** @hide */ @SystemApi + /** @hide */ @SystemApi @TestApi public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; - /** @hide */ @SystemApi + /** @hide */ @SystemApi @TestApi public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; - /** @hide */ @SystemApi + /** @hide */ @SystemApi @TestApi public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; /** @@ -7056,6 +7056,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @TestApi @SuppressLint("Doclava125") public int checkCarrierPrivilegesForPackage(String pkgName) { try { diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl index a4eb424ab66e..b51eda3f1b34 100644 --- a/telephony/java/com/android/internal/telephony/ISms.aidl +++ b/telephony/java/com/android/internal/telephony/ISms.aidl @@ -18,6 +18,8 @@ package com.android.internal.telephony; 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. @@ -560,4 +562,30 @@ interface ISms { * @param intent PendingIntent to be sent when an SMS is received containing the token. */ String createAppSpecificSmsToken(int subId, String callingPkg, in PendingIntent intent); + + /** + * Create an app-only incoming SMS request for the calling package. + * + * If an incoming text contains the token returned by this method the provided + * <code>PendingIntent</code> will be sent containing the SMS data. + * + * @param subId the SIM id. + * @param callingPkg the package name of the calling app. + * @param prefixes the caller provided prefixes + * @param intent PendingIntent to be sent when a SMS is received containing the token and one + * of the prefixes + */ + 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 1cdf44d897b2..12c5c308739a 100644 --- a/telephony/java/com/android/internal/telephony/ISmsImplBase.java +++ b/telephony/java/com/android/internal/telephony/ISmsImplBase.java @@ -18,6 +18,8 @@ package com.android.internal.telephony; import android.app.PendingIntent; import android.net.Uri; +import android.os.Bundle; +import android.telephony.IFinancialSmsCallback; import java.util.List; @@ -188,4 +190,16 @@ public class ISmsImplBase extends ISms.Stub { public String createAppSpecificSmsToken(int subId, String callingPkg, PendingIntent intent) { throw new UnsupportedOperationException(); } + + @Override + public String createAppSpecificSmsTokenWithPackageInfo( + 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/test-mock/src/android/test/mock/MockCursor.java b/test-mock/src/android/test/mock/MockCursor.java index 576f24ad6384..f69db2c84111 100644 --- a/test-mock/src/android/test/mock/MockCursor.java +++ b/test-mock/src/android/test/mock/MockCursor.java @@ -24,6 +24,8 @@ import android.database.DataSetObserver; import android.net.Uri; import android.os.Bundle; +import java.util.List; + /** * A mock {@link android.database.Cursor} class that isolates the test code from real * Cursor implementation. @@ -226,11 +228,21 @@ public class MockCursor implements Cursor { } @Override + public void setNotificationUris(ContentResolver cr, List<Uri> uris) { + throw new UnsupportedOperationException("unimplemented mock method"); + } + + @Override public Uri getNotificationUri() { throw new UnsupportedOperationException("unimplemented mock method"); } @Override + public List<Uri> getNotificationUris() { + throw new UnsupportedOperationException("unimplemented mock method"); + } + + @Override public void unregisterContentObserver(ContentObserver observer) { throw new UnsupportedOperationException("unimplemented mock method"); } diff --git a/tests/DexLoggerIntegrationTests/Android.mk b/tests/DexLoggerIntegrationTests/Android.mk index 979d13ac9405..ee02a72b6819 100644 --- a/tests/DexLoggerIntegrationTests/Android.mk +++ b/tests/DexLoggerIntegrationTests/Android.mk @@ -35,7 +35,6 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_MODULE := DexLoggerNativeTestLibrary -LOCAL_MULTILIB := first LOCAL_SRC_FILES := src/cpp/com_android_dcl_Jni.cpp LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) @@ -44,8 +43,6 @@ LOCAL_NDK_STL_VARIANT := c++_static include $(BUILD_SHARED_LIBRARY) -dexloggertest_so := $(LOCAL_BUILT_MODULE) - # And a standalone native executable that we can exec. include $(CLEAR_VARS) @@ -73,11 +70,15 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-test \ truth-prebuilt \ +# Include both versions of the .so if we have 2 arch +LOCAL_MULTILIB := both +LOCAL_JNI_SHARED_LIBRARIES := \ + DexLoggerNativeTestLibrary \ + # This gets us the javalib.jar built by DexLoggerTestLibrary above as well as the various # native binaries. LOCAL_JAVA_RESOURCE_FILES := \ $(dexloggertest_jar) \ - $(dexloggertest_so) \ - $(dexloggertest_executable) + $(dexloggertest_executable) \ include $(BUILD_PACKAGE) diff --git a/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java index d68769b378b9..e92cc56322eb 100644 --- a/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java +++ b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertWithMessage; import android.app.UiAutomation; import android.content.Context; +import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.SystemClock; import android.support.test.InstrumentationRegistry; @@ -147,7 +148,7 @@ public final class DexLoggerIntegrationTests { String expectedNameHash = "996223BAD4B4FE75C57A3DEC61DB9C0B38E0A7AD479FC95F33494F4BC55A0F0E"; String expectedContentHash = - copyAndHashResource("/DexLoggerNativeTestLibrary.so", privateCopyFile); + copyAndHashResource(libraryPath("DexLoggerNativeTestLibrary.so"), privateCopyFile); System.load(privateCopyFile.toString()); @@ -170,7 +171,7 @@ public final class DexLoggerIntegrationTests { String expectedNameHash = "8C39990C560B4F36F83E208E279F678746FE23A790E4C50F92686584EA2041CA"; String expectedContentHash = - copyAndHashResource("/DexLoggerNativeTestLibrary.so", privateCopyFile); + copyAndHashResource(libraryPath("DexLoggerNativeTestLibrary.so"), privateCopyFile); System.load(privateCopyFile.toString()); @@ -307,6 +308,12 @@ public final class DexLoggerIntegrationTests { return new File(sContext.getDir("dcl", Context.MODE_PRIVATE), name); } + private String libraryPath(final String libraryName) { + // This may be deprecated. but it tells us the ABI of this process which is exactly what we + // want. + return "/lib/" + Build.CPU_ABI + "/" + libraryName; + } + private static String copyAndHashResource(String resourcePath, File copyTo) throws Exception { MessageDigest hasher = MessageDigest.getInstance("SHA-256"); diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index ec07037b3a8f..86af6422dad3 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.os.test.TestLooper; 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 @@ import java.util.concurrent.TimeUnit; 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 @@ public class PackageWatchdogTest { 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 @@ public class PackageWatchdogTest { 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_C (not observed) above the threshold + for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { + watchdog.onPackageFailure(new String[]{APP_C}); + } + + // Run handler so package failures are dispatched to observers + mTestLooper.dispatchAll(); - // Then fail APP_A and APP_B above the threshold + // Verify that observers are not notified + assertEquals(0, observer1.mFailedPackages.size()); + assertEquals(0, observer2.mFailedPackages.size()); + } + + /** + * Test package failure and notifies only least impact observers. + */ + @Test + public void testPackageFailureNotifyAllDifferentImpacts() throws Exception { + PackageWatchdog watchdog = createWatchdog(); + 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}); + watchdog.onPackageFailure(new String[]{APP_A, APP_B, APP_C, APP_D}); } - // 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 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 notifies only one observer because it handles the failure + * 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 testPackageFailureNotifyOne() throws Exception { + public void testPackageFailureNotifyLeastSuccessively() throws Exception { PackageWatchdog watchdog = createWatchdog(); - TestObserver observer1 = new TestObserver(OBSERVER_NAME_1, true /* shouldHandle */); - TestObserver observer2 = new TestObserver(OBSERVER_NAME_2, true /* shouldHandle */); + 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 @@ public class PackageWatchdogTest { 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 @@ public class PackageWatchdogTest { 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 int onHealthCheckFailed(String packageName) { + return mImpact; } - public boolean onHealthCheckFailed(String packageName) { + 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/RollbackBroadcastReceiver.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java index 030641bf0895..e10f866c899f 100644 --- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java +++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java @@ -29,7 +29,7 @@ import java.util.concurrent.TimeUnit; /** * A broadcast receiver that can be used to get - * ACTION_PACKAGE_ROLLBACK_EXECUTED broadcasts. + * ACTION_ROLLBACK_COMMITTED broadcasts. */ class RollbackBroadcastReceiver extends BroadcastReceiver { @@ -43,7 +43,7 @@ class RollbackBroadcastReceiver extends BroadcastReceiver { */ RollbackBroadcastReceiver() { IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED); + filter.addAction(Intent.ACTION_ROLLBACK_COMMITTED); InstrumentationRegistry.getContext().registerReceiver(this, filter); } diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java index 13ac4f09dd86..0493ef8ab062 100644 --- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java +++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java @@ -31,11 +31,8 @@ import android.support.test.InstrumentationRegistry; import android.util.Log; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.junit.Ignore; @@ -43,6 +40,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -86,8 +84,8 @@ public class RollbackTest { Manifest.permission.DELETE_PACKAGES, Manifest.permission.MANAGE_ROLLBACKS); - // Register a broadcast receiver for notification when the rollback is - // done executing. + // Register a broadcast receiver for notification when the + // rollback has been committed. RollbackBroadcastReceiver broadcastReceiver = new RollbackBroadcastReceiver(); RollbackManager rm = RollbackTestUtils.getRollbackManager(); @@ -99,12 +97,11 @@ public class RollbackTest { // uninstalled and when rollback manager deletes the rollback. Fix it // so that's not the case! for (int i = 0; i < 5; ++i) { - for (RollbackInfo info : rm.getRecentlyExecutedRollbacks()) { - if (TEST_APP_A.equals(info.targetPackage.getPackageName())) { - Log.i(TAG, "Sleeping 1 second to wait for uninstall to take effect."); - Thread.sleep(1000); - break; - } + RollbackInfo rollback = getUniqueRollbackInfoForPackage( + rm.getRecentlyCommittedRollbacks(), TEST_APP_A); + if (rollback != null) { + Log.i(TAG, "Sleeping 1 second to wait for uninstall to take effect."); + Thread.sleep(1000); } } @@ -113,13 +110,11 @@ public class RollbackTest { // between when the app is uninstalled and when the previously // available rollback, if any, is removed. Thread.sleep(1000); - assertNull(rm.getAvailableRollback(TEST_APP_A)); - assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A)); + assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A)); - // There should be no recently executed rollbacks for this package. - for (RollbackInfo info : rm.getRecentlyExecutedRollbacks()) { - assertNotEquals(TEST_APP_A, info.targetPackage.getPackageName()); - } + // There should be no recently committed rollbacks for this package. + assertNull(getUniqueRollbackInfoForPackage( + rm.getRecentlyCommittedRollbacks(), TEST_APP_A)); // Install v1 of the app (without rollbacks enabled). RollbackTestUtils.install("RollbackTestAppAv1.apk", false); @@ -134,10 +129,9 @@ public class RollbackTest { // between when the app is installed and when the rollback // is made available. Thread.sleep(1000); - assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A)); - RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A); - assertNotNull(rollback); - assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.targetPackage); + RollbackInfo rollback = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_A); + assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback); // We should not have received any rollback requests yet. // TODO: Possibly flaky if, by chance, some other app on device @@ -145,7 +139,7 @@ public class RollbackTest { 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. @@ -156,15 +150,9 @@ public class RollbackTest { assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS)); // Verify the recent rollback has been recorded. - rollback = null; - for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) { - if (TEST_APP_A.equals(r.targetPackage.getPackageName())) { - assertNull(rollback); - rollback = r; - } - } - assertNotNull(rollback); - assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.targetPackage); + rollback = getUniqueRollbackInfoForPackage( + rm.getRecentlyCommittedRollbacks(), TEST_APP_A); + assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback); broadcastReceiver.unregister(); context.unregisterReceiver(enableRollbackReceiver); @@ -202,31 +190,28 @@ public class RollbackTest { // is made available. Thread.sleep(1000); - assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A)); - RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A); - assertNotNull(rollbackA); - assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA.targetPackage); + RollbackInfo rollbackA = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_A); + assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA); - assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B)); - RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B); - assertNotNull(rollbackB); - assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB.targetPackage); + RollbackInfo rollbackB = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_B); + assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB); // Reload the persisted data. rm.reloadPersistedData(); // The apps should still be available for rollback. - rollbackA = rm.getAvailableRollback(TEST_APP_A); - assertNotNull(rollbackA); - assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA.targetPackage); + rollbackA = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_A); + assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA); - assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B)); - rollbackB = rm.getAvailableRollback(TEST_APP_B); - assertNotNull(rollbackB); - assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB.targetPackage); + rollbackB = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_B); + 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 { @@ -264,31 +249,26 @@ public class RollbackTest { // is made available. Thread.sleep(1000); - assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A)); - RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A); - assertNotNull(rollbackA); - assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA.targetPackage); + RollbackInfo rollbackA = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_A); + assertRollbackInfoForAandB(rollbackA); - assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B)); - RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B); - assertNotNull(rollbackB); - assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB.targetPackage); + RollbackInfo rollbackB = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_B); + assertRollbackInfoForAandB(rollbackB); // Reload the persisted data. rm.reloadPersistedData(); // The apps should still be available for rollback. - rollbackA = rm.getAvailableRollback(TEST_APP_A); - assertNotNull(rollbackA); - assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA.targetPackage); + rollbackA = getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A); + assertRollbackInfoForAandB(rollbackA); - assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B)); - rollbackB = rm.getAvailableRollback(TEST_APP_B); - assertNotNull(rollbackB); - assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB.targetPackage); + rollbackB = getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_B); + 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 { @@ -297,10 +277,10 @@ public class RollbackTest { } /** - * Test that recently executed rollback data is properly persisted. + * Test that recently committed rollback data is properly persisted. */ @Test - public void testRecentlyExecutedRollbackPersistence() throws Exception { + public void testRecentlyCommittedRollbackPersistence() throws Exception { try { RollbackTestUtils.adoptShellPermissionIdentity( Manifest.permission.INSTALL_PACKAGES, @@ -319,37 +299,27 @@ public class RollbackTest { // between when the app is installed and when the rollback // is made available. Thread.sleep(1000); - assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A)); - RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A); + RollbackInfo rollback = getUniqueRollbackInfoForPackage( + 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. - rollback = null; - for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) { - if (TEST_APP_A.equals(r.targetPackage.getPackageName())) { - assertNull(rollback); - rollback = r; - } - } + rollback = getUniqueRollbackInfoForPackage( + rm.getRecentlyCommittedRollbacks(), TEST_APP_A); assertNotNull(rollback); - assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.targetPackage); + assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback); // Reload the persisted data. rm.reloadPersistedData(); // Verify the recent rollback is still recorded. - rollback = null; - for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) { - if (TEST_APP_A.equals(r.targetPackage.getPackageName())) { - assertNull(rollback); - rollback = r; - } - } + rollback = getUniqueRollbackInfoForPackage( + rm.getRecentlyCommittedRollbacks(), TEST_APP_A); assertNotNull(rollback); - assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.targetPackage); + assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback); } finally { RollbackTestUtils.dropShellPermissionIdentity(); } @@ -378,17 +348,16 @@ public class RollbackTest { // between when the app is installed and when the rollback // is made available. Thread.sleep(1000); - assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A)); - RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A); - assertNotNull(rollback); - assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.targetPackage); + RollbackInfo rollback = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_A); + assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback); // Expire the rollback. rm.expireRollbackForPackage(TEST_APP_A); // The rollback should no longer be available. - assertNull(rm.getAvailableRollback(TEST_APP_A)); - assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A)); + assertNull(getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_A)); } finally { RollbackTestUtils.dropShellPermissionIdentity(); } @@ -459,8 +428,9 @@ public class RollbackTest { processUserData(TEST_APP_A); RollbackManager rm = RollbackTestUtils.getRollbackManager(); - RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A); - RollbackTestUtils.rollback(rollback); + RollbackInfo rollback = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_A); + RollbackTestUtils.rollback(rollback.getRollbackId()); processUserData(TEST_APP_A); } finally { RollbackTestUtils.dropShellPermissionIdentity(); @@ -469,12 +439,12 @@ public class RollbackTest { /** * Test restrictions on rollback broadcast sender. - * A random app should not be able to send a PACKAGE_ROLLBACK_EXECUTED broadcast. + * A random app should not be able to send a ROLLBACK_COMMITTED broadcast. */ @Test public void testRollbackBroadcastRestrictions() throws Exception { RollbackBroadcastReceiver broadcastReceiver = new RollbackBroadcastReceiver(); - Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED); + Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED); try { InstrumentationRegistry.getContext().sendBroadcast(broadcast); fail("Succeeded in sending restricted broadcast from app context."); @@ -516,21 +486,21 @@ public class RollbackTest { assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); // Both test apps should now be available for rollback, and the - // targetPackage returned for rollback should be correct. + // RollbackInfo returned for the rollbacks should be correct. // TODO: See if there is a way to remove this race condition // between when the app is installed and when the rollback // is made available. Thread.sleep(1000); - RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A); - assertNotNull(rollbackA); - assertEquals(TEST_APP_A, rollbackA.targetPackage.getPackageName()); + RollbackInfo rollbackA = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_A); + assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA); - RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B); - assertNotNull(rollbackB); - assertEquals(TEST_APP_B, rollbackB.targetPackage.getPackageName()); + RollbackInfo rollbackB = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_B); + 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)); @@ -539,7 +509,7 @@ public class RollbackTest { 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 { @@ -558,21 +528,14 @@ public class RollbackTest { RollbackManager rm = RollbackTestUtils.getRollbackManager(); try { - rm.getAvailableRollback(TEST_APP_A); - fail("expected SecurityException"); - } catch (SecurityException e) { - // Expected. - } - - try { - rm.getPackagesWithAvailableRollbacks(); + rm.getAvailableRollbacks(); fail("expected SecurityException"); } catch (SecurityException e) { // Expected. } try { - rm.getRecentlyExecutedRollbacks(); + rm.getRecentlyCommittedRollbacks(); fail("expected SecurityException"); } catch (SecurityException e) { // Expected. @@ -581,7 +544,7 @@ public class RollbackTest { try { // TODO: What if the implementation checks arguments for non-null // first? Then this test isn't valid. - rm.executeRollback(null, null); + rm.commitRollback(0, null); fail("expected SecurityException"); } catch (SecurityException e) { // Expected. @@ -629,27 +592,26 @@ public class RollbackTest { assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); // TEST_APP_A should now be available for rollback. - assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A)); - RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A); - assertNotNull(rollback); - - // TODO: Test the dependent apps for rollback are correct once we - // support that in the RollbackInfo API. + RollbackInfo rollback = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_A); + assertRollbackInfoForAandB(rollback); // 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)); - // We should not see a recent rollback listed for TEST_APP_B - for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) { - assertNotEquals(TEST_APP_B, r.targetPackage.getPackageName()); - } + // We should see recent rollbacks listed for both A and B. + Thread.sleep(1000); + RollbackInfo rollbackA = getUniqueRollbackInfoForPackage( + rm.getRecentlyCommittedRollbacks(), TEST_APP_A); - // TODO: Test the listed dependent apps for the recently executed - // rollback are correct once we support that in the RollbackInfo - // API. + RollbackInfo rollbackB = getUniqueRollbackInfoForPackage( + rm.getRecentlyCommittedRollbacks(), TEST_APP_B); + assertRollbackInfoForAandB(rollbackB); + + assertEquals(rollbackA.getRollbackId(), rollbackB.getRollbackId()); } finally { RollbackTestUtils.dropShellPermissionIdentity(); } @@ -697,13 +659,13 @@ public class RollbackTest { // between when the app is installed and when the rollback // is made available. Thread.sleep(1000); - RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A); - assertNotNull(rollbackA); - assertEquals(TEST_APP_A, rollbackA.targetPackage.getPackageName()); + RollbackInfo rollbackA = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_A); + assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA); - RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B); - assertNotNull(rollbackB); - assertEquals(TEST_APP_B, rollbackB.targetPackage.getPackageName()); + RollbackInfo rollbackB = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_B); + assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB); // Start apps PackageWatchdog#TRIGGER_FAILURE_COUNT times so TEST_APP_A crashes for (int i = 0; i < 5; i++) { @@ -724,4 +686,47 @@ public class RollbackTest { RollbackTestUtils.dropShellPermissionIdentity(); } } + + // Helper function to test the value of a RollbackInfo with single package + private void assertRollbackInfoEquals(String packageName, + long versionRolledBackFrom, long versionRolledBackTo, + RollbackInfo info) { + assertNotNull(info); + assertEquals(1, info.getPackages().size()); + assertPackageRollbackInfoEquals(packageName, versionRolledBackFrom, versionRolledBackTo, + info.getPackages().get(0)); + } + + // Helper function to test that the given rollback info is a rollback for + // the atomic set {A2, B2} -> {A1, B1}. + private void assertRollbackInfoForAandB(RollbackInfo rollback) { + assertNotNull(rollback); + assertEquals(2, rollback.getPackages().size()); + if (TEST_APP_A.equals(rollback.getPackages().get(0).getPackageName())) { + assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.getPackages().get(0)); + assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollback.getPackages().get(1)); + } else { + assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollback.getPackages().get(0)); + assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.getPackages().get(1)); + } + } + + // Helper function to return the RollbackInfo with a given package in the + // list of rollbacks. Throws an assertion failure if there is more than + // one such rollback info. Returns null if there are no such rollback + // infos. + private RollbackInfo getUniqueRollbackInfoForPackage(List<RollbackInfo> rollbacks, + String packageName) { + RollbackInfo found = null; + for (RollbackInfo rollback : rollbacks) { + for (PackageRollbackInfo info : rollback.getPackages()) { + if (packageName.equals(info.getPackageName())) { + assertNull(found); + found = rollback; + break; + } + } + } + return found; + } } diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java index edb13556b8fc..14786577c814 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.Intent; 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; @@ -90,12 +89,12 @@ class RollbackTestUtils { } /** - * Execute the given rollback. + * 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.executeRollback(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 3452819835f5..ba6e0f299057 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 @@ public class NetworkUtilsTest { 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/StaticIpConfigurationTest.java b/tests/net/java/android/net/StaticIpConfigurationTest.java index 5bb573455358..2b5ad378e0ae 100644 --- a/tests/net/java/android/net/StaticIpConfigurationTest.java +++ b/tests/net/java/android/net/StaticIpConfigurationTest.java @@ -26,13 +26,13 @@ import android.os.Parcel; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.net.InetAddress; import java.util.HashSet; import java.util.Objects; -import org.junit.Test; -import org.junit.runner.RunWith; - @RunWith(AndroidJUnit4.class) @SmallTest public class StaticIpConfigurationTest { @@ -203,7 +203,7 @@ public class StaticIpConfigurationTest { try { s.writeToParcel(p, 0); p.setDataPosition(0); - s2 = StaticIpConfiguration.CREATOR.createFromParcel(p); + s2 = StaticIpConfiguration.readFromParcel(p); } finally { p.recycle(); } diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/tests/net/java/android/net/ip/IpServerTest.java index 80aac047a723..f7542a7b4bfa 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.TETHERING_WIFI; 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 000000000000..6da851400af1 --- /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/tests/net/java/android/net/shared/IpConfigurationParcelableUtilTest.java b/tests/net/java/android/net/shared/IpConfigurationParcelableUtilTest.java index 14df392cbe07..fb4d43c367db 100644 --- a/tests/net/java/android/net/shared/IpConfigurationParcelableUtilTest.java +++ b/tests/net/java/android/net/shared/IpConfigurationParcelableUtilTest.java @@ -62,7 +62,7 @@ public class IpConfigurationParcelableUtilTest { mDhcpResults.leaseDuration = 3600; mDhcpResults.mtu = 1450; // Any added DhcpResults field must be included in equals() to be tested properly - assertFieldCountEquals(4, DhcpResults.class); + assertFieldCountEquals(8, DhcpResults.class); } @Test diff --git a/tools/bit/aapt.cpp b/tools/bit/aapt.cpp index 961b47cdfecd..cee0cd52a546 100644 --- a/tools/bit/aapt.cpp +++ b/tools/bit/aapt.cpp @@ -159,10 +159,11 @@ int inspect_apk(Apk* apk, const string& filename) { // Load the manifest xml - Command cmd("aapt"); + Command cmd("aapt2"); cmd.AddArg("dump"); cmd.AddArg("xmltree"); cmd.AddArg(filename); + cmd.AddArg("--file"); cmd.AddArg("AndroidManifest.xml"); int err; @@ -217,11 +218,11 @@ inspect_apk(Apk* apk, const string& filename) if (current != NULL) { Attribute attr; string str = match[2]; - size_t colon = str.find(':'); + size_t colon = str.rfind(':'); if (colon == string::npos) { attr.name = str; } else { - attr.ns = scope->namespaces[string(str, 0, colon)]; + attr.ns.assign(str, 0, colon); attr.name.assign(str, colon+1, string::npos); } attr.value = match[3]; diff --git a/tools/bit/main.cpp b/tools/bit/main.cpp index a71cea1c44f9..860094aef1f4 100644 --- a/tools/bit/main.cpp +++ b/tools/bit/main.cpp @@ -623,12 +623,13 @@ run_phases(vector<Target*> targets, const Options& options) const string buildProduct = get_required_env("TARGET_PRODUCT", false); const string buildVariant = get_required_env("TARGET_BUILD_VARIANT", false); const string buildType = get_required_env("TARGET_BUILD_TYPE", false); - + const string buildOut = get_out_dir(); chdir_or_exit(buildTop.c_str()); - const string buildDevice = get_build_var("TARGET_DEVICE", false); - const string buildId = get_build_var("BUILD_ID", false); - const string buildOut = get_out_dir(); + BuildVars buildVars(buildOut, buildProduct, buildVariant, buildType); + + const string buildDevice = buildVars.GetBuildVar("TARGET_DEVICE", false); + const string buildId = buildVars.GetBuildVar("BUILD_ID", false); // Get the modules for the targets map<string,Module> modules; @@ -661,6 +662,7 @@ run_phases(vector<Target*> targets, const Options& options) string dataPath = buildOut + "/target/product/" + buildDevice + "/data/"; bool syncSystem = false; bool alwaysSyncSystem = false; + vector<string> systemFiles; vector<InstallApk> installApks; for (size_t i=0; i<targets.size(); i++) { Target* target = targets[i]; @@ -670,6 +672,7 @@ run_phases(vector<Target*> targets, const Options& options) // System partition if (starts_with(file, systemPath)) { syncSystem = true; + systemFiles.push_back(file); if (!target->build) { // If a system partition target didn't get built then // it won't change we will always need to do adb sync @@ -692,6 +695,19 @@ run_phases(vector<Target*> targets, const Options& options) get_directory_contents(systemPath, &systemFilesBefore); } + if (systemFiles.size() > 0){ + print_info("System files:"); + for (size_t i=0; i<systemFiles.size(); i++) { + printf(" %s\n", systemFiles[i].c_str()); + } + } + if (installApks.size() > 0){ + print_info("APKs to install:"); + for (size_t i=0; i<installApks.size(); i++) { + printf(" %s\n", installApks[i].file.filename.c_str()); + } + } + // // Build // @@ -798,7 +814,8 @@ run_phases(vector<Target*> targets, const Options& options) for (size_t j=0; j<target->module.installed.size(); j++) { string filename = target->module.installed[j]; - if (!ends_with(filename, ".apk")) { + // Apk in the data partition + if (!starts_with(filename, dataPath) || !ends_with(filename, ".apk")) { continue; } @@ -1004,13 +1021,16 @@ run_phases(vector<Target*> targets, const Options& options) void run_tab_completion(const string& word) { - const string buildTop = get_required_env("ANDROID_BUILD_TOP", true); + const string buildTop = get_required_env("ANDROID_BUILD_TOP", false); const string buildProduct = get_required_env("TARGET_PRODUCT", false); + const string buildVariant = get_required_env("TARGET_BUILD_VARIANT", false); + const string buildType = get_required_env("TARGET_BUILD_TYPE", false); const string buildOut = get_out_dir(); - chdir_or_exit(buildTop.c_str()); - string buildDevice = sniff_device_name(buildOut, buildProduct); + BuildVars buildVars(buildOut, buildProduct, buildVariant, buildType); + + string buildDevice = buildVars.GetBuildVar("TARGET_DEVICE", false); map<string,Module> modules; read_modules(buildOut, buildDevice, &modules, true); diff --git a/tools/bit/make.cpp b/tools/bit/make.cpp index 5a9ab22719cb..627091321b2e 100644 --- a/tools/bit/make.cpp +++ b/tools/bit/make.cpp @@ -21,6 +21,7 @@ #include "util.h" #include <json/reader.h> +#include <json/writer.h> #include <json/value.h> #include <fstream> @@ -34,22 +35,118 @@ using namespace std; -map<string,string> g_buildVars; +static bool +map_contains(const map<string,string>& m, const string& k, const string& v) { + map<string,string>::const_iterator it = m.find(k); + if (it == m.end()) { + return false; + } + return it->second == v; +} + +static string +make_cache_filename(const string& outDir) +{ + string filename(outDir); + return filename + "/.bit_cache"; +} + +BuildVars::BuildVars(const string& outDir, const string& buildProduct, + const string& buildVariant, const string& buildType) + :m_filename(), + m_cache() +{ + m_cache["TARGET_PRODUCT"] = buildProduct; + m_cache["TARGET_BUILD_VARIANT"] = buildVariant; + m_cache["TARGET_BUILD_TYPE"] = buildType; + + // If we have any problems reading the file, that's ok, just do + // uncached calls to make / soong. + + if (outDir == "") { + return; + } + + + m_filename = make_cache_filename(outDir); + + std::ifstream stream(m_filename, std::ifstream::binary); + + if (stream.fail()) { + return; + } + + Json::Value json; + Json::Reader reader; + if (!reader.parse(stream, json)) { + return; + } + + if (!json.isObject()) { + return; + } + + map<string,string> cache; + + vector<string> names = json.getMemberNames(); + const int N = names.size(); + for (int i=0; i<N; i++) { + const string& name = names[i]; + const Json::Value& value = json[name]; + if (!value.isString()) { + continue; + } + cache[name] = value.asString(); + } + + // If all of the base variables match, then we can use this cache. Otherwise, use our + // base one. The next time someone reads a value, the new one, with our base varaibles + // will be saved. + if (map_contains(cache, "TARGET_PRODUCT", buildProduct) + && map_contains(cache, "TARGET_BUILD_VARIANT", buildVariant) + && map_contains(cache, "TARGET_BUILD_TYPE", buildType)) { + m_cache = cache; + } +} + +BuildVars::~BuildVars() +{ +} + +void +BuildVars::save() +{ + if (m_filename == "") { + return; + } + + Json::StyledStreamWriter writer(" "); + + Json::Value json(Json::objectValue); + + for (map<string,string>::const_iterator it = m_cache.begin(); it != m_cache.end(); it++) { + json[it->first] = it->second; + } + + std::ofstream stream(m_filename, std::ofstream::binary); + writer.write(stream, json); +} string -get_build_var(const string& name, bool quiet) +BuildVars::GetBuildVar(const string& name, bool quiet) { int err; - map<string,string>::iterator it = g_buildVars.find(name); - if (it == g_buildVars.end()) { + map<string,string>::iterator it = m_cache.find(name); + if (it == m_cache.end()) { Command cmd("build/soong/soong_ui.bash"); cmd.AddArg("--dumpvar-mode"); cmd.AddArg(name); string output = trim(get_command_output(cmd, &err, quiet)); if (err == 0) { - g_buildVars[name] = output; + m_cache[name] = output; + save(); return output; } else { return string(); @@ -59,38 +156,6 @@ get_build_var(const string& name, bool quiet) } } -string -sniff_device_name(const string& buildOut, const string& product) -{ - string match("ro.build.product=" + product); - - string base(buildOut + "/target/product"); - DIR* dir = opendir(base.c_str()); - if (dir == NULL) { - return string(); - } - - dirent* entry; - while ((entry = readdir(dir)) != NULL) { - if (entry->d_name[0] == '.') { - continue; - } - if (entry->d_type == DT_DIR) { - string filename(base + "/" + entry->d_name + "/system/build.prop"); - vector<string> lines; - split_lines(&lines, read_file(filename)); - for (size_t i=0; i<lines.size(); i++) { - if (lines[i] == match) { - return entry->d_name; - } - } - } - } - - closedir(dir); - return string(); -} - void json_error(const string& filename, const char* error, bool quiet) { diff --git a/tools/bit/make.h b/tools/bit/make.h index 1c9504d62d46..db0b69f88a0e 100644 --- a/tools/bit/make.h +++ b/tools/bit/make.h @@ -31,16 +31,26 @@ struct Module vector<string> installed; }; -string get_build_var(const string& name, bool quiet); - /** - * Poke around in the out directory and try to find a device name that matches - * our product. This is faster than running get_build_var and good enough for - * tab completion. - * - * Returns the empty string if we can't find one. + * Class to encapsulate getting build variables. Caches the + * results if possible. */ -string sniff_device_name(const string& buildOut, const string& product); +class BuildVars +{ +public: + BuildVars(const string& outDir, const string& buildProduct, + const string& buildVariant, const string& buildType); + ~BuildVars(); + + string GetBuildVar(const string& name, bool quiet); + +private: + void save(); + + string m_filename; + + map<string,string> m_cache; +}; void read_modules(const string& buildOut, const string& buildDevice, map<string,Module>* modules, bool quiet); diff --git a/tools/bit/print.cpp b/tools/bit/print.cpp index 790e0b4b227e..35feda11ec29 100644 --- a/tools/bit/print.cpp +++ b/tools/bit/print.cpp @@ -116,6 +116,20 @@ print_warning(const char* format, ...) } void +print_info(const char* format, ...) +{ + fputs(g_escapeBold, stdout); + + va_list args; + va_start(args, format); + vfprintf(stdout, format, args); + va_end(args); + + fputs(g_escapeEndColor, stdout); + fputc('\n', stdout); +} + +void print_one_line(const char* format, ...) { if (g_stdoutIsTty) { diff --git a/tools/bit/print.h b/tools/bit/print.h index b6c3e9aa27fa..db6cf5f65cf8 100644 --- a/tools/bit/print.h +++ b/tools/bit/print.h @@ -33,6 +33,7 @@ void print_status(const char* format, ...); void print_command(const Command& command); void print_error(const char* format, ...); void print_warning(const char* format, ...); +void print_info(const char* format, ...); void print_one_line(const char* format, ...); void check_error(int err); |