diff options
244 files changed, 5724 insertions, 3897 deletions
diff --git a/Android.bp b/Android.bp index 459442599b3a..05c7dbcba02e 100644 --- a/Android.bp +++ b/Android.bp @@ -772,7 +772,6 @@ java_defaults { "android.hardware.vibrator-V1.3-java", "android.hardware.wifi-V1.0-java-constants", "networkstack-aidl-framework-java", - "netd_aidl_parcelables-java", "devicepolicyprotosnano", ], @@ -912,6 +911,9 @@ aidl_interface { "core/java/android/net/dhcp/IDhcpServerCallbacks.aidl", "core/java/android/net/ip/IIpClient.aidl", "core/java/android/net/ip/IIpClientCallbacks.aidl", + "core/java/android/net/IIpMemoryStore.aidl", + "core/java/android/net/IIpMemoryStoreCallbacks.aidl", + "core/java/android/net/ipmemorystore/**/*.aidl", ], backend: { ndk: { @@ -925,29 +927,49 @@ aidl_interface { } aidl_interface { - name: "networkstack-aidl-framework", + name: "ipmemorystore-aidl-interfaces", local_include_dir: "core/java", srcs: [ - "core/java/android/net/TcpKeepalivePacketDataParcelable.aidl", "core/java/android/net/IIpMemoryStore.aidl", + "core/java/android/net/IIpMemoryStoreCallbacks.aidl", "core/java/android/net/ipmemorystore/**/*.aidl", ], +} + +aidl_interface { + name: "networkstack-aidl-framework", + local_include_dir: "core/java", + srcs: [ + "core/java/android/net/TcpKeepalivePacketDataParcelable.aidl", + ], api_dir: "aidl/networkstack", + backend: { + java: { + sdk_version: "28", + }, + }, } filegroup { - name: "framework-networkstack-shared-srcs", + name: "framework-annotations", 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/GuardedBy.java", "core/java/com/android/internal/annotations/VisibleForTesting.java", + ] +} + +filegroup { + name: "framework-networkstack-shared-srcs", + srcs: [ + // TODO: remove these annotations as soon as we can use andoid.support.annotations.* + ":framework-annotations", + "core/java/android/net/DhcpResults.java", + "core/java/android/util/LocalLog.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", @@ -1802,4 +1824,4 @@ aidl_mapping { name: "framework-aidl-mappings", srcs: [":framework-defaults"], output: "framework-aidl-mappings.txt" -}
\ No newline at end of file +} diff --git a/api/current.txt b/api/current.txt index d1b77f2c1096..2a0f499c3630 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6623,7 +6623,7 @@ package android.app.admin { method @Nullable public String[] getAccountTypesWithManagementDisabled(); method @Nullable public java.util.List<android.content.ComponentName> getActiveAdmins(); method @NonNull public java.util.Set<java.lang.String> getAffiliationIds(@NonNull android.content.ComponentName); - method @Nullable public java.util.List<java.lang.String> getAlwaysOnVpnLockdownWhitelist(@NonNull android.content.ComponentName); + method @Nullable public java.util.Set<java.lang.String> getAlwaysOnVpnLockdownWhitelist(@NonNull android.content.ComponentName); method @Nullable public String getAlwaysOnVpnPackage(@NonNull android.content.ComponentName); method @WorkerThread @NonNull public android.os.Bundle getApplicationRestrictions(@Nullable android.content.ComponentName, String); method @Deprecated @Nullable public String getApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName); @@ -6736,7 +6736,7 @@ package android.app.admin { method public void setAccountManagementDisabled(@NonNull android.content.ComponentName, String, boolean); method public void setAffiliationIds(@NonNull android.content.ComponentName, @NonNull java.util.Set<java.lang.String>); method public void setAlwaysOnVpnPackage(@NonNull android.content.ComponentName, @Nullable String, boolean) throws android.content.pm.PackageManager.NameNotFoundException; - method public void setAlwaysOnVpnPackage(@NonNull android.content.ComponentName, @Nullable String, boolean, @Nullable java.util.List<java.lang.String>) throws android.content.pm.PackageManager.NameNotFoundException; + method public void setAlwaysOnVpnPackage(@NonNull android.content.ComponentName, @Nullable String, boolean, @Nullable java.util.Set<java.lang.String>) throws android.content.pm.PackageManager.NameNotFoundException; method public boolean setApplicationHidden(@NonNull android.content.ComponentName, String, boolean); method @WorkerThread public void setApplicationRestrictions(@Nullable android.content.ComponentName, String, android.os.Bundle); method @Deprecated public void setApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName, @Nullable String) throws android.content.pm.PackageManager.NameNotFoundException; @@ -12016,7 +12016,7 @@ package android.content.pm { method @NonNull public android.content.pm.ShortcutInfo.Builder setIntents(@NonNull android.content.Intent[]); method @NonNull public android.content.pm.ShortcutInfo.Builder setLocusId(@NonNull android.content.LocusId); method @NonNull public android.content.pm.ShortcutInfo.Builder setLongLabel(@NonNull CharSequence); - method @NonNull public android.content.pm.ShortcutInfo.Builder setLongLived(); + method @NonNull public android.content.pm.ShortcutInfo.Builder setLongLived(boolean); method @NonNull public android.content.pm.ShortcutInfo.Builder setPerson(@NonNull android.app.Person); method @NonNull public android.content.pm.ShortcutInfo.Builder setPersons(@NonNull android.app.Person[]); method @NonNull public android.content.pm.ShortcutInfo.Builder setRank(int); @@ -15953,7 +15953,6 @@ package android.graphics.text { public class MeasuredText { method public void getBounds(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.Rect); method @FloatRange(from=0.0f) @Px public float getCharWidthAt(@IntRange(from=0) int); - method @NonNull public char[] getChars(); method @FloatRange(from=0.0) @Px public float getWidth(@IntRange(from=0) int, @IntRange(from=0) int); } @@ -17367,15 +17366,16 @@ package android.hardware.camera2.params { method @Nullable public java.util.Set<android.util.Size> getInputSizes(int); method @NonNull public java.util.Set<java.lang.Integer> getOutputFormats(); method @IntRange(from=0) public long getOutputMinFrameDuration(int, @NonNull android.util.Size); - method public <T> long getOutputMinFrameDuration(@NonNull Class<T>, @NonNull android.util.Size); + method @IntRange(from=0) public <T> long getOutputMinFrameDuration(@NonNull Class<T>, @NonNull android.util.Size); method @Nullable public java.util.Set<android.util.Size> getOutputSizes(int); - method public <T> java.util.Set<android.util.Size> getOutputSizes(@NonNull Class<T>); + method @Nullable public <T> java.util.Set<android.util.Size> getOutputSizes(@NonNull Class<T>); method @IntRange(from=0) public long getOutputStallDuration(int, @NonNull android.util.Size); - method public <T> long getOutputStallDuration(@NonNull Class<T>, @NonNull android.util.Size); + method @IntRange(from=0) public <T> long getOutputStallDuration(@NonNull Class<T>, @NonNull android.util.Size); method public int getRecommendedUseCase(); method @Nullable public java.util.Set<java.lang.Integer> getValidOutputFormatsForInput(int); method public boolean isOutputSupportedFor(int); method public boolean isOutputSupportedFor(@NonNull android.view.Surface); + field public static final int USECASE_LOW_LATENCY_SNAPSHOT = 6; // 0x6 field public static final int USECASE_PREVIEW = 0; // 0x0 field public static final int USECASE_RAW = 5; // 0x5 field public static final int USECASE_RECORD = 1; // 0x1 @@ -22966,6 +22966,7 @@ package android.location { method public void unregisterGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback); method public void unregisterGnssNavigationMessageCallback(@NonNull android.location.GnssNavigationMessage.Callback); method public void unregisterGnssStatusCallback(@NonNull android.location.GnssStatus.Callback); + field public static final String EXTRA_PROVIDER_NAME = "android.location.extra.PROVIDER_NAME"; field public static final String GPS_PROVIDER = "gps"; field public static final String KEY_LOCATION_CHANGED = "location"; field public static final String KEY_PROVIDER_ENABLED = "providerEnabled"; @@ -23293,6 +23294,7 @@ package android.media { field public static final String ACTION_MICROPHONE_MUTE_CHANGED = "android.media.action.MICROPHONE_MUTE_CHANGED"; field @Deprecated public static final String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED"; field public static final String ACTION_SCO_AUDIO_STATE_UPDATED = "android.media.ACTION_SCO_AUDIO_STATE_UPDATED"; + field public static final String ACTION_SPEAKERPHONE_STATE_CHANGED = "android.media.action.SPEAKERPHONE_STATE_CHANGED"; field public static final int ADJUST_LOWER = -1; // 0xffffffff field public static final int ADJUST_MUTE = -100; // 0xffffff9c field public static final int ADJUST_RAISE = 1; // 0x1 @@ -28756,10 +28758,9 @@ package android.net { } public final class DnsResolver { - method public static android.net.DnsResolver getInstance(); - method public void query(@Nullable android.net.Network, @NonNull byte[], int, @NonNull android.os.Handler, @NonNull android.net.DnsResolver.RawAnswerListener) throws android.system.ErrnoException; - method public void query(@Nullable android.net.Network, @NonNull String, int, int, int, @NonNull android.os.Handler, @NonNull android.net.DnsResolver.RawAnswerListener) throws android.system.ErrnoException; - method public void query(@Nullable android.net.Network, @NonNull String, int, @NonNull android.os.Handler, @NonNull android.net.DnsResolver.InetAddressAnswerListener) throws android.system.ErrnoException; + method @NonNull public static android.net.DnsResolver getInstance(); + method public <T> void query(@Nullable android.net.Network, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.DnsResolver.AnswerCallback<T>); + method public <T> void query(@Nullable android.net.Network, @NonNull String, int, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.DnsResolver.AnswerCallback<T>); field public static final int CLASS_IN = 1; // 0x1 field public static final int FLAG_EMPTY = 0; // 0x0 field public static final int FLAG_NO_CACHE_LOOKUP = 4; // 0x4 @@ -28769,12 +28770,23 @@ package android.net { field public static final int TYPE_AAAA = 28; // 0x1c } - public static interface DnsResolver.InetAddressAnswerListener { - method public void onAnswer(@NonNull java.util.List<java.net.InetAddress>); + public abstract static class DnsResolver.AnswerCallback<T> { + ctor public DnsResolver.AnswerCallback(@NonNull android.net.DnsResolver.AnswerParser<T>); + method public abstract void onAnswer(@NonNull T); + method public abstract void onParseException(@NonNull android.net.ParseException); + method public abstract void onQueryException(@NonNull android.system.ErrnoException); } - public static interface DnsResolver.RawAnswerListener { - method public void onAnswer(@Nullable byte[]); + public static interface DnsResolver.AnswerParser<T> { + method @NonNull public T parse(@NonNull byte[]) throws android.net.ParseException; + } + + public abstract static class DnsResolver.InetAddressAnswerCallback extends android.net.DnsResolver.AnswerCallback<java.util.List<java.net.InetAddress>> { + ctor public DnsResolver.InetAddressAnswerCallback(); + } + + public abstract static class DnsResolver.RawAnswerCallback extends android.net.DnsResolver.AnswerCallback<byte[]> { + ctor public DnsResolver.RawAnswerCallback(); } public class InetAddresses { @@ -29089,6 +29101,8 @@ package android.net { } public class ParseException extends java.lang.RuntimeException { + ctor public ParseException(@NonNull String); + ctor public ParseException(@NonNull String, @NonNull Throwable); field public String response; } @@ -39006,10 +39020,12 @@ package android.provider { method public static long getLong(android.content.ContentResolver, String) throws android.provider.Settings.SettingNotFoundException; method public static String getString(android.content.ContentResolver, String); method public static android.net.Uri getUriFor(String); + method @Deprecated public static boolean isLocationProviderEnabled(android.content.ContentResolver, String); method public static boolean putFloat(android.content.ContentResolver, String, float); method public static boolean putInt(android.content.ContentResolver, String, int); method public static boolean putLong(android.content.ContentResolver, String, long); method public static boolean putString(android.content.ContentResolver, String, String); + method @Deprecated public static void setLocationProviderEnabled(android.content.ContentResolver, String, boolean); field public static final String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED = "accessibility_display_inversion_enabled"; field public static final String ACCESSIBILITY_ENABLED = "accessibility_enabled"; field @Deprecated public static final String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password"; @@ -43189,6 +43205,7 @@ package android.telecom { method public void unregisterCallback(android.telecom.Call.Callback); field @Deprecated public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts"; field public static final String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS = "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS"; + field public static final String EXTRA_SILENT_RINGING_REQUESTED = "android.telecom.extra.SILENT_RINGING_REQUESTED"; field public static final String EXTRA_SUGGESTED_PHONE_ACCOUNTS = "android.telecom.extra.SUGGESTED_PHONE_ACCOUNTS"; field public static final int STATE_ACTIVE = 4; // 0x4 field public static final int STATE_CONNECTING = 9; // 0x9 @@ -43376,6 +43393,7 @@ package android.telecom { public static class CallScreeningService.CallResponse { method public boolean getDisallowCall(); method public boolean getRejectCall(); + method public boolean getSilenceCall(); method public boolean getSkipCallLog(); method public boolean getSkipNotification(); } @@ -43385,6 +43403,7 @@ package android.telecom { method public android.telecom.CallScreeningService.CallResponse build(); method public android.telecom.CallScreeningService.CallResponse.Builder setDisallowCall(boolean); method public android.telecom.CallScreeningService.CallResponse.Builder setRejectCall(boolean); + method @NonNull public android.telecom.CallScreeningService.CallResponse.Builder setSilenceCall(boolean); method public android.telecom.CallScreeningService.CallResponse.Builder setSkipCallLog(boolean); method public android.telecom.CallScreeningService.CallResponse.Builder setSkipNotification(boolean); } @@ -45242,7 +45261,7 @@ package android.telephony { field public static final String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID"; field public static final String EXTRA_CARRIER_NAME = "android.telephony.extra.CARRIER_NAME"; field public static final String EXTRA_HIDE_PUBLIC_SETTINGS = "android.telephony.extra.HIDE_PUBLIC_SETTINGS"; - field public static final String EXTRA_INCOMING_NUMBER = "incoming_number"; + field @Deprecated public static final String EXTRA_INCOMING_NUMBER = "incoming_number"; field public static final String EXTRA_IS_REFRESH = "android.telephony.extra.IS_REFRESH"; field public static final String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT"; field public static final String EXTRA_NETWORK_COUNTRY = "android.telephony.extra.NETWORK_COUNTRY"; diff --git a/api/removed.txt b/api/removed.txt index 5108dd0d8514..fa07094e6b84 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -314,17 +314,11 @@ package android.media.tv { package android.net { public class ConnectivityManager { - method @Deprecated public void getLatestTetheringEntitlementValue(int, boolean, @NonNull android.net.ConnectivityManager.TetheringEntitlementValueListener, @Nullable android.os.Handler); method @Deprecated public boolean requestRouteToHost(int, int); method @Deprecated public int startUsingNetworkFeature(int, String); method @Deprecated public int stopUsingNetworkFeature(int, String); } - @Deprecated public abstract static class ConnectivityManager.TetheringEntitlementValueListener { - ctor public ConnectivityManager.TetheringEntitlementValueListener(); - method public void onEntitlementResult(int); - } - @Deprecated public class NetworkBadging { method @NonNull public static android.graphics.drawable.Drawable getWifiIcon(@IntRange(from=0, to=4) int, int, @Nullable android.content.res.Resources.Theme); field public static final int BADGING_4K = 30; // 0x1e @@ -553,11 +547,6 @@ package android.provider { field @Deprecated public static final String CONTACT_METADATA_SYNC = "contact_metadata_sync"; } - public static final class Settings.Secure extends android.provider.Settings.NameValueTable { - method @Deprecated public static boolean isLocationProviderEnabled(android.content.ContentResolver, String); - method @Deprecated public static void setLocationProviderEnabled(android.content.ContentResolver, String, boolean); - } - public static final class Settings.System extends android.provider.Settings.NameValueTable { field public static final String APPEND_FOR_LAST_AUDIBLE = "_last_audible"; field public static final String VOLUME_ALARM = "volume_alarm"; diff --git a/api/system-current.txt b/api/system-current.txt index f901b38dd796..79aca0b534f6 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -924,8 +924,10 @@ package android.app.backup { method public int restoreAll(long, android.app.backup.RestoreObserver); method public int restorePackage(String, android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor); method public int restorePackage(String, android.app.backup.RestoreObserver); - method public int restoreSome(long, android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor, String[]); - method public int restoreSome(long, android.app.backup.RestoreObserver, String[]); + method public int restorePackages(long, @Nullable android.app.backup.RestoreObserver, @NonNull java.util.Set<java.lang.String>, @Nullable android.app.backup.BackupManagerMonitor); + method public int restorePackages(long, @Nullable android.app.backup.RestoreObserver, @NonNull java.util.Set<java.lang.String>); + method @Deprecated public int restoreSome(long, android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor, String[]); + method @Deprecated public int restoreSome(long, android.app.backup.RestoreObserver, String[]); } public class RestoreSet implements android.os.Parcelable { @@ -4482,16 +4484,16 @@ package android.net.metrics { package android.net.util { - public class SocketUtils { + public final class SocketUtils { method public static void addArpEntry(@NonNull java.net.Inet4Address, @NonNull android.net.MacAddress, @NonNull String, @NonNull java.io.FileDescriptor) throws java.io.IOException; method public static void attachControlPacketFilter(@NonNull java.io.FileDescriptor, int) throws java.net.SocketException; method public static void attachDhcpFilter(@NonNull java.io.FileDescriptor) throws java.net.SocketException; method public static void attachRaFilter(@NonNull java.io.FileDescriptor, int) throws java.net.SocketException; method public static void bindSocketToInterface(@NonNull java.io.FileDescriptor, @NonNull String) throws android.system.ErrnoException; method public static void closeSocket(@Nullable java.io.FileDescriptor) throws java.io.IOException; - method public static java.net.SocketAddress makeNetlinkSocketAddress(int, int); - method public static java.net.SocketAddress makePacketSocketAddress(short, int); - method public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]); + method @NonNull public static java.net.SocketAddress makeNetlinkSocketAddress(int, int); + method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int); + method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]); method public static void setSocketTimeValueOption(@NonNull java.io.FileDescriptor, int, int, long) throws android.system.ErrnoException; } @@ -5603,7 +5605,7 @@ package android.os { method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.PersistableBundle getSeedAccountOptions(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountType(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public long[] getSerialNumbersOfUsers(boolean); - method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.graphics.Bitmap getUserIcon(); + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public android.graphics.Bitmap getUserIcon(); method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int getUserRestrictionSource(String, android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle); method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public int getUserSwitchability(); @@ -7235,7 +7237,6 @@ package android.telephony { field public static final int ACCESS_BLOCK_ALL = 2088; // 0x828 field public static final int ACCESS_CLASS_DSAC_REJECTION = 2108; // 0x83c field public static final int ACCESS_CONTROL_LIST_CHECK_FAILURE = 2128; // 0x850 - field public static final int ACCESS_PROBE_LIMIT_REACHED = 2079; // 0x81f field public static final int ACTIVATION_REJECTED_BCM_VIOLATION = 48; // 0x30 field public static final int ACTIVATION_REJECT_GGSN = 30; // 0x1e field public static final int ACTIVATION_REJECT_UNSPECIFIED = 31; // 0x1f @@ -7350,9 +7351,7 @@ package android.telephony { field public static final int INVALID_PRIMARY_NSAPI = 2158; // 0x86e field public static final int INVALID_SIM_STATE = 2224; // 0x8b0 field public static final int INVALID_TRANSACTION_ID = 81; // 0x51 - field public static final int IPV4_CONNECTIONS_LIMIT_REACHED = 2052; // 0x804 field public static final int IPV6_ADDRESS_TRANSFER_FAILED = 2047; // 0x7ff - field public static final int IPV6_CONNECTIONS_LIMIT_REACHED = 2053; // 0x805 field public static final int IPV6_PREFIX_UNAVAILABLE = 2250; // 0x8ca field public static final int IP_ADDRESS_MISMATCH = 119; // 0x77 field public static final int IP_VERSION_MISMATCH = 2055; // 0x807 @@ -7371,6 +7370,10 @@ package android.telephony { field public static final int MAC_FAILURE = 2183; // 0x887 field public static final int MAXIMIUM_NSAPIS_EXCEEDED = 2157; // 0x86d field public static final int MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED = 2166; // 0x876 + field public static final int MAX_ACCESS_PROBE = 2079; // 0x81f + field public static final int MAX_IPV4_CONNECTIONS = 2052; // 0x804 + field public static final int MAX_IPV6_CONNECTIONS = 2053; // 0x805 + field public static final int MAX_PPP_INACTIVITY_TIMER_EXPIRED = 2046; // 0x7fe field public static final int MESSAGE_INCORRECT_SEMANTIC = 95; // 0x5f field public static final int MESSAGE_TYPE_UNSUPPORTED = 97; // 0x61 field public static final int MIP_CONFIG_FAILURE = 2050; // 0x802 @@ -7479,7 +7482,6 @@ package android.telephony { field public static final int PPP_AUTH_FAILURE = 2229; // 0x8b5 field public static final int PPP_CHAP_FAILURE = 2232; // 0x8b8 field public static final int PPP_CLOSE_IN_PROGRESS = 2233; // 0x8b9 - field public static final int PPP_INACTIVITY_TIMER_EXPIRED = 2046; // 0x7fe field public static final int PPP_OPTION_MISMATCH = 2230; // 0x8b6 field public static final int PPP_PAP_FAILURE = 2231; // 0x8b7 field public static final int PPP_TIMEOUT = 2228; // 0x8b4 @@ -7680,8 +7682,8 @@ package android.telephony { field public static final String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming"; } - public class NetworkRegistrationState implements android.os.Parcelable { - ctor public NetworkRegistrationState(int, int, int, int, int, boolean, @NonNull int[], @Nullable android.telephony.CellIdentity); + public class NetworkRegistrationInfo implements android.os.Parcelable { + ctor public NetworkRegistrationInfo(int, int, int, int, int, boolean, @NonNull int[], @Nullable android.telephony.CellIdentity); method public int describeContents(); method public int getAccessNetworkTechnology(); method @NonNull public int[] getAvailableServices(); @@ -7695,7 +7697,7 @@ package android.telephony { method public boolean isEmergencyEnabled(); method public boolean isRoaming(); method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.NetworkRegistrationState> CREATOR; + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.NetworkRegistrationInfo> CREATOR; field public static final int DOMAIN_CS = 1; // 0x1 field public static final int DOMAIN_PS = 2; // 0x2 field public static final int REG_STATE_DENIED = 3; // 0x3 @@ -7711,19 +7713,19 @@ package android.telephony { field public static final int SERVICE_TYPE_VOICE = 1; // 0x1 } - public static class NetworkRegistrationState.Builder { - ctor public NetworkRegistrationState.Builder(); - method @NonNull public android.telephony.NetworkRegistrationState build(); - method @NonNull public android.telephony.NetworkRegistrationState.Builder setAccessNetworkTechnology(int); - method @NonNull public android.telephony.NetworkRegistrationState.Builder setAvailableServices(@NonNull int[]); - method @NonNull public android.telephony.NetworkRegistrationState.Builder setCellIdentity(@Nullable android.telephony.CellIdentity); - method @NonNull public android.telephony.NetworkRegistrationState.Builder setDomain(int); - method @NonNull public android.telephony.NetworkRegistrationState.Builder setEmergencyOnly(boolean); - method @NonNull public android.telephony.NetworkRegistrationState.Builder setNrStatus(int); - method @NonNull public android.telephony.NetworkRegistrationState.Builder setRegState(int); - method @NonNull public android.telephony.NetworkRegistrationState.Builder setRejectCause(int); - method @NonNull public android.telephony.NetworkRegistrationState.Builder setRoamingType(int); - method @NonNull public android.telephony.NetworkRegistrationState.Builder setTransportType(int); + public static class NetworkRegistrationInfo.Builder { + ctor public NetworkRegistrationInfo.Builder(); + method @NonNull public android.telephony.NetworkRegistrationInfo build(); + method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setAccessNetworkTechnology(int); + method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setAvailableServices(@NonNull int[]); + method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setCellIdentity(@Nullable android.telephony.CellIdentity); + method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setDomain(int); + method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setEmergencyOnly(boolean); + method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setNrStatus(int); + method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRegState(int); + method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRejectCause(int); + method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRoamingType(int); + method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setTransportType(int); } public abstract class NetworkService extends android.app.Service { @@ -7736,13 +7738,13 @@ package android.telephony { public abstract class NetworkService.NetworkServiceProvider implements java.lang.AutoCloseable { ctor public NetworkService.NetworkServiceProvider(int); method public abstract void close(); - method public void getNetworkRegistrationState(int, @NonNull android.telephony.NetworkServiceCallback); + method public void getNetworkRegistrationInfo(int, @NonNull android.telephony.NetworkServiceCallback); method public final int getSlotIndex(); - method public final void notifyNetworkRegistrationStateChanged(); + method public final void notifyNetworkRegistrationInfoChanged(); } public class NetworkServiceCallback { - method public void onGetNetworkRegistrationStateComplete(int, @Nullable android.telephony.NetworkRegistrationState); + method public void onGetNetworkRegistrationInfoComplete(int, @Nullable android.telephony.NetworkRegistrationInfo); field public static final int RESULT_ERROR_BUSY = 3; // 0x3 field public static final int RESULT_ERROR_FAILED = 5; // 0x5 field public static final int RESULT_ERROR_ILLEGAL_STATE = 4; // 0x4 @@ -7917,12 +7919,10 @@ package android.telephony { } public class ServiceState implements android.os.Parcelable { - method @Nullable public android.telephony.NetworkRegistrationState getNetworkRegistrationState(int, int); - method @NonNull public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates(); - method @Deprecated @NonNull public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates(int); - method @Deprecated @Nullable public android.telephony.NetworkRegistrationState getNetworkRegistrationStates(int, int); - method @NonNull public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStatesForDomain(int); - method @NonNull public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStatesForTransportType(int); + method @Nullable public android.telephony.NetworkRegistrationInfo getNetworkRegistrationInfo(int, int); + method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoList(); + method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForDomain(int); + method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForTransportType(int); field public static final int ROAMING_TYPE_DOMESTIC = 2; // 0x2 field public static final int ROAMING_TYPE_INTERNATIONAL = 3; // 0x3 field public static final int ROAMING_TYPE_NOT_ROAMING = 0; // 0x0 diff --git a/api/test-current.txt b/api/test-current.txt index 4ccfa1c06745..8b540b194582 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1507,16 +1507,16 @@ package android.net.metrics { package android.net.util { - public class SocketUtils { + public final class SocketUtils { method public static void addArpEntry(@NonNull java.net.Inet4Address, @NonNull android.net.MacAddress, @NonNull String, @NonNull java.io.FileDescriptor) throws java.io.IOException; method public static void attachControlPacketFilter(@NonNull java.io.FileDescriptor, int) throws java.net.SocketException; method public static void attachDhcpFilter(@NonNull java.io.FileDescriptor) throws java.net.SocketException; method public static void attachRaFilter(@NonNull java.io.FileDescriptor, int) throws java.net.SocketException; method public static void bindSocketToInterface(@NonNull java.io.FileDescriptor, @NonNull String) throws android.system.ErrnoException; method public static void closeSocket(@Nullable java.io.FileDescriptor) throws java.io.IOException; - method public static java.net.SocketAddress makeNetlinkSocketAddress(int, int); - method public static java.net.SocketAddress makePacketSocketAddress(short, int); - method public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]); + method @NonNull public static java.net.SocketAddress makeNetlinkSocketAddress(int, int); + method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int); + method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]); method public static void setSocketTimeValueOption(@NonNull java.io.FileDescriptor, int, int, long) throws android.system.ErrnoException; } @@ -2548,7 +2548,8 @@ package android.telephony { method public int getCarrierIdListVersion(); method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile(); - method public void setCarrierTestOverride(String, String, String, String, String, String, String); + method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String); + method public void setCarrierTestOverride(String, String, 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 diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index 062ba655640e..98ab6665958c 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -803,8 +803,8 @@ public class Bmgr { } else { String[] names = new String[filter.size()]; filter.toArray(names); - didRestore = (mRestore.restoreSome(token, observer, - null, names) == 0); + didRestore = (mRestore.restorePackages(token, observer, names, + null) == 0); } break; } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index b8e8fdc6a9f2..1dbbbc5595ba 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -45,6 +45,7 @@ import "frameworks/base/core/proto/android/stats/docsui/docsui_enums.proto"; import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto"; import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.proto"; import "frameworks/base/core/proto/android/stats/launcher/launcher.proto"; +import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto"; import "frameworks/base/core/proto/android/stats/style/style_enums.proto"; import "frameworks/base/core/proto/android/telecomm/enums.proto"; import "frameworks/base/core/proto/android/telephony/enums.proto"; @@ -250,6 +251,7 @@ message Atom { HiddenApiUsed hidden_api_used = 178 [(allow_from_any_uid) = true]; StyleUIChanged style_ui_changed = 179; PrivacyIndicatorsInteracted privacy_indicators_interacted = 180; + AppInstallOnExternalStorageReported app_install_on_external_storage_reported = 181; } // Pulled events will start at field 10000. @@ -308,7 +310,7 @@ message Atom { DangerousPermissionState dangerous_permission_state = 10050; TrainInfo train_info = 10051; TimeZoneDataInfo time_zone_data_info = 10052; - SDCardInfo sdcard_info = 10053; + ExternalStorageInfo external_storage_info = 10053; GpuStatsGlobalInfo gpu_stats_global_info = 10054; GpuStatsAppInfo gpu_stats_app_info = 10055; SystemIonHeapSize system_ion_heap_size = 10056; @@ -2558,6 +2560,18 @@ message AppOptimizedAfterDowngraded { } /** + * Logs whenever an app is installed on external storage. + * Logged from: + frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java + */ +message AppInstallOnExternalStorageReported { + // The type of external storage. + optional android.stats.storage.ExternalStorageType storage_type = 1; + // The name of the package that is installed on the sd card. + optional string package_name = 2; +} + +/** * Logs when an app crashes. * Logged from: * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java @@ -3297,25 +3311,27 @@ message BatteryCycleCount { } /** - * Logs that an SD card is mounted and information about it, its type (public or private) and the - * size in bytes. + * Logs that external storage is mounted and information about it, the storage type (sd card/usb/ + * others), its type (public or private) and the size in bytes. * Pulled from: * StatsCompanionService */ -message SDCardInfo { +message ExternalStorageInfo { - enum Type { + enum VolumeType { UNKNOWN = 0; - TYPE_PUBLIC = 1; - TYPE_PRIVATE = 2; - OTHERS = 3; + PUBLIC = 1; + PRIVATE = 2; + OTHER = 3; } - // Type of the SD card: TYPE_PUBLIC if portable and TYPE_PRIVATE if internal. - optional Type type = 1; + // The type of external storage. + optional android.stats.storage.ExternalStorageType storage_type = 1; + // Type of the volume: TYPE_PUBLIC if portable and TYPE_PRIVATE if internal. + optional VolumeType volume_type = 2; // Total size of the sd card in bytes. - optional int64 size_bytes = 2; + optional int64 size_bytes = 3; } /* diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index c7ae656920b8..2abfc2450a00 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -242,9 +242,9 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { // TimeZoneDataInfo. {android::util::TIME_ZONE_DATA_INFO, {.puller = new StatsCompanionServicePuller(android::util::TIME_ZONE_DATA_INFO)}}, - // SDCardInfo - {android::util::SDCARD_INFO, - {.puller = new StatsCompanionServicePuller(android::util::SDCARD_INFO)}}, + // ExternalStorageInfo + {android::util::EXTERNAL_STORAGE_INFO, + {.puller = new StatsCompanionServicePuller(android::util::EXTERNAL_STORAGE_INFO)}}, // GpuStatsGlobalInfo {android::util::GPU_STATS_GLOBAL_INFO, {.puller = new GpuStatsPuller(android::util::GPU_STATS_GLOBAL_INFO)}}, diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 254d7d5a38f5..c023e6f77e7c 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -319,10 +319,11 @@ void CountMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) { return; } - flushCurrentBucketLocked(eventTimeNs, eventTimeNs); // Setup the bucket start time and number. int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs; - mCurrentBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs; + int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs; + flushCurrentBucketLocked(eventTimeNs, nextBucketNs); + mCurrentBucketNum += numBucketsForward; VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId, (long long)mCurrentBucketStartTimeNs); @@ -375,6 +376,7 @@ void CountMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, // Only resets the counters, but doesn't setup the times nor numbers. // (Do not clear since the old one is still referenced in mAnomalyTrackers). mCurrentSlicedCounter = std::make_shared<DimToValMap>(); + mCurrentBucketStartTimeNs = nextBucketStartTimeNs; } // Rough estimate of CountMetricProducer buffer stored. This number will be diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 7dc4e2d91533..615c7f2959c0 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -559,26 +559,10 @@ void DurationMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) { return; } VLOG("flushing..........."); - for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin(); - whatIt != mCurrentSlicedDurationTrackerMap.end();) { - for (auto it = whatIt->second.begin(); it != whatIt->second.end();) { - if (it->second->flushIfNeeded(eventTimeNs, &mPastBuckets)) { - VLOG("erase bucket for key %s %s", - whatIt->first.toString().c_str(), it->first.toString().c_str()); - it = whatIt->second.erase(it); - } else { - ++it; - } - } - if (whatIt->second.empty()) { - whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt); - } else { - whatIt++; - } - } - int numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs; - mCurrentBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs; + int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs; + flushCurrentBucketLocked(eventTimeNs, nextBucketNs); + mCurrentBucketNum += numBucketsForward; } @@ -602,6 +586,7 @@ void DurationMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs } } StatsdStats::getInstance().noteBucketCount(mMetricId); + mCurrentBucketStartTimeNs = nextBucketStartTimeNs; } void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index 2b6cac8189f3..2f9afa5d5c19 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -529,11 +529,11 @@ void GaugeMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) { return; } - flushCurrentBucketLocked(eventTimeNs, eventTimeNs); - // Adjusts the bucket start and end times. int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs; - mCurrentBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs; + int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs; + flushCurrentBucketLocked(eventTimeNs, nextBucketNs); + mCurrentBucketNum += numBucketsForward; VLOG("Gauge metric %lld: new bucket start time: %lld", (long long)mMetricId, (long long)mCurrentBucketStartTimeNs); @@ -578,6 +578,7 @@ void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, StatsdStats::getInstance().noteBucketCount(mMetricId); mCurrentSlicedBucket = std::make_shared<DimToGaugeAtomsMap>(); + mCurrentBucketStartTimeNs = nextBucketStartTimeNs; } size_t GaugeMetricProducer::byteSizeLocked() const { diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index a0a1348cacff..12cec5d6dbec 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -178,7 +178,7 @@ private: void pullAndMatchEventsLocked(const int64_t timestampNs, ConditionState condition); - void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData, + void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData, int64_t originalPullTimeNs, int64_t eventElapsedTimeNs, ConditionState condition); diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp index 52d5ffc33317..f7e32d4aed26 100644 --- a/cmds/statsd/src/shell/ShellSubscriber.cpp +++ b/cmds/statsd/src/shell/ShellSubscriber.cpp @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#define DEBUG true // STOPSHIP if true +#define DEBUG false // STOPSHIP if true #include "Log.h" #include "ShellSubscriber.h" diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 664f0a37a459..f6cfe4855070 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -329,6 +329,9 @@ public abstract class ActivityManagerInternal { /** Returns true if the given uid is the app in the foreground. */ public abstract boolean isAppForeground(int uid); + /** Returns true if the given uid is currently marked 'bad' */ + public abstract boolean isAppBad(ApplicationInfo info); + /** Remove pending backup for the given userId. */ public abstract void clearPendingBackup(int userId); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index c0a702f28b8d..08239a1e7ed9 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3115,6 +3115,10 @@ public final class ActivityThread extends ClientTransactionHandler { if (!r.stopped) { throw new IllegalStateException("Can't start activity that is not stopped."); } + if (r.activity.mFinished) { + // TODO(lifecycler): How can this happen? + return; + } // Start activity.performStart("handleStartActivity"); @@ -3237,8 +3241,6 @@ public final class ActivityThread extends ClientTransactionHandler { if (!r.activity.mFinished && pendingActions != null) { pendingActions.setOldState(r.state); pendingActions.setRestoreInstanceState(true); - } - if (pendingActions != null) { pendingActions.setCallOnPostCreate(true); } } else { @@ -3940,7 +3942,7 @@ public final class ActivityThread extends ClientTransactionHandler { if (localLOGV) { Slog.v(TAG, "Performing resume of " + r + " finished=" + r.activity.mFinished); } - if (r == null) { + if (r == null || r.activity.mFinished) { return null; } if (r.getLifecycleState() == ON_RESUME) { @@ -4210,6 +4212,12 @@ public final class ActivityThread extends ClientTransactionHandler { private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason, PendingTransactionActions pendingActions) { if (r.paused) { + if (r.activity.mFinished) { + // If we are finishing, we won't call onResume() in certain cases. + // So here we likewise don't want to call onPause() if the activity + // isn't resumed. + return null; + } RuntimeException e = new RuntimeException( "Performing pause of activity that is not resumed: " + r.intent.getComponent().toShortString()); @@ -4329,13 +4337,20 @@ public final class ActivityThread extends ClientTransactionHandler { boolean saveState, boolean finalStateRequest, String reason) { if (localLOGV) Slog.v(TAG, "Performing stop of " + r); if (r != null) { - if (!keepShown && r.stopped && !finalStateRequest) { - // Double stop request is possible if activity receives 'sleep' followed by 'stop'. - final RuntimeException e = new RuntimeException( - "Performing stop of activity that is already stopped: " - + r.intent.getComponent().toShortString()); - Slog.e(TAG, e.getMessage(), e); - Slog.e(TAG, r.getStateString()); + if (!keepShown && r.stopped) { + if (r.activity.mFinished) { + // If we are finishing, we won't call onResume() in certain + // cases. So here we likewise don't want to call onStop() + // if the activity isn't resumed. + return; + } + if (!finalStateRequest) { + final RuntimeException e = new RuntimeException( + "Performing stop of activity that is already stopped: " + + r.intent.getComponent().toShortString()); + Slog.e(TAG, e.getMessage(), e); + Slog.e(TAG, r.getStateString()); + } } // One must first be paused before stopped... diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 29e6807a3244..15084de0d7dd 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -193,7 +193,7 @@ interface INotificationManager void setNotificationDelegate(String callingPkg, String delegate); String getNotificationDelegate(String callingPkg); - boolean canNotifyAsPackage(String callingPkg, String targetPkg); + boolean canNotifyAsPackage(String callingPkg, String targetPkg, int userId); void setPrivateNotificationsAllowed(boolean allow); boolean getPrivateNotificationsAllowed(); diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index db8c905eac34..41a992139111 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -781,6 +781,16 @@ public final class LoadedApk { isBundledApp = false; } + // Similar to vendor apks, we should add /product/lib for apks from product partition + // and not having /product/lib in the default search path + final boolean treatProductApkAsUnbundled = !defaultSearchPaths.contains("/product/lib"); + if (mApplicationInfo.getCodePath() != null + && mApplicationInfo.isProduct() && treatProductApkAsUnbundled + // TODO(b/128557860): Change target SDK version when version code R is available. + && getTargetSdkVersion() == Build.VERSION_CODES.CUR_DEVELOPMENT) { + isBundledApp = false; + } + makePaths(mActivityThread, isBundledApp, mApplicationInfo, zipPaths, libPaths); String libraryPermittedPath = mDataDir; diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 11b40c244482..6a301c91bb06 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -627,7 +627,7 @@ public class NotificationManager { public boolean canNotifyAsPackage(@NonNull String pkg) { INotificationManager service = getService(); try { - return service.canNotifyAsPackage(mContext.getPackageName(), pkg); + return service.canNotifyAsPackage(mContext.getPackageName(), pkg, mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 08e08806a310..4d280b76b3fa 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -101,11 +101,9 @@ import android.net.ConnectivityThread; import android.net.EthernetManager; import android.net.IConnectivityManager; import android.net.IEthernetManager; -import android.net.IIpMemoryStore; import android.net.IIpSecService; import android.net.INetworkPolicyManager; import android.net.ITestNetworkManager; -import android.net.IpMemoryStore; import android.net.IpSecManager; import android.net.NetworkPolicyManager; import android.net.NetworkScoreManager; @@ -340,17 +338,6 @@ final class SystemServiceRegistry { } }); - registerService(Context.IP_MEMORY_STORE_SERVICE, IpMemoryStore.class, - new CachedServiceFetcher<IpMemoryStore>() { - @Override - public IpMemoryStore createService(final ContextImpl ctx) - throws ServiceNotFoundException { - IBinder b = ServiceManager.getServiceOrThrow( - Context.IP_MEMORY_STORE_SERVICE); - IIpMemoryStore service = IIpMemoryStore.Stub.asInterface(b); - return new IpMemoryStore(ctx, service); - }}); - registerService(Context.IPSEC_SERVICE, IpSecManager.class, new CachedServiceFetcher<IpSecManager>() { @Override diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 9a4e2151dd3b..94361450eccb 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -112,6 +112,7 @@ import java.security.spec.PKCS8EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -5171,7 +5172,8 @@ public class DevicePolicyManager { * </ul> * The call will fail if called with the package name of an unsupported VPN app. * <p> Enabling lockdown via {@code lockdownEnabled} argument carries the risk that any failure - * of the VPN provider could break networking for all apps. + * of the VPN provider could break networking for all apps. This method clears any lockdown + * whitelist set by {@link #setAlwaysOnVpnPackage(ComponentName, String, boolean, Set)}. * * @param vpnPackage The package name for an installed VPN app on the device, or {@code null} to * remove an existing always-on VPN configuration. @@ -5181,11 +5183,11 @@ public class DevicePolicyManager { * @throws NameNotFoundException if {@code vpnPackage} is not installed. * @throws UnsupportedOperationException if {@code vpnPackage} exists but does not support being * set as always-on, or if always-on VPN is not available. - * @see #setAlwaysOnVpnPackage(ComponentName, String, boolean, List) + * @see #setAlwaysOnVpnPackage(ComponentName, String, boolean, Set) */ public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage, boolean lockdownEnabled) throws NameNotFoundException { - setAlwaysOnVpnPackage(admin, vpnPackage, lockdownEnabled, Collections.emptyList()); + setAlwaysOnVpnPackage(admin, vpnPackage, lockdownEnabled, Collections.emptySet()); } /** @@ -5195,6 +5197,11 @@ public class DevicePolicyManager { * System apps can always bypass VPN. * <p> Note that the system doesn't update the whitelist when packages are installed or * uninstalled, the admin app must call this method to keep the list up to date. + * <p> When {@code lockdownEnabled} is false {@code lockdownWhitelist} is ignored . When + * {@code lockdownEnabled} is {@code true} and {@code lockdownWhitelist} is {@code null} or + * empty, only system apps can bypass VPN. + * <p> Setting always-on VPN package to {@code null} or using + * {@link #setAlwaysOnVpnPackage(ComponentName, String, boolean)} clears lockdown whitelist. * * @param vpnPackage package name for an installed VPN app on the device, or {@code null} * to remove an existing always-on VPN configuration @@ -5211,13 +5218,13 @@ public class DevicePolicyManager { * available. */ public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage, - boolean lockdownEnabled, @Nullable List<String> lockdownWhitelist) + boolean lockdownEnabled, @Nullable Set<String> lockdownWhitelist) throws NameNotFoundException { throwIfParentInstance("setAlwaysOnVpnPackage"); if (mService != null) { try { - mService.setAlwaysOnVpnPackage( - admin, vpnPackage, lockdownEnabled, lockdownWhitelist); + mService.setAlwaysOnVpnPackage(admin, vpnPackage, lockdownEnabled, + lockdownWhitelist == null ? null : new ArrayList<>(lockdownWhitelist)); } catch (ServiceSpecificException e) { switch (e.errorCode) { case ERROR_VPN_PACKAGE_NOT_FOUND: @@ -5255,7 +5262,7 @@ public class DevicePolicyManager { } /** - * Called by device or profile owner to query the list of packages that are allowed to access + * Called by device or profile owner to query the set of packages that are allowed to access * the network directly when always-on VPN is in lockdown mode but not connected. Returns * {@code null} when always-on VPN is not active or not in lockdown mode. * @@ -5263,13 +5270,15 @@ public class DevicePolicyManager { * * @throws SecurityException if {@code admin} is not a device or a profile owner. * - * @see #setAlwaysOnVpnPackage(ComponentName, String, boolean, List) + * @see #setAlwaysOnVpnPackage(ComponentName, String, boolean, Set) */ - public @Nullable List<String> getAlwaysOnVpnLockdownWhitelist(@NonNull ComponentName admin) { + public @Nullable Set<String> getAlwaysOnVpnLockdownWhitelist(@NonNull ComponentName admin) { throwIfParentInstance("getAlwaysOnVpnLockdownWhitelist"); if (mService != null) { try { - return mService.getAlwaysOnVpnLockdownWhitelist(admin); + final List<String> whitelist = + mService.getAlwaysOnVpnLockdownWhitelist(admin); + return whitelist == null ? null : new HashSet<>(whitelist); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/backup/IRestoreSession.aidl b/core/java/android/app/backup/IRestoreSession.aidl index b9e94856b69e..c3a298bd8ee5 100644 --- a/core/java/android/app/backup/IRestoreSession.aidl +++ b/core/java/android/app/backup/IRestoreSession.aidl @@ -71,8 +71,8 @@ interface IRestoreSession { * applications mentioned in this list will have their data restored. * @param monitor If non null the binder will send important events to this monitor. */ - int restoreSome(long token, IRestoreObserver observer, IBackupManagerMonitor monitor, - in String[] packages); + int restorePackages(long token, IRestoreObserver observer, in String[] packages, + IBackupManagerMonitor monitor); /** * Restore a single application from backup. The data will be restored from the diff --git a/core/java/android/app/backup/RestoreSession.java b/core/java/android/app/backup/RestoreSession.java index 79925ec2d47a..084b13b61617 100644 --- a/core/java/android/app/backup/RestoreSession.java +++ b/core/java/android/app/backup/RestoreSession.java @@ -16,6 +16,8 @@ package android.app.backup; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.Context; import android.os.Bundle; @@ -24,6 +26,10 @@ import android.os.Message; import android.os.RemoteException; import android.util.Log; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + /** * Interface for managing a restore session. * @hide @@ -138,14 +144,15 @@ public class RestoreSession { * the restore set that should be used. * @param observer If non-null, this binder points to an object that will receive * progress callbacks during the restore operation. - * @param monitor If non-null, this binder points to an object that will receive - * progress callbacks during the restore operation. * @param packages The set of packages for which to attempt a restore. Regardless of * the contents of the actual back-end dataset named by {@code token}, only * applications mentioned in this list will have their data restored. + * @param monitor If non-null, this binder points to an object that will receive + * progress callbacks during the restore operation containing detailed information on any + * failures or important decisions made by {@link BackupManager}. */ - public int restoreSome(long token, RestoreObserver observer, BackupManagerMonitor monitor, - String[] packages) { + public int restorePackages(long token, @Nullable RestoreObserver observer, + @NonNull Set<String> packages, @Nullable BackupManagerMonitor monitor) { int err = -1; if (mObserver != null) { Log.d(TAG, "restoreAll() called during active restore"); @@ -156,7 +163,8 @@ public class RestoreSession { ? null : new BackupManagerMonitorWrapper(monitor); try { - err = mBinder.restoreSome(token, mObserver, monitorWrapper, packages); + err = mBinder.restorePackages(token, mObserver, packages.toArray(new String[] {}), + monitorWrapper); } catch (RemoteException e) { Log.d(TAG, "Can't contact server to restore packages"); } @@ -180,6 +188,60 @@ public class RestoreSession { * the contents of the actual back-end dataset named by {@code token}, only * applications mentioned in this list will have their data restored. */ + public int restorePackages(long token, @Nullable RestoreObserver observer, + @NonNull Set<String> packages) { + return restorePackages(token, observer, packages, null); + } + + /** + * Restore select packages from the given set onto the device, replacing the + * current data of any app contained in the set with the data previously + * backed up. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + * + * @return Zero on success, nonzero on error. The observer will only receive + * progress callbacks if this method returned zero. + * @param token The token from {@link getAvailableRestoreSets()} corresponding to + * the restore set that should be used. + * @param observer If non-null, this binder points to an object that will receive + * progress callbacks during the restore operation. + * @param monitor If non-null, this binder points to an object that will receive + * progress callbacks during the restore operation. + * @param packages The set of packages for which to attempt a restore. Regardless of + * the contents of the actual back-end dataset named by {@code token}, only + * applications mentioned in this list will have their data restored. + * + * @deprecated use {@link RestoreSession#restorePackages(long, RestoreObserver, + * BackupManagerMonitor, Set)} instead. + */ + @Deprecated + public int restoreSome(long token, RestoreObserver observer, BackupManagerMonitor monitor, + String[] packages) { + return restorePackages(token, observer, new HashSet<>(Arrays.asList(packages)), monitor); + } + + /** + * Restore select packages from the given set onto the device, replacing the + * current data of any app contained in the set with the data previously + * backed up. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + * + * @return Zero on success, nonzero on error. The observer will only receive + * progress callbacks if this method returned zero. + * @param token The token from {@link getAvailableRestoreSets()} corresponding to + * the restore set that should be used. + * @param observer If non-null, this binder points to an object that will receive + * progress callbacks during the restore operation. + * @param packages The set of packages for which to attempt a restore. Regardless of + * the contents of the actual back-end dataset named by {@code token}, only + * applications mentioned in this list will have their data restored. + * + * @deprecated use {@link RestoreSession#restorePackages(long, RestoreObserver, Set)} + * instead. + */ + @Deprecated public int restoreSome(long token, RestoreObserver observer, String[] packages) { return restoreSome(token, observer, null, packages); } diff --git a/core/java/android/app/prediction/AppPredictionContext.java b/core/java/android/app/prediction/AppPredictionContext.java index b6f37f6e30c5..298b0031d726 100644 --- a/core/java/android/app/prediction/AppPredictionContext.java +++ b/core/java/android/app/prediction/AppPredictionContext.java @@ -26,7 +26,8 @@ import android.os.Parcel; import android.os.Parcelable; /** - * TODO(b/111701043): Add java docs + * Class that provides contextual information about the environment in which the app prediction is + * used, such as package name, UI in which the app targets are shown, and number of targets. * * @hide */ @@ -57,20 +58,32 @@ public final class AppPredictionContext implements Parcelable { mExtras = parcel.readBundle(); } + /** + * Returns the UI surface of the prediction context. + */ @NonNull public String getUiSurface() { return mUiSurface; } + /** + * Returns the predicted target count + */ public @IntRange(from = 0) int getPredictedTargetCount() { return mPredictedTargetCount; } + /** + * Returns the package name of the prediction context. + */ @NonNull public String getPackageName() { return mPackageName; } + /** + * Returns the extras of the prediction context. + */ @Nullable public Bundle getExtras() { return mExtras; @@ -100,9 +113,6 @@ public final class AppPredictionContext implements Parcelable { dest.writeBundle(mExtras); } - /** - * @see Parcelable.Creator - */ public static final @android.annotation.NonNull Parcelable.Creator<AppPredictionContext> CREATOR = new Parcelable.Creator<AppPredictionContext>() { public AppPredictionContext createFromParcel(Parcel parcel) { @@ -132,7 +142,7 @@ public final class AppPredictionContext implements Parcelable { private Bundle mExtras; /** - * TODO(b/123591863): Add java docs + * @param context The {@link Context} of the prediction client. * * @hide */ diff --git a/core/java/android/app/prediction/AppPredictionManager.java b/core/java/android/app/prediction/AppPredictionManager.java index 45825cf93ae3..cb5b7e7763d9 100644 --- a/core/java/android/app/prediction/AppPredictionManager.java +++ b/core/java/android/app/prediction/AppPredictionManager.java @@ -23,7 +23,8 @@ import android.content.Context; import com.android.internal.util.Preconditions; /** - * TODO (b/111701043) : Add java doc + * Class that provides methods to create prediction clients. + * * @hide */ @SystemApi diff --git a/core/java/android/app/prediction/AppPredictionSessionId.java b/core/java/android/app/prediction/AppPredictionSessionId.java index 1c5d8b49db2c..281a16f7715a 100644 --- a/core/java/android/app/prediction/AppPredictionSessionId.java +++ b/core/java/android/app/prediction/AppPredictionSessionId.java @@ -22,7 +22,7 @@ import android.os.Parcel; import android.os.Parcelable; /** - * TODO (b/111701043) : Add java doc + * The id for an app prediction session. See {@link AppPredictor}. * * @hide */ @@ -33,6 +33,8 @@ public final class AppPredictionSessionId implements Parcelable { private final String mId; /** + * Creates a new id for a prediction session. + * * @hide */ public AppPredictionSessionId(@NonNull String id) { @@ -58,7 +60,6 @@ public final class AppPredictionSessionId implements Parcelable { @Override public int hashCode() { - // Ensure that the id has a consistent hash return mId.hashCode(); } @@ -72,9 +73,6 @@ public final class AppPredictionSessionId implements Parcelable { dest.writeString(mId); } - /** - * @see Parcelable.Creator - */ public static final @android.annotation.NonNull Parcelable.Creator<AppPredictionSessionId> CREATOR = new Parcelable.Creator<AppPredictionSessionId>() { public AppPredictionSessionId createFromParcel(Parcel parcel) { diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java index 284327dac524..3e4e8dc2db72 100644 --- a/core/java/android/app/prediction/AppPredictor.java +++ b/core/java/android/app/prediction/AppPredictor.java @@ -39,7 +39,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; /** - * TODO (b/111701043) : Add java doc + * Class that represents an App Prediction client. * * <p> * Usage: <pre> {@code @@ -49,14 +49,20 @@ import java.util.function.Consumer; * * void onCreate() { * mClient = new AppPredictor(...) + * mClient.registerPredictionUpdates(...) * } * * void onStart() { - * mClient.requestPredictionUpdate(); + * mClient.requestPredictionUpdate() + * } + * + * void onClick(...) { + * mClient.notifyAppTargetEvent(...) * } * * void onDestroy() { - * mClient.close(); + * mClient.unregisterPredictionUpdates() + * mClient.close() * } * * }</pre> @@ -83,7 +89,8 @@ public final class AppPredictor { * The caller should call {@link AppPredictor#destroy()} to dispose the client once it * no longer used. * - * @param predictionContext The prediction context + * @param context The {@link Context} of the user of this {@link AppPredictor}. + * @param predictionContext The prediction context. */ AppPredictor(@NonNull Context context, @NonNull AppPredictionContext predictionContext) { IBinder b = ServiceManager.getService(Context.APP_PREDICTION_SERVICE); @@ -102,6 +109,8 @@ public final class AppPredictor { /** * Notifies the prediction service of an app target event. + * + * @param event The {@link AppTargetEvent} that represents the app target event. */ public void notifyAppTargetEvent(@NonNull AppTargetEvent event) { if (mIsClosed.get()) { @@ -118,6 +127,9 @@ public final class AppPredictor { /** * Notifies the prediction service when the targets in a launch location are shown to the user. + * + * @param launchLocation The launch location where the targets are shown to the user. + * @param targetIds List of {@link AppTargetId}s that are shown to the user. */ public void notifyLocationShown(@NonNull String launchLocation, @NonNull List<AppTargetId> targetIds) { @@ -138,7 +150,10 @@ public final class AppPredictor { * Requests the prediction service provide continuous updates of App predictions via the * provided callback, until the given callback is unregistered. * - * @see Callback#onTargetsAvailable(List) + * @see Callback#onTargetsAvailable(List). + * + * @param callbackExecutor The callback executor to use when calling the callback. + * @param callback The Callback to be called when updates of App predictions are available. */ public void registerPredictionUpdates(@NonNull @CallbackExecutor Executor callbackExecutor, @NonNull AppPredictor.Callback callback) { @@ -164,6 +179,10 @@ public final class AppPredictor { /** * Requests the prediction service to stop providing continuous updates to the provided * callback until the callback is re-registered. + * + * @see {@link AppPredictor#registerPredictionUpdates(Executor, Callback)}. + * + * @param callback The callback to be unregistered. */ public void unregisterPredictionUpdates(@NonNull AppPredictor.Callback callback) { if (mIsClosed.get()) { @@ -187,7 +206,7 @@ public final class AppPredictor { * Requests the prediction service to dispatch a new set of App predictions via the provided * callback. * - * @see Callback#onTargetsAvailable(List) + * @see Callback#onTargetsAvailable(List). */ public void requestPredictionUpdate() { if (mIsClosed.get()) { @@ -205,6 +224,10 @@ public final class AppPredictor { /** * Returns a new list of AppTargets sorted based on prediction rank or {@code null} if the * ranker is not available. + * + * @param targets List of app targets to be sorted. + * @param callbackExecutor The callback executor to use when calling the callback. + * @param callback The callback to return the sorted list of app targets. */ @Nullable public void sortTargets(@NonNull List<AppTarget> targets, @@ -255,7 +278,7 @@ public final class AppPredictor { } /** - * TODO(b/123591863): Add java docs + * Returns the id of this prediction session. * * @hide */ @@ -271,7 +294,7 @@ public final class AppPredictor { /** * Called when a new set of predicted app targets are available. - * @param targets Sorted list of predicted targets + * @param targets Sorted list of predicted targets. */ void onTargetsAvailable(@NonNull List<AppTarget> targets); } diff --git a/core/java/android/app/prediction/AppTarget.java b/core/java/android/app/prediction/AppTarget.java index 6f09d344c13f..bb1b96ce5f00 100644 --- a/core/java/android/app/prediction/AppTarget.java +++ b/core/java/android/app/prediction/AppTarget.java @@ -29,6 +29,7 @@ import com.android.internal.util.Preconditions; /** * A representation of a launchable target. + * * @hide */ @SystemApi @@ -45,7 +46,12 @@ public final class AppTarget implements Parcelable { private int mRank; /** - * TODO(b/123591863): Add java docs + * Creates an instance of AppTarget that represent a launchable component. + * + * @param id A unique id for this launchable target. + * @param packageName Package name of the target. + * @param className Class name of the target. + * @param user The UserHandle of the user which this target belongs to. * * @hide */ @@ -62,7 +68,11 @@ public final class AppTarget implements Parcelable { } /** - * TODO(b/123591863): Add java docs + * Creates an instance of AppTarget that represent a launchable shortcut. + * + * @param id A unique id for this launchable target. + * @param shortcutInfo The {@link ShortcutInfo} that is represented with this target. + * @param className Class name fo the target. * * @hide */ @@ -186,9 +196,6 @@ public final class AppTarget implements Parcelable { dest.writeInt(mRank); } - /** - * @see Parcelable.Creator - */ public static final @android.annotation.NonNull Parcelable.Creator<AppTarget> CREATOR = new Parcelable.Creator<AppTarget>() { public AppTarget createFromParcel(Parcel parcel) { diff --git a/core/java/android/app/prediction/AppTargetEvent.java b/core/java/android/app/prediction/AppTargetEvent.java index f6964f3f4236..54b95639f68f 100644 --- a/core/java/android/app/prediction/AppTargetEvent.java +++ b/core/java/android/app/prediction/AppTargetEvent.java @@ -28,6 +28,7 @@ import java.lang.annotation.RetentionPolicy; /** * A representation of an app target event. + * * @hide */ @SystemApi @@ -118,9 +119,6 @@ public final class AppTargetEvent implements Parcelable { dest.writeInt(mAction); } - /** - * @see Creator - */ public static final @android.annotation.NonNull Creator<AppTargetEvent> CREATOR = new Creator<AppTargetEvent>() { public AppTargetEvent createFromParcel(Parcel parcel) { @@ -134,6 +132,7 @@ public final class AppTargetEvent implements Parcelable { /** * A builder for app target events. + * * @hide */ @SystemApi @@ -143,6 +142,10 @@ public final class AppTargetEvent implements Parcelable { private String mLocation; private @ActionType int mAction; + /** + * @param target The app target that is associated with this event. + * @param actionType The event type, which is one of the values in {@link ActionType}. + */ public Builder(@Nullable AppTarget target, @ActionType int actionType) { mTarget = target; mAction = actionType; diff --git a/core/java/android/app/prediction/AppTargetId.java b/core/java/android/app/prediction/AppTargetId.java index aa2ec1fc3a62..3603f5f3ab10 100644 --- a/core/java/android/app/prediction/AppTargetId.java +++ b/core/java/android/app/prediction/AppTargetId.java @@ -22,7 +22,8 @@ import android.os.Parcel; import android.os.Parcelable; /** - * The id for a prediction target. + * The id for a prediction target. See {@link AppTarget}. + * * @hide */ @SystemApi @@ -33,7 +34,7 @@ public final class AppTargetId implements Parcelable { private final String mId; /** - * TODO(b/123591863): Add java docs + * Creates a new id for a prediction target. * * @hide */ @@ -49,6 +50,7 @@ public final class AppTargetId implements Parcelable { /** * Returns the id. + * * @hide */ @NonNull @@ -66,7 +68,6 @@ public final class AppTargetId implements Parcelable { @Override public int hashCode() { - // Ensure that the id has a consistent hash return mId.hashCode(); } @@ -80,9 +81,6 @@ public final class AppTargetId implements Parcelable { dest.writeString(mId); } - /** - * @see Creator - */ public static final @android.annotation.NonNull Creator<AppTargetId> CREATOR = new Creator<AppTargetId>() { public AppTargetId createFromParcel(Parcel parcel) { diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java index b4eaab20c5bc..d6edb90ca27b 100644 --- a/core/java/android/bluetooth/BluetoothHearingAid.java +++ b/core/java/android/bluetooth/BluetoothHearingAid.java @@ -22,7 +22,6 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -92,7 +91,6 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -324,7 +322,8 @@ public final class BluetoothHearingAid implements BluetoothProfile { /** * {@inheritDoc} */ - @Override public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates( + @Override + public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates( @NonNull int[] states) { if (VDBG) log("getDevicesMatchingStates()"); try { @@ -346,7 +345,8 @@ public final class BluetoothHearingAid implements BluetoothProfile { * {@inheritDoc} */ @Override - public int getConnectionState(@NonNull BluetoothDevice device) { + public @BluetoothProfile.BtProfileState int getConnectionState( + @NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); try { mServiceLock.readLock().lock(); @@ -386,7 +386,6 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @UnsupportedAppUsage public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); try { @@ -418,7 +417,6 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH) - @UnsupportedAppUsage public List<BluetoothDevice> getActiveDevices() { if (VDBG) log("getActiveDevices()"); try { diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index ef775967f8a7..dabe0fdac39a 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -276,7 +276,7 @@ public interface BluetoothProfile { * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} */ @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionState(BluetoothDevice device); + @BtProfileState int getConnectionState(BluetoothDevice device); /** * An interface for notifying BluetoothProfile IPC clients when they have diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index efb3bfc1f6d2..fb933b1a7163 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -338,6 +338,16 @@ public abstract class Context { public static final int BIND_ADJUST_BELOW_PERCEPTIBLE = 0x0100; /** + * Flag for {@link #bindService}: This flag is intended to be used only by the system to adjust + * the scheduling policy for IMEs (and any other out-of-process user-visible components that + * work closely with the top app) so that UI hosted in such services can have the same + * scheduling policy (e.g. SCHED_FIFO when it is enabled and TOP_APP_PRIORITY_BOOST otherwise) + * as the actual top-app. + * @hide + */ + public static final int BIND_SCHEDULE_LIKE_TOP_APP = 0x00080000; + + /** * Flag for {@link #bindService}: allow background activity starts from the bound service's * process. * This flag is only respected if the caller is holding @@ -3693,14 +3703,6 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve a - * {@link android.net.IpMemoryStore} to store and read information about - * known networks. - * @hide - */ - public static final String IP_MEMORY_STORE_SERVICE = "ipmemorystore"; - - /** - * Use with {@link #getSystemService(String)} to retrieve a * {@link android.net.IpSecManager} for encrypting Sockets or Networks with * IPSec. * @@ -4006,6 +4008,16 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve a + * {@link com.android.server.attention.AttentionManagerService} for attention services. + * + * @see #getSystemService(String) + * @see android.server.attention.AttentionManagerService + * @hide + */ + public static final String ATTENTION_SERVICE = "attention"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a * {@link android.view.inputmethod.InputMethodManager} for accessing input * methods. * diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index a2d3f6af2564..efd9990f4ade 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1997,7 +1997,8 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_LAUNCHER_EXTRAS = "android.intent.extra.LAUNCHER_EXTRAS"; /** - * Intent extra: ID of the shortcut used to send the share intent. + * Intent extra: ID of the shortcut used to send the share intent. Will be sent with + * {@link #ACTION_SEND}. * * @see ShortcutInfo#getId() * diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 2a19763321f2..954deac97d6d 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -142,17 +142,6 @@ public class LauncherApps { public static final String EXTRA_PIN_ITEM_REQUEST = "android.content.pm.extra.PIN_ITEM_REQUEST"; - /** - * Metadata key that specifies vouched certs, so any apps signed by a cert in vouched certs - * will not show hidden icon in launcher even it does not have a launcher visible activity. - * - * If an app has this metadata in manifest, it won't be eligible to hide its icon even if its - * cert is in vouched certs list. - * - * @hide - */ - public static final String VOUCHED_CERTS_KEY = "vouched_certs"; - private final Context mContext; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private final ILauncherApps mService; diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index 7b61807f9684..1f82fa6b57e3 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -1270,8 +1270,8 @@ public final class ShortcutInfo implements Parcelable { * system services even after it has been unpublished as a dynamic shortcut. */ @NonNull - public Builder setLongLived() { - mIsLongLived = true; + public Builder setLongLived(boolean londLived) { + mIsLongLived = londLived; return this; } diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java index 6ea35f01add0..bae0fd3ad3b9 100644 --- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java @@ -315,7 +315,7 @@ public interface BiometricFaceConstants { /** * The sensor is dirty. The user should be informed to clean the sensor. */ - public static final int SENSOR_DIRTY = 21; + public static final int FACE_ACQUIRED_SENSOR_DIRTY = 21; /** * Hardware vendors may extend this list if there are conditions that do not fall under one of diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index e751b2c77dbb..08035972a0db 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -523,6 +523,11 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * cancelled notification through {@link AuthenticationCallback#onAuthenticationError(int, * CharSequence)}. * + * Note: Applications generally should not cancel and start authentication in quick succession. + * For example, to properly handle authentication across configuration changes, it's recommended + * to use BiometricPrompt in a fragment with setRetainInstance(true). By doing so, the + * application will not need to cancel/restart authentication during the configuration change. + * * @throws IllegalArgumentException If any of the arguments are null * * @param crypto Object associated with the call @@ -568,6 +573,11 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * authentication. The interrupted client will receive a cancelled notification through {@link * AuthenticationCallback#onAuthenticationError(int, CharSequence)}. * + * Note: Applications generally should not cancel and start authentication in quick succession. + * For example, to properly handle authentication across configuration changes, it's recommended + * to use BiometricPrompt in a fragment with setRetainInstance(true). By doing so, the + * application will not need to cancel/restart authentication during the configuration change. + * * @throws IllegalArgumentException If any of the arguments are null * * @param cancel An object that can be used to cancel authentication diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 7ae673c1eded..c39796b881ca 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -336,6 +336,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <li>{@link RecommendedStreamConfigurationMap#USECASE_SNAPSHOT}</li> * <li>{@link RecommendedStreamConfigurationMap#USECASE_RAW}</li> * <li>{@link RecommendedStreamConfigurationMap#USECASE_ZSL}</li> + * <li>{@link RecommendedStreamConfigurationMap#USECASE_LOW_LATENCY_SNAPSHOT}</li> * </ul> * </p> * @@ -400,7 +401,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri public @Nullable RecommendedStreamConfigurationMap getRecommendedStreamConfigurationMap( @RecommendedStreamConfigurationMap.RecommendedUsecase int usecase) { if (((usecase >= RecommendedStreamConfigurationMap.USECASE_PREVIEW) && - (usecase <= RecommendedStreamConfigurationMap.USECASE_RAW)) || + (usecase <= RecommendedStreamConfigurationMap.USECASE_LOW_LATENCY_SNAPSHOT)) || ((usecase >= RecommendedStreamConfigurationMap.USECASE_VENDOR_START) && (usecase < RecommendedStreamConfigurationMap.MAX_USECASE_COUNT))) { if (mRecommendedConfigurations == null) { diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 1cdf23530ddc..e909c0075f38 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -1113,8 +1113,10 @@ public class CameraMetadataNative implements Parcelable { depthStreamDurationList.get(i), depthStreamStallList.get(i), depthScData); } - if ((scData.streamConfigurationArray == null) && - (depthScData.streamConfigurationArray == null)) { + if ((scData.streamConfigurationArray == null || + scData.streamConfigurationArray.length == 0) && + (depthScData.streamConfigurationArray == null || + depthScData.streamConfigurationArray.length == 0)) { recommendedConfigurations.add(null); continue; } @@ -1125,6 +1127,7 @@ public class CameraMetadataNative implements Parcelable { switch (i) { case RecommendedStreamConfigurationMap.USECASE_PREVIEW: case RecommendedStreamConfigurationMap.USECASE_RAW: + case RecommendedStreamConfigurationMap.USECASE_LOW_LATENCY_SNAPSHOT: case RecommendedStreamConfigurationMap.USECASE_VIDEO_SNAPSHOT: map = new StreamConfigurationMap(scData.streamConfigurationArray, scData.minDurationArray, scData.stallDurationArray, diff --git a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java index 068c0ce8d052..2d725989af17 100644 --- a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java +++ b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java @@ -21,6 +21,7 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.ImageFormat; +import android.graphics.ImageFormat.Format; import android.graphics.PixelFormat; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; @@ -137,6 +138,17 @@ public final class RecommendedStreamConfigurationMap { public static final int USECASE_RAW = 0x5; /** + * The recommended stream configuration map for use case low latency snapshot must contain + * subset of configurations with end-to-end latency that does not exceed 200 ms. under standard + * operating conditions (reasonable light levels, not loaded system). The expected output format + * will be primarily {@link android.graphics.ImageFormat#JPEG} however other image formats can + * be present as well. Even if available for the camera device, high speed and input + * configurations will be absent. This suggested configuration map may be absent on some devices + * that can not support any low latency requests. + */ + public static final int USECASE_LOW_LATENCY_SNAPSHOT = 0x6; + + /** * Device specific use cases. * @hide */ @@ -150,7 +162,8 @@ public final class RecommendedStreamConfigurationMap { USECASE_VIDEO_SNAPSHOT, USECASE_SNAPSHOT, USECASE_ZSL, - USECASE_RAW }) + USECASE_RAW, + USECASE_LOW_LATENCY_SNAPSHOT}) public @interface RecommendedUsecase {}; /** @@ -214,7 +227,7 @@ public final class RecommendedStreamConfigurationMap { * * @return a non-modifiable set of Integer formats */ - public @Nullable Set<Integer> getValidOutputFormatsForInput(int inputFormat) { + public @Nullable Set<Integer> getValidOutputFormatsForInput(@Format int inputFormat) { return getUnmodifiableIntegerSet(mRecommendedMap.getValidOutputFormatsForInput( inputFormat)); } @@ -250,7 +263,7 @@ public final class RecommendedStreamConfigurationMap { * @param format a format from {@link #getInputFormats} * @return a non-modifiable set of sizes, or {@code null} if the format was not available. */ - public @Nullable Set<Size> getInputSizes(int format) { + public @Nullable Set<Size> getInputSizes(@Format int format) { return getUnmodifiableSizeSet(mRecommendedMap.getInputSizes(format)); } @@ -272,7 +285,7 @@ public final class RecommendedStreamConfigurationMap { * if the image format was not a defined named constant * from either {@link ImageFormat} or {@link PixelFormat} */ - public boolean isOutputSupportedFor(int format) { + public boolean isOutputSupportedFor(@Format int format) { return mRecommendedMap.isOutputSupportedFor(format); } @@ -288,7 +301,7 @@ public final class RecommendedStreamConfigurationMap { * @return a non-modifiable set of supported sizes, * or {@code null} if the {@code format} is not a supported output */ - public @Nullable Set<Size> getOutputSizes(int format) { + public @Nullable Set<Size> getOutputSizes(@Format int format) { return getUnmodifiableSizeSet(mRecommendedMap.getOutputSizes(format)); } @@ -372,7 +385,7 @@ public final class RecommendedStreamConfigurationMap { * @return a non-modifiable set of supported slower high-resolution sizes, or {@code null} if * the BURST_CAPTURE capability is not supported */ - public @Nullable Set<Size> getHighResolutionOutputSizes(int format) { + public @Nullable Set<Size> getHighResolutionOutputSizes(@Format int format) { return getUnmodifiableSizeSet(mRecommendedMap.getHighResolutionOutputSizes(format)); } @@ -392,7 +405,8 @@ public final class RecommendedStreamConfigurationMap { * * @throws IllegalArgumentException if {@code format} or {@code size} was not supported */ - public @IntRange(from = 0) long getOutputMinFrameDuration(int format, @NonNull Size size) { + public @IntRange(from = 0) long getOutputMinFrameDuration(@Format int format, + @NonNull Size size) { return mRecommendedMap.getOutputMinFrameDuration(format, size); } @@ -409,7 +423,7 @@ public final class RecommendedStreamConfigurationMap { * * @throws IllegalArgumentException if {@code format} or {@code size} was not supported */ - public @IntRange(from = 0) long getOutputStallDuration(int format, @NonNull Size size) { + public @IntRange(from = 0) long getOutputStallDuration(@Format int format, @NonNull Size size) { return mRecommendedMap.getOutputStallDuration(format, size); } @@ -425,7 +439,7 @@ public final class RecommendedStreamConfigurationMap { * a non-modifiable set of supported sizes for {@link ImageFormat#PRIVATE} format, * or {@code null} if the {@code klass} is not a supported output. */ - public <T> @Nullable Set<Size> getOutputSizes(@NonNull Class<T> klass) { + public @Nullable <T> Set<Size> getOutputSizes(@NonNull Class<T> klass) { if (mSupportsPrivate) { return getUnmodifiableSizeSet(mRecommendedMap.getOutputSizes(klass)); } @@ -448,7 +462,7 @@ public final class RecommendedStreamConfigurationMap { * * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported */ - public <T> @IntRange(from = 0) long getOutputMinFrameDuration(@NonNull final Class<T> klass, + public @IntRange(from = 0) <T> long getOutputMinFrameDuration(@NonNull final Class<T> klass, @NonNull final Size size) { if (mSupportsPrivate) { return mRecommendedMap.getOutputMinFrameDuration(klass, size); @@ -471,7 +485,7 @@ public final class RecommendedStreamConfigurationMap { * * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported */ - public <T> @IntRange(from = 0) long getOutputStallDuration(@NonNull final Class<T> klass, + public @IntRange(from = 0) <T> long getOutputStallDuration(@NonNull final Class<T> klass, @NonNull final Size size) { if (mSupportsPrivate) { return mRecommendedMap.getOutputStallDuration(klass, size); diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java index 7fa1cfb7438e..21fcc63c76fb 100644 --- a/core/java/android/hardware/display/BrightnessChangeEvent.java +++ b/core/java/android/hardware/display/BrightnessChangeEvent.java @@ -80,15 +80,23 @@ public final class BrightnessChangeEvent implements Parcelable { * Histogram counting how many times a pixel of a given value was displayed onscreen for the * Value component of HSV if the device supports color sampling, if the device does not support * color sampling the value will be null. + * * The buckets of the histogram are evenly weighted, the number of buckets is device specific. - * For example if we had {10, 6, 4, 1} this means that 10 pixels were in the range - * [0x00,0x3f], 6 pixels were in the range [0x40,0x7f] etc. + * The units are in pixels * milliseconds, with 1 pixel millisecond being 1 pixel displayed + * for 1 millisecond. + * For example if we had {100, 50, 30, 20}, value component was onscreen for 100 pixel + * milliseconds in range 0x00->0x3F, 30 pixel milliseconds in range 0x40->0x7F, etc. + * + * {@see #colorSampleDuration} */ @Nullable public final long[] colorValueBuckets; /** - * How many milliseconds of data are contained in the colorValueBuckets. + * How many milliseconds of data are contained in the colorValueBuckets, if the device does + * not support color sampling the value will be 0L. + * + * {@see #colorValueBuckets} */ public final long colorSampleDuration; @@ -283,7 +291,8 @@ public final class BrightnessChangeEvent implements Parcelable { return this; } - /** {@see BrightnessChangeEvent#valueBuckets} */ + /** {@see BrightnessChangeEvent#colorValueBuckets} + * {@see BrightnessChangeEvent#colorSampleDuration} */ public Builder setColorValues(@NonNull long[] colorValueBuckets, long colorSampleDuration) { Objects.requireNonNull(colorValueBuckets); mColorValueBuckets = colorValueBuckets; diff --git a/core/java/android/hardware/display/DisplayedContentSample.java b/core/java/android/hardware/display/DisplayedContentSample.java index 0610377c648a..4a429bb33fcb 100644 --- a/core/java/android/hardware/display/DisplayedContentSample.java +++ b/core/java/android/hardware/display/DisplayedContentSample.java @@ -30,12 +30,14 @@ public final class DisplayedContentSample { * Construct an object representing a color histogram of pixels that were displayed on screen. * * @param numFrames The number of frames represented by this sample. - * @param mSamplesComponent0 is a histogram counting how many times a pixel of a given value - * was displayed onscreen for FORMAT_COMPONENT_0. The buckets of the histogram are evenly - * weighted, the number of buckets is device specific. - * eg, for RGBA_8888, if sampleComponent0 is {10, 6, 4, 1} this means that 10 red pixels were - * displayed onscreen in range 0x00->0x3F, 6 red pixels were displayed onscreen in range - * 0x40->0x7F, etc. + * @param mSamplesComponent0 is a histogram counting how many times and for how long a pixel + * of a given value was displayed onscreen for FORMAT_COMPONENT_0. The buckets of the + * histogram are evenly weighted, the number of buckets is device specific. + * The units are in pixels * milliseconds, with 1 pixel millisecond being 1 pixel displayed + * onscreen for 1ms. + * eg, for RGBA_8888, if sampleComponent0 is {100, 50, 30, 20}, then red component was + * onscreen for 100 pixel milliseconds in range 0x00->0x3F, 30 pixel milliseconds in + * range 0x40->0x7F, etc. * @param mSamplesComponent1 is the same sample definition as sampleComponent0, but for the * second component of format. * @param mSamplesComponent2 is the same sample definition as sampleComponent0, but for the diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 139a5ee09dd8..3e8c334e8aa1 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -634,6 +634,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan return context.getString(R.string.face_acquired_obscured); case FACE_ACQUIRED_START: return null; + case FACE_ACQUIRED_SENSOR_DIRTY: + return context.getString(R.string.face_acquired_sensor_dirty); case FACE_ACQUIRED_VENDOR: { String[] msgArray = context.getResources().getStringArray( R.array.face_acquired_vendor); diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index d08379fab047..e5802c23eb6f 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -2816,23 +2816,6 @@ public class ConnectivityManager { } /** - * @removed - * @deprecated This API would be removed when all of caller has been updated. - * */ - @Deprecated - public abstract static class TetheringEntitlementValueListener { - /** - * Called to notify entitlement result. - * - * @param resultCode a int value of entitlement result. It may be one of - * {@link #TETHER_ERROR_NO_ERROR}, - * {@link #TETHER_ERROR_PROVISION_FAILED}, or - * {@link #TETHER_ERROR_ENTITLEMENT_UNKONWN}. - */ - public void onEntitlementResult(int resultCode) {} - } - - /** * Get the last value of the entitlement check on this downstream. If the cached value is * {@link #TETHER_ERROR_NO_ERROR} or showEntitlementUi argument is false, it just return the * cached value. Otherwise, a UI-based entitlement check would be performed. It is not @@ -2878,31 +2861,6 @@ public class ConnectivityManager { } /** - * @removed - * @deprecated This API would be removed when all of caller has been updated. - * */ - @Deprecated - public void getLatestTetheringEntitlementValue(int type, boolean showEntitlementUi, - @NonNull final TetheringEntitlementValueListener listener, @Nullable Handler handler) { - Preconditions.checkNotNull(listener, "TetheringEntitlementValueListener cannot be null."); - ResultReceiver wrappedListener = new ResultReceiver(handler) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - listener.onEntitlementResult(resultCode); - } - }; - - try { - String pkgName = mContext.getOpPackageName(); - Log.i(TAG, "getLatestTetheringEntitlementValue:" + pkgName); - mService.getLatestTetheringEntitlementResult(type, wrappedListener, - showEntitlementUi, pkgName); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** * Report network connectivity status. This is currently used only * to alter status bar UI. * <p>This method requires the caller to hold the permission diff --git a/core/java/android/net/DnsPacket.java b/core/java/android/net/DnsPacket.java index 0ac02b1b7b37..83e57e0a047b 100644 --- a/core/java/android/net/DnsPacket.java +++ b/core/java/android/net/DnsPacket.java @@ -71,7 +71,7 @@ public abstract class DnsPacket { } /** - * It's used both for DNS questions and DNS resource records. + * Superclass for DNS questions and DNS resource records. * * DNS questions (No TTL/RDATA) * DNS resource records (With TTL/RDATA) @@ -96,12 +96,13 @@ public abstract class DnsPacket { /** * Create a new DnsRecord from a positioned ByteBuffer. * - * @param ByteBuffer input of record, must be in network byte order - * (which is the default). * Reads the passed ByteBuffer from its current position and decodes a DNS record. * When this constructor returns, the reading position of the ByteBuffer has been * advanced to the end of the DNS header record. * This is meant to chain with other methods reading a DNS response in sequence. + * + * @param ByteBuffer input of record, must be in network byte order + * (which is the default). */ DnsRecord(int recordType, @NonNull ByteBuffer buf) throws BufferUnderflowException, ParseException { @@ -205,16 +206,6 @@ public abstract class DnsPacket { protected final DnsHeader mHeader; protected final List<DnsRecord>[] mRecords; - public static class ParseException extends Exception { - public ParseException(String msg) { - super(msg); - } - - public ParseException(String msg, Throwable cause) { - super(msg, cause); - } - } - protected DnsPacket(@NonNull byte[] data) throws ParseException { if (null == data) throw new ParseException("Parse header failed, null input data"); final ByteBuffer buffer; diff --git a/core/java/android/net/DnsResolver.java b/core/java/android/net/DnsResolver.java index d3bc3e66fbee..93b8cf801d45 100644 --- a/core/java/android/net/DnsResolver.java +++ b/core/java/android/net/DnsResolver.java @@ -22,11 +22,11 @@ import static android.net.NetworkUtils.resNetworkSend; import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR; import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.os.Handler; -import android.os.MessageQueue; +import android.os.Looper; import android.system.ErrnoException; import android.util.Log; @@ -37,8 +37,7 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; -import java.util.function.Consumer; - +import java.util.concurrent.Executor; /** * Dns resolver class for asynchronous dns querying @@ -81,66 +80,137 @@ public final class DnsResolver { public static final int FLAG_NO_CACHE_STORE = 1 << 1; public static final int FLAG_NO_CACHE_LOOKUP = 1 << 2; - private static final int DNS_RAW_RESPONSE = 1; - private static final int NETID_UNSET = 0; private static final DnsResolver sInstance = new DnsResolver(); /** - * listener for receiving raw answers + * Get instance for DnsResolver + */ + public static @NonNull DnsResolver getInstance() { + return sInstance; + } + + private DnsResolver() {} + + /** + * Answer parser for parsing raw answers + * + * @param <T> The type of the parsed answer */ - public interface RawAnswerListener { + public interface AnswerParser<T> { /** - * {@code byte[]} is {@code null} if query timed out + * Creates a <T> answer by parsing the given raw answer. + * + * @param rawAnswer the raw answer to be parsed + * @return a parsed <T> answer + * @throws ParseException if parsing failed */ - void onAnswer(@Nullable byte[] answer); + @NonNull T parse(@NonNull byte[] rawAnswer) throws ParseException; } /** - * listener for receiving parsed answers + * Base class for answer callbacks + * + * @param <T> The type of the parsed answer */ - public interface InetAddressAnswerListener { + public abstract static class AnswerCallback<T> { + /** @hide */ + public final AnswerParser<T> parser; + + public AnswerCallback(@NonNull AnswerParser<T> parser) { + this.parser = parser; + }; + /** - * Will be called exactly once with all the answers to the query. - * size of addresses will be zero if no available answer could be parsed. + * Success response to + * {@link android.net.DnsResolver#query query()}. + * + * Invoked when the answer to a query was successfully parsed. + * + * @param answer parsed answer to the query. + * + * {@see android.net.DnsResolver#query query()} */ - void onAnswer(@NonNull List<InetAddress> addresses); + public abstract void onAnswer(@NonNull T answer); + + /** + * Error response to + * {@link android.net.DnsResolver#query query()}. + * + * Invoked when there is no valid answer to + * {@link android.net.DnsResolver#query query()} + * + * @param exception a {@link ParseException} object with additional + * detail regarding the failure + */ + public abstract void onParseException(@NonNull ParseException exception); + + /** + * Error response to + * {@link android.net.DnsResolver#query query()}. + * + * Invoked if an error happens when + * issuing the DNS query or receiving the result. + * {@link android.net.DnsResolver#query query()} + * + * @param exception an {@link ErrnoException} object with additional detail + * regarding the failure + */ + public abstract void onQueryException(@NonNull ErrnoException exception); } /** - * Get instance for DnsResolver + * Callback for receiving raw answers */ - public static DnsResolver getInstance() { - return sInstance; + public abstract static class RawAnswerCallback extends AnswerCallback<byte[]> { + public RawAnswerCallback() { + super(rawAnswer -> rawAnswer); + } } - private DnsResolver() {} + /** + * Callback for receiving parsed {@link InetAddress} answers + * + * Note that if the answer does not contain any IP addresses, + * onAnswer will be called with an empty list. + */ + public abstract static class InetAddressAnswerCallback + extends AnswerCallback<List<InetAddress>> { + public InetAddressAnswerCallback() { + super(rawAnswer -> new DnsAddressAnswer(rawAnswer).getAddresses()); + } + } /** - * Pass in a blob and corresponding setting, - * get a blob back asynchronously with the entire raw answer. + * Send a raw DNS query. + * The answer will be provided asynchronously through the provided {@link AnswerCallback}. * * @param network {@link Network} specifying which network for querying. * {@code null} for query on default network. * @param query blob message * @param flags flags as a combination of the FLAGS_* constants - * @param handler {@link Handler} to specify the thread - * upon which the {@link RawAnswerListener} will be invoked. - * @param listener a {@link RawAnswerListener} which will be called to notify the caller + * @param executor The {@link Executor} that the callback should be executed on. + * @param callback an {@link AnswerCallback} which will be called to notify the caller * of the result of dns query. */ - public void query(@Nullable Network network, @NonNull byte[] query, @QueryFlag int flags, - @NonNull Handler handler, @NonNull RawAnswerListener listener) throws ErrnoException { - final FileDescriptor queryfd = resNetworkSend((network != null + public <T> void query(@Nullable Network network, @NonNull byte[] query, @QueryFlag int flags, + @NonNull @CallbackExecutor Executor executor, @NonNull AnswerCallback<T> callback) { + final FileDescriptor queryfd; + try { + queryfd = resNetworkSend((network != null ? network.netId : NETID_UNSET), query, query.length, flags); - registerFDListener(handler.getLooper().getQueue(), queryfd, - answerbuf -> listener.onAnswer(answerbuf)); + } catch (ErrnoException e) { + callback.onQueryException(e); + return; + } + + registerFDListener(executor, queryfd, callback); } /** - * Pass in a domain name and corresponding setting, - * get a blob back asynchronously with the entire raw answer. + * Send a DNS query with the specified name, class and query type. + * The answer will be provided asynchronously through the provided {@link AnswerCallback}. * * @param network {@link Network} specifying which network for querying. * {@code null} for query on default network. @@ -148,74 +218,53 @@ public final class DnsResolver { * @param nsClass dns class as one of the CLASS_* constants * @param nsType dns resource record (RR) type as one of the TYPE_* constants * @param flags flags as a combination of the FLAGS_* constants - * @param handler {@link Handler} to specify the thread - * upon which the {@link RawAnswerListener} will be invoked. - * @param listener a {@link RawAnswerListener} which will be called to notify the caller + * @param executor The {@link Executor} that the callback should be executed on. + * @param callback an {@link AnswerCallback} which will be called to notify the caller * of the result of dns query. */ - public void query(@Nullable Network network, @NonNull String domain, @QueryClass int nsClass, - @QueryType int nsType, @QueryFlag int flags, - @NonNull Handler handler, @NonNull RawAnswerListener listener) throws ErrnoException { - final FileDescriptor queryfd = resNetworkQuery((network != null - ? network.netId : NETID_UNSET), domain, nsClass, nsType, flags); - registerFDListener(handler.getLooper().getQueue(), queryfd, - answerbuf -> listener.onAnswer(answerbuf)); - } - - /** - * Pass in a domain name and corresponding setting, - * get back a set of InetAddresses asynchronously. - * - * @param network {@link Network} specifying which network for querying. - * {@code null} for query on default network. - * @param domain domain name for querying - * @param flags flags as a combination of the FLAGS_* constants - * @param handler {@link Handler} to specify the thread - * upon which the {@link InetAddressAnswerListener} will be invoked. - * @param listener an {@link InetAddressAnswerListener} which will be called to - * notify the caller of the result of dns query. - * - */ - public void query(@Nullable Network network, @NonNull String domain, @QueryFlag int flags, - @NonNull Handler handler, @NonNull InetAddressAnswerListener listener) - throws ErrnoException { - final FileDescriptor v4fd = resNetworkQuery((network != null - ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_A, flags); - final FileDescriptor v6fd = resNetworkQuery((network != null - ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_AAAA, flags); - - final InetAddressAnswerAccumulator accmulator = - new InetAddressAnswerAccumulator(2, listener); - final Consumer<byte[]> consumer = answerbuf -> - accmulator.accumulate(parseAnswers(answerbuf)); - - registerFDListener(handler.getLooper().getQueue(), v4fd, consumer); - registerFDListener(handler.getLooper().getQueue(), v6fd, consumer); + public <T> void query(@Nullable Network network, @NonNull String domain, + @QueryClass int nsClass, @QueryType int nsType, @QueryFlag int flags, + @NonNull @CallbackExecutor Executor executor, @NonNull AnswerCallback<T> callback) { + final FileDescriptor queryfd; + try { + queryfd = resNetworkQuery((network != null + ? network.netId : NETID_UNSET), domain, nsClass, nsType, flags); + } catch (ErrnoException e) { + callback.onQueryException(e); + return; + } + registerFDListener(executor, queryfd, callback); } - private void registerFDListener(@NonNull MessageQueue queue, - @NonNull FileDescriptor queryfd, @NonNull Consumer<byte[]> answerConsumer) { - queue.addOnFileDescriptorEventListener( + private <T> void registerFDListener(@NonNull Executor executor, + @NonNull FileDescriptor queryfd, @NonNull AnswerCallback<T> answerCallback) { + Looper.getMainLooper().getQueue().addOnFileDescriptorEventListener( queryfd, FD_EVENTS, (fd, events) -> { - byte[] answerbuf = null; - try { - // TODO: Implement result function in Java side instead of using JNI - // Because JNI method close fd prior than unregistering fd on - // event listener. - answerbuf = resNetworkResult(fd); - } catch (ErrnoException e) { - Log.e(TAG, "resNetworkResult:" + e.toString()); - } - answerConsumer.accept(answerbuf); + executor.execute(() -> { + byte[] answerbuf = null; + try { + answerbuf = resNetworkResult(fd); + } catch (ErrnoException e) { + Log.e(TAG, "resNetworkResult:" + e.toString()); + answerCallback.onQueryException(e); + return; + } + try { + answerCallback.onAnswer( + answerCallback.parser.parse(answerbuf)); + } catch (ParseException e) { + answerCallback.onParseException(e); + } + }); // Unregister this fd listener return 0; }); } - private class DnsAddressAnswer extends DnsPacket { + private static class DnsAddressAnswer extends DnsPacket { private static final String TAG = "DnsResolver.DnsAddressAnswer"; private static final boolean DBG = false; @@ -226,12 +275,6 @@ public final class DnsResolver { if ((mHeader.flags & (1 << 15)) == 0) { throw new ParseException("Not an answer packet"); } - if (mHeader.rcode != 0) { - throw new ParseException("Response error, rcode:" + mHeader.rcode); - } - if (mHeader.getRecordCount(ANSECTION) == 0) { - throw new ParseException("No available answer"); - } if (mHeader.getRecordCount(QDSECTION) == 0) { throw new ParseException("No question found"); } @@ -241,6 +284,8 @@ public final class DnsResolver { public @NonNull List<InetAddress> getAddresses() { final List<InetAddress> results = new ArrayList<InetAddress>(); + if (mHeader.getRecordCount(ANSECTION) == 0) return results; + for (final DnsRecord ansSec : mRecords[ANSECTION]) { // Only support A and AAAA, also ignore answers if query type != answer type. int nsType = ansSec.nsType; @@ -259,34 +304,4 @@ public final class DnsResolver { } } - private @Nullable List<InetAddress> parseAnswers(@Nullable byte[] data) { - try { - return (data == null) ? null : new DnsAddressAnswer(data).getAddresses(); - } catch (DnsPacket.ParseException e) { - Log.e(TAG, "Parse answer fail " + e.getMessage()); - return null; - } - } - - private class InetAddressAnswerAccumulator { - private final List<InetAddress> mAllAnswers; - private final InetAddressAnswerListener mAnswerListener; - private final int mTargetAnswerCount; - private int mReceivedAnswerCount = 0; - - InetAddressAnswerAccumulator(int size, @NonNull InetAddressAnswerListener listener) { - mTargetAnswerCount = size; - mAllAnswers = new ArrayList<>(); - mAnswerListener = listener; - } - - public void accumulate(@Nullable List<InetAddress> answer) { - if (null != answer) { - mAllAnswers.addAll(answer); - } - if (++mReceivedAnswerCount == mTargetAnswerCount) { - mAnswerListener.onAnswer(mAllAnswers); - } - } - } } diff --git a/core/java/android/net/IIpMemoryStoreCallbacks.aidl b/core/java/android/net/IIpMemoryStoreCallbacks.aidl new file mode 100644 index 000000000000..53108dbca097 --- /dev/null +++ b/core/java/android/net/IIpMemoryStoreCallbacks.aidl @@ -0,0 +1,24 @@ +/* + * 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; + +import android.net.IIpMemoryStore; + +/** {@hide} */ +oneway interface IIpMemoryStoreCallbacks { + void onIpMemoryStoreFetched(in IIpMemoryStore ipMemoryStore); +} diff --git a/core/java/android/net/INetworkStackConnector.aidl b/core/java/android/net/INetworkStackConnector.aidl index edb9df61044f..3751c36d6ee9 100644 --- a/core/java/android/net/INetworkStackConnector.aidl +++ b/core/java/android/net/INetworkStackConnector.aidl @@ -15,6 +15,7 @@ */ package android.net; +import android.net.IIpMemoryStoreCallbacks; import android.net.INetworkMonitorCallbacks; import android.net.Network; import android.net.dhcp.DhcpServingParamsParcel; @@ -27,4 +28,5 @@ oneway interface INetworkStackConnector { in IDhcpServerCallbacks cb); void makeNetworkMonitor(in Network network, String name, in INetworkMonitorCallbacks cb); void makeIpClient(in String ifName, in IIpClientCallbacks callbacks); -}
\ No newline at end of file + void fetchIpMemoryStore(in IIpMemoryStoreCallbacks cb); +} diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java index c638491291c2..c97b37b55c78 100644 --- a/core/java/android/net/InterfaceConfiguration.java +++ b/core/java/android/net/InterfaceConfiguration.java @@ -19,11 +19,9 @@ package android.net; import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; -import android.text.TextUtils; import com.google.android.collect.Sets; -import java.net.InetAddress; import java.util.HashSet; /** @@ -117,40 +115,6 @@ public class InterfaceConfiguration implements Parcelable { } /** - * Construct InterfaceConfiguration from InterfaceConfigurationParcel. - */ - public static InterfaceConfiguration fromParcel(InterfaceConfigurationParcel p) { - InterfaceConfiguration cfg = new InterfaceConfiguration(); - cfg.setHardwareAddress(p.hwAddr); - - final InetAddress addr = NetworkUtils.numericToInetAddress(p.ipv4Addr); - cfg.setLinkAddress(new LinkAddress(addr, p.prefixLength)); - for (String flag : p.flags) { - cfg.setFlag(flag); - } - - return cfg; - } - - /** - * Convert InterfaceConfiguration to InterfaceConfigurationParcel with given ifname. - */ - public InterfaceConfigurationParcel toParcel(String iface) { - InterfaceConfigurationParcel cfgParcel = new InterfaceConfigurationParcel(); - cfgParcel.ifName = iface; - if (!TextUtils.isEmpty(mHwAddr)) { - cfgParcel.hwAddr = mHwAddr; - } else { - cfgParcel.hwAddr = ""; - } - cfgParcel.ipv4Addr = mAddr.getAddress().getHostAddress(); - cfgParcel.prefixLength = mAddr.getPrefixLength(); - cfgParcel.flags = mFlags.toArray(EMPTY_STRING_ARRAY); - - return cfgParcel; - } - - /** * This function determines if the interface is up and has a valid IP * configuration (IP address has a non zero octet). * diff --git a/core/java/android/net/ParseException.java b/core/java/android/net/ParseException.java index 2380e863d043..9d4727a84bc0 100644 --- a/core/java/android/net/ParseException.java +++ b/core/java/android/net/ParseException.java @@ -16,15 +16,22 @@ package android.net; +import android.annotation.NonNull; + /** - * Thrown when parsing a URL fails. + * Thrown when parsing failed. */ // See non-public class {@link WebAddress}. public class ParseException extends RuntimeException { public String response; - ParseException(String response) { + public ParseException(@NonNull String response) { super(response); this.response = response; } + + public ParseException(@NonNull String response, @NonNull Throwable cause) { + super(response, cause); + this.response = response; + } } diff --git a/core/java/android/net/UidRange.java b/core/java/android/net/UidRange.java index d4a4cf436787..e56f05995cdc 100644 --- a/core/java/android/net/UidRange.java +++ b/core/java/android/net/UidRange.java @@ -19,14 +19,17 @@ package android.net; import static android.os.UserHandle.PER_USER_RANGE; import android.os.Parcel; +import android.os.Parcelable; /** * An inclusive range of UIDs. * * @hide */ -public final class UidRange extends UidRangeParcel { - private UidRange() {} +public final class UidRange implements Parcelable { + public final int start; + public final int stop; + public UidRange(int startUid, int stopUid) { if (startUid < 0) throw new IllegalArgumentException("Invalid start UID."); if (stopUid < 0) throw new IllegalArgumentException("Invalid stop UID."); @@ -86,18 +89,28 @@ public final class UidRange extends UidRangeParcel { return start + "-" + stop; } - /** - * DO NOT override "writeToParcel" and "readFromParcel" in this class. - * The parceling code is autogenerated by the superclass. - */ + // Implement the Parcelable interface + // TODO: Consider making this class no longer parcelable, since all users are likely in the + // system server. + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(start); + dest.writeInt(stop); + } public static final @android.annotation.NonNull Creator<UidRange> CREATOR = new Creator<UidRange>() { @Override public UidRange createFromParcel(Parcel in) { - UidRange obj = new UidRange(); - obj.readFromParcel(in); - return obj; + int start = in.readInt(); + int stop = in.readInt(); + + return new UidRange(start, stop); } @Override public UidRange[] newArray(int size) { diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java index 870d8b1b7c22..ea245a4879c3 100644 --- a/core/java/android/net/VpnService.java +++ b/core/java/android/net/VpnService.java @@ -27,6 +27,8 @@ import android.annotation.UnsupportedAppUsage; import android.app.Activity; import android.app.PendingIntent; import android.app.Service; +import android.app.admin.DevicePolicyManager; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.IPackageManager; @@ -48,6 +50,7 @@ import java.net.InetAddress; import java.net.Socket; import java.util.ArrayList; import java.util.List; +import java.util.Set; /** * VpnService is a base class for applications to extend and build their @@ -138,7 +141,7 @@ public class VpnService extends Service { * provides users with the ability to set it as always-on, so that VPN connection is * persisted after device reboot and app upgrade. Always-on VPN can also be enabled by device * owner and profile owner apps through - * {@link android.app.admin.DevicePolicyManager#setAlwaysOnVpnPackage}. + * {@link DevicePolicyManager#setAlwaysOnVpnPackage}. * * <p>VPN apps not supporting this feature should opt out by adding this meta-data field to the * {@code VpnService} component of {@code AndroidManifest.xml}. In case there is more than one @@ -370,7 +373,10 @@ public class VpnService extends Service { } /** - * Returns whether the service is running in always-on VPN mode. + * Returns whether the service is running in always-on VPN mode. In this mode the system ensures + * that the service is always running by restarting it when necessary, e.g. after reboot. + * + * @see DevicePolicyManager#setAlwaysOnVpnPackage(ComponentName, String, boolean, Set) */ public final boolean isAlwaysOn() { try { @@ -381,8 +387,11 @@ public class VpnService extends Service { } /** - * Returns whether the service is running in always-on VPN mode blocking connections without - * VPN. + * Returns whether the service is running in always-on VPN lockdown mode. In this mode the + * system ensures that the service is always running and that the apps aren't allowed to bypass + * the VPN. + * + * @see DevicePolicyManager#setAlwaysOnVpnPackage(ComponentName, String, boolean, Set) */ public final boolean isLockdownEnabled() { try { diff --git a/core/java/android/net/util/SocketUtils.java b/core/java/android/net/util/SocketUtils.java index 5827f9ec9343..6f8aece848f8 100644 --- a/core/java/android/net/util/SocketUtils.java +++ b/core/java/android/net/util/SocketUtils.java @@ -45,7 +45,7 @@ import java.net.SocketException; */ @SystemApi @TestApi -public class SocketUtils { +public final class SocketUtils { /** * Create a raw datagram socket that is bound to an interface. * @@ -63,6 +63,7 @@ public class SocketUtils { /** * Make a socket address to communicate with netlink. */ + @NonNull public static SocketAddress makeNetlinkSocketAddress(int portId, int groupsMask) { return new NetlinkSocketAddress(portId, groupsMask); } @@ -70,13 +71,15 @@ public class SocketUtils { /** * Make socket address that packet sockets can bind to. */ - public static SocketAddress makePacketSocketAddress(short protocol, int ifIndex) { - return new PacketSocketAddress(protocol, ifIndex); + @NonNull + public static SocketAddress makePacketSocketAddress(int protocol, int ifIndex) { + return new PacketSocketAddress((short) protocol, ifIndex); } /** * Make a socket address that packet socket can send packets to. */ + @NonNull public static SocketAddress makePacketSocketAddress(int ifIndex, @NonNull byte[] hwAddr) { return new PacketSocketAddress(ifIndex, hwAddr); } diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 39e91383c6a4..707a404da4ed 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -82,12 +82,18 @@ public class GraphicsEnvironment { public void setup(Context context, Bundle coreSettings) { final PackageManager pm = context.getPackageManager(); final String packageName = context.getPackageName(); + Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupGpuLayers"); setupGpuLayers(context, coreSettings, pm, packageName); + Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); + Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupAngle"); setupAngle(context, coreSettings, pm, packageName); + Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); + Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "chooseDriver"); if (!chooseDriver(context, coreSettings, pm, packageName)) { setGpuStats(SYSTEM_DRIVER_NAME, SYSTEM_DRIVER_VERSION_NAME, SYSTEM_DRIVER_VERSION_CODE, SystemProperties.getLong(PROPERTY_GFX_DRIVER_BUILD_TIME, 0), packageName); } + Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); } /** diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 24afb56da1c0..63641e538b8e 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -101,6 +101,7 @@ interface IUserManager { boolean isUserNameSet(int userHandle); boolean hasRestrictedProfiles(); boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userHandle, in IntentSender target); + String getUserName(); long getUserStartRealtime(); long getUserUnlockRealtime(); } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 2bd19535f39b..790bb27562a7 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1352,12 +1352,16 @@ public class UserManager { /** * Returns the user name of the user making this call. This call is only * available to applications on the system image; it requires the - * MANAGE_USERS permission. + * {@code android.permission.MANAGE_USERS} or {@code android.permission.GET_ACCOUNTS_PRIVILEGED} + * permissions. * @return the user name */ public String getUserName() { - UserInfo user = getUserInfo(getUserHandle()); - return user == null ? "" : user.name; + try { + return mService.getUserName(); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } } /** @@ -2884,14 +2888,16 @@ public class UserManager { /** * Returns a Bitmap for the calling user's photo. - * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. + * Requires {@link android.Manifest.permission#MANAGE_USERS} + * or {@link android.Manifest.permission#GET_ACCOUNTS_PRIVILEGED} permissions. * * @return a {@link Bitmap} of the user's photo, or null if there's no photo. * @see com.android.internal.util.UserIcons#getDefaultUserIcon for a default. * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public @Nullable Bitmap getUserIcon() { return getUserIcon(getUserHandle()); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index df5da6c6a5e3..313384d668ef 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9053,7 +9053,6 @@ public final class Settings { * @return true if the provider is enabled * * @deprecated use {@link LocationManager#isProviderEnabled(String)} - * @removed no longer supported */ @Deprecated public static boolean isLocationProviderEnabled(ContentResolver cr, String provider) { @@ -9063,12 +9062,12 @@ public final class Settings { } /** - * Thread-safe method for enabling or disabling a single location provider. + * Thread-safe method for enabling or disabling a single location provider. This will have + * no effect on Android Q and above. * @param cr the content resolver to use * @param provider the location provider to enable or disable * @param enabled true if the provider should be enabled * @deprecated This API is deprecated - * @removed no longer supported */ @Deprecated public static void setLocationProviderEnabled(ContentResolver cr, diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java index c794a69d3680..c1d122a1f9b1 100644 --- a/core/java/android/view/GestureDetector.java +++ b/core/java/android/view/GestureDetector.java @@ -16,11 +16,20 @@ package android.view; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SCROLL; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION; + import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Build; import android.os.Handler; import android.os.Message; +import android.os.SystemClock; +import android.util.StatsLog; /** * Detects various gestures and events using the supplied {@link MotionEvent}s. @@ -251,8 +260,12 @@ public class GestureDetector { private boolean mAlwaysInTapRegion; private boolean mAlwaysInBiggerTapRegion; private boolean mIgnoreNextUpEvent; + // Whether a classification has been recorded by statsd for the current event stream. Reset on + // ACTION_DOWN. + private boolean mHasRecordedClassification; private MotionEvent mCurrentDownEvent; + private MotionEvent mCurrentMotionEvent; private MotionEvent mPreviousUpEvent; /** @@ -297,6 +310,7 @@ public class GestureDetector { break; case LONG_PRESS: + recordGestureClassification(msg.arg1); dispatchLongPress(); break; @@ -304,6 +318,8 @@ public class GestureDetector { // If the user's finger is still down, do not count it as a tap if (mDoubleTapListener != null) { if (!mStillDown) { + recordGestureClassification( + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP); mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent); } else { mDeferConfirmSingleTap = true; @@ -501,6 +517,11 @@ public class GestureDetector { final int action = ev.getAction(); + if (mCurrentMotionEvent != null) { + mCurrentMotionEvent.recycle(); + } + mCurrentMotionEvent = MotionEvent.obtain(ev); + if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } @@ -569,6 +590,8 @@ public class GestureDetector { && isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) { // This is a second tap mIsDoubleTapping = true; + recordGestureClassification( + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP); // Give a callback with the first tap of the double-tap handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent); // Give a callback with down event of the double-tap @@ -590,11 +613,17 @@ public class GestureDetector { mStillDown = true; mInLongPress = false; mDeferConfirmSingleTap = false; + mHasRecordedClassification = false; if (mIsLongpressEnabled) { mHandler.removeMessages(LONG_PRESS); - mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime() - + ViewConfiguration.getLongPressTimeout()); + mHandler.sendMessageAtTime( + mHandler.obtainMessage( + LONG_PRESS, + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS, + 0 /* arg2 */), + mCurrentDownEvent.getDownTime() + + ViewConfiguration.getLongPressTimeout()); } mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT); @@ -613,6 +642,8 @@ public class GestureDetector { final float scrollY = mLastFocusY - focusY; if (mIsDoubleTapping) { // Give the move events of the double-tap + recordGestureClassification( + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP); handled |= mDoubleTapListener.onDoubleTapEvent(ev); } else if (mAlwaysInTapRegion) { final int deltaX = (int) (focusX - mDownFocusX); @@ -635,8 +666,12 @@ public class GestureDetector { // reschedule long press with a modified timeout. mHandler.removeMessages(LONG_PRESS); final long longPressTimeout = ViewConfiguration.getLongPressTimeout(); - mHandler.sendEmptyMessageAtTime(LONG_PRESS, ev.getDownTime() - + (long) (longPressTimeout * multiplier)); + mHandler.sendMessageAtTime( + mHandler.obtainMessage( + LONG_PRESS, + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS, + 0 /* arg2 */), + ev.getDownTime() + (long) (longPressTimeout * multiplier)); } // Inhibit default scroll. If a gesture is ambiguous, we prevent scroll // until the gesture is resolved. @@ -646,6 +681,8 @@ public class GestureDetector { } if (distance > slopSquare) { + recordGestureClassification( + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SCROLL); handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); mLastFocusX = focusX; mLastFocusY = focusY; @@ -659,6 +696,7 @@ public class GestureDetector { mAlwaysInBiggerTapRegion = false; } } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) { + recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SCROLL); handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); mLastFocusX = focusX; mLastFocusY = focusY; @@ -667,7 +705,11 @@ public class GestureDetector { motionClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS; if (deepPress && hasPendingLongPress) { mHandler.removeMessages(LONG_PRESS); - mHandler.sendEmptyMessage(LONG_PRESS); + mHandler.sendMessage( + mHandler.obtainMessage( + LONG_PRESS, + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS, + 0 /* arg2 */)); } break; @@ -676,11 +718,15 @@ public class GestureDetector { MotionEvent currentUpEvent = MotionEvent.obtain(ev); if (mIsDoubleTapping) { // Finally, give the up event of the double-tap + recordGestureClassification( + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP); handled |= mDoubleTapListener.onDoubleTapEvent(ev); } else if (mInLongPress) { mHandler.removeMessages(TAP); mInLongPress = false; } else if (mAlwaysInTapRegion && !mIgnoreNextUpEvent) { + recordGestureClassification( + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP); handled = mListener.onSingleTapUp(ev); if (mDeferConfirmSingleTap && mDoubleTapListener != null) { mDoubleTapListener.onSingleTapConfirmed(ev); @@ -821,4 +867,21 @@ public class GestureDetector { mInLongPress = true; mListener.onLongPress(mCurrentDownEvent); } + + private void recordGestureClassification(int classification) { + if (mHasRecordedClassification + || classification + == TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION) { + // Only record the first classification for an event stream. + return; + } + StatsLog.write( + StatsLog.TOUCH_GESTURE_CLASSIFIED, + getClass().getName(), + classification, + (int) (SystemClock.uptimeMillis() - mCurrentMotionEvent.getDownTime()), + (float) Math.hypot(mCurrentMotionEvent.getRawX() - mCurrentDownEvent.getRawX(), + mCurrentMotionEvent.getRawY() - mCurrentDownEvent.getRawY())); + mHasRecordedClassification = true; + } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index b857f1ed9996..49eb78d63479 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -17,6 +17,10 @@ package android.view; import static android.content.res.Resources.ID_NULL; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION; import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED; @@ -96,6 +100,7 @@ import android.util.Property; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.StateSet; +import android.util.StatsLog; import android.util.SuperNotCalledException; import android.util.TypedValue; import android.view.AccessibilityIterators.CharacterTextSegmentIterator; @@ -14542,7 +14547,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (clickable) { setPressed(true, x, y); } - checkForLongClick(ViewConfiguration.getLongPressTimeout(), x, y); + checkForLongClick( + ViewConfiguration.getLongPressTimeout(), + x, + y, + // This is not a touch gesture -- do not classify it as one. + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION); return true; } } @@ -15283,7 +15293,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mHasPerformedLongPress = false; if (!clickable) { - checkForLongClick(ViewConfiguration.getLongPressTimeout(), x, y); + checkForLongClick( + ViewConfiguration.getLongPressTimeout(), + x, + y, + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS); break; } @@ -15307,7 +15321,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } else { // Not inside a scrolling container, so show the feedback right away setPressed(true, x, y); - checkForLongClick(ViewConfiguration.getLongPressTimeout(), x, y); + checkForLongClick( + ViewConfiguration.getLongPressTimeout(), + x, + y, + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS); } break; @@ -15344,7 +15362,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * ambiguousMultiplier); // Subtract the time already spent delay -= event.getEventTime() - event.getDownTime(); - checkForLongClick(delay, x, y); + checkForLongClick( + delay, + x, + y, + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS); } touchSlop *= ambiguousMultiplier; } @@ -15366,7 +15388,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (deepPress && hasPendingLongPressCallback()) { // process the long click action immediately removeLongPressCallback(); - checkForLongClick(0 /* send immediately */, x, y); + checkForLongClick( + 0 /* send immediately */, + x, + y, + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS); } break; @@ -26031,7 +26057,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } - private void checkForLongClick(long delay, float x, float y) { + private void checkForLongClick(long delay, float x, float y, int classification) { if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE || (mViewFlags & TOOLTIP) == TOOLTIP) { mHasPerformedLongPress = false; @@ -26041,6 +26067,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPendingCheckForLongPress.setAnchor(x, y); mPendingCheckForLongPress.rememberWindowAttachCount(); mPendingCheckForLongPress.rememberPressedState(); + mPendingCheckForLongPress.setClassification(classification); postDelayed(mPendingCheckForLongPress, delay); } } @@ -27598,11 +27625,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private float mX; private float mY; private boolean mOriginalPressedState; + /** + * The classification of the long click being checked: one of the + * StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__* constants. + */ + private int mClassification; @Override public void run() { if ((mOriginalPressedState == isPressed()) && (mParent != null) && mOriginalWindowAttachCount == mWindowAttachCount) { + recordGestureClassification(mClassification); if (performLongClick(mX, mY)) { mHasPerformedLongPress = true; } @@ -27621,6 +27654,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public void rememberPressedState() { mOriginalPressedState = isPressed(); } + + public void setClassification(int classification) { + mClassification = classification; + } } private final class CheckForTap implements Runnable { @@ -27633,17 +27670,28 @@ public class View implements Drawable.Callback, KeyEvent.Callback, setPressed(true, x, y); final long delay = ViewConfiguration.getLongPressTimeout() - ViewConfiguration.getTapTimeout(); - checkForLongClick(delay, x, y); + checkForLongClick(delay, x, y, TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS); } } private final class PerformClick implements Runnable { @Override public void run() { + recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP); performClickInternal(); } } + /** Records a classification for the current event stream. */ + private void recordGestureClassification(int classification) { + if (classification == TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION) { + return; + } + // To avoid negatively impacting View performance, the latency and displacement metrics + // are omitted. + StatsLog.write(StatsLog.TOUCH_GESTURE_CLASSIFIED, getClass().getName(), classification); + } + /** * This method returns a ViewPropertyAnimator object, which can be used to animate * specific properties on this View. diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 6d88530f29d9..4413585535f2 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -448,8 +448,9 @@ public final class WebViewFactory { Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()"); try { - initialApplication.getAssets().addAssetPathAsSharedLibrary( - webViewContext.getApplicationInfo().sourceDir); + for (String newAssetPath : webViewContext.getApplicationInfo().getAllApkPaths()) { + initialApplication.getAssets().addAssetPathAsSharedLibrary(newAssetPath); + } ClassLoader clazzLoader = webViewContext.getClassLoader(); Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()"); diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index f250666012f6..faf0c7dbed37 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -680,7 +680,7 @@ public class ChooserActivity extends ResolverActivity { & DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL) != 0; } } - } catch (SecurityException e) { + } catch (SecurityException | NullPointerException e) { Log.w(TAG, "Error loading file preview", e); } @@ -918,6 +918,8 @@ public class ChooserActivity extends ResolverActivity { if (isSendAction(in)) { in.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + + in.fixUris(getUserId()); } } diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 5f2371961857..07b82d006cc8 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -122,6 +122,12 @@ public class ZygoteInit { private static boolean sPreloadComplete; + /** + * Cached classloader to use for the system server. Will only be populated in the system + * server process. + */ + private static ClassLoader sCachedSystemServerClassLoader = null; + static void preload(TimingsTraceLog bootTimingsTraceLog) { Log.d(TAG, "begin preload"); bootTimingsTraceLog.traceBegin("BeginPreload"); @@ -443,7 +449,13 @@ public class ZygoteInit { final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH"); if (systemServerClasspath != null) { - performSystemServerDexOpt(systemServerClasspath); + if (performSystemServerDexOpt(systemServerClasspath)) { + // Throw away the cached classloader. If we compiled here, the classloader would + // not have had AoT-ed artifacts. + // Note: This only works in a very special environment where selinux enforcement is + // disabled, e.g., Mac builds. + sCachedSystemServerClassLoader = null; + } // Capturing profiles is only supported for debug or eng builds since selinux normally // prevents it. boolean profileSystemServer = SystemProperties.getBoolean( @@ -476,10 +488,9 @@ public class ZygoteInit { throw new IllegalStateException("Unexpected return from WrapperInit.execApplication"); } else { - ClassLoader cl = null; - if (systemServerClasspath != null) { - cl = createPathClassLoader(systemServerClasspath, parsedArgs.mTargetSdkVersion); - + createSystemServerClassLoader(); + ClassLoader cl = sCachedSystemServerClassLoader; + if (cl != null) { Thread.currentThread().setContextClassLoader(cl); } @@ -494,6 +505,24 @@ public class ZygoteInit { } /** + * Create the classloader for the system server and store it in + * {@link sCachedSystemServerClassLoader}. This function may be called through JNI in + * system server startup, when the runtime is in a critically low state. Do not do + * extended computation etc here. + */ + private static void createSystemServerClassLoader() { + if (sCachedSystemServerClassLoader != null) { + return; + } + final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH"); + // TODO: Should we run optimization here? + if (systemServerClasspath != null) { + sCachedSystemServerClassLoader = createPathClassLoader(systemServerClasspath, + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT); + } + } + + /** * Note that preparing the profiles for system server does not require special selinux * permissions. From the installer perspective the system server is a regular package which can * capture profile information. @@ -557,15 +586,16 @@ public class ZygoteInit { /** * Performs dex-opt on the elements of {@code classPath}, if needed. We choose the instruction - * set of the current runtime. + * set of the current runtime. If something was compiled, return true. */ - private static void performSystemServerDexOpt(String classPath) { + private static boolean performSystemServerDexOpt(String classPath) { final String[] classPathElements = classPath.split(":"); final IInstalld installd = IInstalld.Stub .asInterface(ServiceManager.getService("installd")); final String instructionSet = VMRuntime.getRuntime().vmInstructionSet(); String classPathForElement = ""; + boolean compiledSomething = false; for (String classPathElement : classPathElements) { // System server is fully AOTed and never profiled // for profile guided compilation. @@ -607,6 +637,7 @@ public class ZygoteInit { uuid, classLoaderContext, seInfo, false /* downgrade */, targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null, "server-dexopt"); + compiledSomething = true; } catch (RemoteException | ServiceSpecificException e) { // Ignore (but log), we need this on the classpath for fallback mode. Log.w(TAG, "Failed compiling classpath element for system server: " @@ -617,6 +648,8 @@ public class ZygoteInit { classPathForElement = encodeSystemServerClassPath( classPathForElement, classPathElement); } + + return compiledSomething; } /** diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index 3be7c3e35895..41e2fc84593e 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -62,6 +62,7 @@ interface ILockSettings { void systemReady(); void userPresent(int userId); int getStrongAuthForUser(int userId); + boolean hasPendingEscrowToken(int userId); // Keystore RecoveryController methods. // {@code ServiceSpecificException} may be thrown to signal an error, which caller can diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index c09537670556..1965609ff959 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -1638,7 +1638,7 @@ public class LockPatternUtils { } /** - * @see StrongAuthTracker#isFingerprintAllowedForUser + * @see StrongAuthTracker#isBiometricAllowedForUser(int) */ public boolean isBiometricAllowedForUser(int userId) { return (getStrongAuthForUser(userId) & ~StrongAuthTracker.ALLOWING_BIOMETRIC) == 0; @@ -1980,6 +1980,18 @@ public class LockPatternUtils { } /** + * Returns whether the given user has pending escrow tokens + */ + public boolean hasPendingEscrowToken(int userId) { + try { + return getLockSettings().hasPendingEscrowToken(userId); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + return false; + } + + /** * Return true if the device supports the lock screen feature, false otherwise. */ public boolean hasSecureLockScreen() { diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.h b/core/jni/android/graphics/HarfBuzzNGFaceSkia.h deleted file mode 100644 index 3308d5d51b58..000000000000 --- a/core/jni/android/graphics/HarfBuzzNGFaceSkia.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2011, The Android Open Source Project - * Copyright 2011, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _ANDROID_GRAPHICS_HARF_BUZZ_NG_FACE_SKIA_H_ -#define _ANDROID_GRAPHICS_HARF_BUZZ_NG_FACE_SKIA_H_ - -#include <SkScalar.h> -#include <SkPaint.h> - -#include <hb.h> - -namespace android { - -static inline float -HBFixedToFloat (hb_position_t v) -{ - return scalbnf (v, -8); -} - -static inline hb_position_t -HBFloatToFixed (float v) -{ - return scalbnf (v, +8); -} - -static inline hb_position_t SkScalarToHBFixed(SkScalar value) { - return HBFloatToFixed(SkScalarToFloat(value)); -} - -hb_blob_t* harfbuzzSkiaReferenceTable(hb_face_t* face, hb_tag_t tag, void* userData); - -hb_font_t* createFont(hb_face_t* face, SkPaint* paint, float sizeX, float sizeY); - -} // namespace android - -#endif // _ANDROID_GRAPHICS_HARF_BUZZ_NG_FACE_SKIA_H_ diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index 9f9fbf9eb26d..c4c16ee0ef9b 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -31,6 +31,7 @@ #include <renderthread/CanvasContext.h> #include <TreeInfo.h> #include <hwui/Paint.h> +#include <utils/TraceUtils.h> #include "core_jni_helpers.h" diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 009a6ca70df2..cde188439a10 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -24,6 +24,7 @@ #endif #define LOG_TAG "Zygote" +#define ATRACE_TAG ATRACE_TAG_DALVIK #include <async_safe/log.h> @@ -73,6 +74,7 @@ #include <cutils/multiuser.h> #include <private/android_filesystem_config.h> #include <utils/String8.h> +#include <utils/Trace.h> #include <selinux/android.h> #include <seccomp_policy.h> #include <stats_event_list.h> @@ -111,11 +113,16 @@ static pid_t gSystemServerPid = 0; static const char kIsolatedStorage[] = "persist.sys.isolated_storage"; static const char kIsolatedStorageSnapshot[] = "sys.isolated_storage_snapshot"; -static const char kZygoteClassName[] = "com/android/internal/os/Zygote"; + +static constexpr const char* kZygoteClassName = "com/android/internal/os/Zygote"; static jclass gZygoteClass; static jmethodID gCallPostForkSystemServerHooks; static jmethodID gCallPostForkChildHooks; +static constexpr const char* kZygoteInitClassName = "com/android/internal/os/ZygoteInit"; +static jclass gZygoteInitClass; +static jmethodID gCreateSystemServerClassLoader; + static bool g_is_security_enforced = true; /** @@ -585,6 +592,8 @@ static void SetSchedulerPolicy(fail_fn_t fail_fn) { } static int UnmountTree(const char* path) { + ATRACE_CALL(); + size_t path_len = strlen(path); FILE* fp = setmntent("/proc/mounts", "r"); @@ -627,6 +636,8 @@ static void CreateDir(const std::string& dir, } static void CreatePkgSandboxTarget(userid_t user_id, fail_fn_t fail_fn) { + ATRACE_CALL(); + // Create /mnt/user/0/package std::string pkg_sandbox_dir = StringPrintf("/mnt/user/%d", user_id); CreateDir(pkg_sandbox_dir, 0751, AID_ROOT, AID_ROOT, fail_fn); @@ -650,6 +661,8 @@ static void MountPkgSpecificDir(const std::string& mnt_source_root, uid_t uid, const char* dir_name, fail_fn_t fail_fn) { + ATRACE_CALL(); + std::string mnt_source_dir = StringPrintf("%s/Android/%s/%s", mnt_source_root.c_str(), dir_name, package_name.c_str()); @@ -662,6 +675,8 @@ static void MountPkgSpecificDir(const std::string& mnt_source_root, static void CreateSubDirs(int parent_fd, const std::string& parent_path, const std::vector<std::string>& sub_dirs, fail_fn_t fail_fn) { + ATRACE_CALL(); + for (auto& dir_name : sub_dirs) { struct stat sb; if (TEMP_FAILURE_RETRY(fstatat(parent_fd, dir_name.c_str(), &sb, 0)) == 0) { @@ -686,6 +701,8 @@ static void EnsurePkgSpecificDirs(const std::string& path, const std::vector<std::string>& package_names, bool create_sandbox_dir, fail_fn_t fail_fn) { + ATRACE_CALL(); + std::string android_dir = StringPrintf("%s/Android", path.c_str()); android::base::unique_fd android_fd(open(android_dir.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC)); @@ -729,6 +746,7 @@ static void EnsurePkgSpecificDirs(const std::string& path, } static void CreatePkgSandboxSource(const std::string& sandbox_source, fail_fn_t fail_fn) { + ATRACE_CALL(); struct stat sb; if (TEMP_FAILURE_RETRY(stat(sandbox_source.c_str(), &sb)) == 0) { @@ -751,6 +769,8 @@ static void CreatePkgSandboxSource(const std::string& sandbox_source, fail_fn_t static void PreparePkgSpecificDirs(const std::vector<std::string>& package_names, bool mount_all_obbs, const std::string& sandbox_id, userid_t user_id, uid_t uid, fail_fn_t fail_fn) { + ATRACE_CALL(); + std::unique_ptr<DIR, decltype(&closedir)> dirp(opendir("/storage"), closedir); if (!dirp) { fail_fn(CREATE_ERROR("Failed to opendir /storage: %s", strerror(errno))); @@ -807,6 +827,8 @@ static void HandleMountModeInstaller(int mount_mode, userid_t user_id, const std::string& sandbox_id, fail_fn_t fail_fn) { + ATRACE_CALL(); + std::string obb_mount_dir = StringPrintf("/mnt/user/%d/obb_mount", user_id); std::string obb_mount_file = StringPrintf("%s/%s", obb_mount_dir.c_str(), sandbox_id.c_str()); if (mount_mode == MOUNT_EXTERNAL_INSTALLER) { @@ -844,6 +866,7 @@ static void MountEmulatedStorage(uid_t uid, jint mount_mode, const std::string& sandbox_id, fail_fn_t fail_fn) { // See storage config details at http://source.android.com/tech/storage/ + ATRACE_CALL(); String8 storage_source; if (mount_mode == MOUNT_EXTERNAL_DEFAULT) { @@ -1420,6 +1443,15 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, fail_fn("Error calling post fork system server hooks."); } + // Prefetch the classloader for the system server. This is done early to + // allow a tie-down of the proper system server selinux domain. + env->CallStaticVoidMethod(gZygoteInitClass, gCreateSystemServerClassLoader); + if (env->ExceptionCheck()) { + // Be robust here. The Java code will attempt to create the classloader + // at a later point (but may not have rights to use AoT artifacts). + env->ExceptionClear(); + } + // TODO(oth): Remove hardcoded label here (b/117874058). static const char* kSystemServerLabel = "u:r:system_server:s0"; if (selinux_android_setcon(kSystemServerLabel) != 0) { @@ -1986,6 +2018,13 @@ int register_com_android_internal_os_Zygote(JNIEnv* env) { gCallPostForkChildHooks = GetStaticMethodIDOrDie(env, gZygoteClass, "callPostForkChildHooks", "(IZZLjava/lang/String;)V"); - return RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods)); + gZygoteInitClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, kZygoteInitClassName)); + gCreateSystemServerClassLoader = GetStaticMethodIDOrDie(env, gZygoteInitClass, + "createSystemServerClassLoader", + "()V"); + + RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods)); + + return JNI_OK; } } // namespace android diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 927c85f2db8e..7ad922312fa9 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -2302,4 +2302,7 @@ enum PageId { // Panel for Wifi PANEL_WIFI = 1687; + + // Open: Settings > Special App Access > Do not disturb control for app + ZEN_ACCESS_DETAIL = 1692; } diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto index 8fce94edb104..9bf1825a8da6 100644 --- a/core/proto/android/server/powermanagerservice.proto +++ b/core/proto/android/server/powermanagerservice.proto @@ -318,6 +318,16 @@ message BatterySaverStateMachineProto { // Whether battery saver is enabled. optional bool enabled = 1; + enum StateEnum { + STATE_UNKNOWN = 0; + STATE_OFF = 1; + STATE_MANUAL_ON = 2; + STATE_AUTOMATIC_ON = 3; + STATE_OFF_AUTOMATIC_SNOOZED = 4; + STATE_PENDING_STICKY_ON = 5; + } + optional StateEnum state = 18; + // Whether full battery saver is enabled. optional bool is_full_enabled = 14; @@ -337,8 +347,7 @@ message BatterySaverStateMachineProto { // Whether battery status has been set at least once. optional bool battery_status_set = 4; - // Whether automatic battery saver has been canceled by the user. - optional bool battery_saver_snoozing = 5; + reserved 5; // battery_saver_snoozing // Whether the device is connected to any power source. optional bool is_powered = 6; @@ -373,5 +382,5 @@ message BatterySaverStateMachineProto { // using elapsed realtime as the timebase. optional int64 last_adaptive_battery_saver_changed_externally_elapsed = 17; - // Next tag: 18 + // Next tag: 19 } diff --git a/media/java/android/media/session/SessionLink.aidl b/core/proto/android/stats/storage/storage_enums.proto index c3be23e8f6b7..6892e287472f 100644 --- a/media/java/android/media/session/SessionLink.aidl +++ b/core/proto/android/stats/storage/storage_enums.proto @@ -1,5 +1,5 @@ /* - * Copyright 2019 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +14,13 @@ * limitations under the License. */ -package android.media.session; +syntax = "proto2"; -parcelable SessionLink; +package android.stats.storage; + +enum ExternalStorageType { + UNKNOWN = 0; + SD_CARD = 1; + USB = 2; + OTHER = 3; +} diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index dbea13ec7539..fe2c6655a6ed 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -118,8 +118,11 @@ <attr name="manageSpaceActivity" format="string" /> <!-- Option to let applications specify that user data can/cannot be - cleared by the user in Settings. This flag is turned on by default. - <em>This attribute is usable only by applications + cleared. This flag is turned on by default. + <p>Starting from API level 29 this flag only controls if the user can + clear app data from Settings. To control clearing the data after a + failed restore use allowClearUserDataOnFailedRestore flag. + <p><em>This attribute is usable only by applications included in the system image. Third-party apps cannot use it.</em> --> <attr name="allowClearUserData" format="boolean" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 2b13c4e23602..74970e810181 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1538,43 +1538,45 @@ <string name="permdesc_useFaceAuthentication">Allows the app to use face authentication hardware for authentication</string> <!-- Message shown during face acquisition when the face cannot be recognized [CHAR LIMIT=50] --> - <string name="face_acquired_insufficient">Couldn\u2019t process face. Please try again.</string> + <string name="face_acquired_insufficient">Couldn\u2019t capture accurate face data. Try again.</string> <!-- Message shown during face acquisition when the image is too bright [CHAR LIMIT=50] --> - <string name="face_acquired_too_bright">Face is too bright. Please try in lower light.</string> + <string name="face_acquired_too_bright">Too bright. Try gentler lighting.</string> <!-- Message shown during face acquisition when the image is too dark [CHAR LIMIT=50] --> - <string name="face_acquired_too_dark">Face is too dark. Please uncover light source.</string> + <string name="face_acquired_too_dark">Too dark. Try brighter lighting.</string> <!-- Message shown during face acquisition when the user is too close to sensor [CHAR LIMIT=50] --> - <string name="face_acquired_too_close">Please move sensor farther from face.</string> + <string name="face_acquired_too_close">Move phone farther away.</string> <!-- Message shown during face acquisition when the user is too far from sensor [CHAR LIMIT=50] --> - <string name="face_acquired_too_far">Please bring sensor closer to face.</string> + <string name="face_acquired_too_far">Move phone closer.</string> <!-- Message shown during face acquisition when the user is too high relatively to sensor [CHAR LIMIT=50] --> - <string name="face_acquired_too_high">Please move sensor higher.</string> + <string name="face_acquired_too_high">Move phone higher.</string> <!-- Message shown during face acquisition when the user is too low relatively to sensor [CHAR LIMIT=50] --> - <string name="face_acquired_too_low">Please move sensor lower.</string> + <string name="face_acquired_too_low">Move phone lower.</string> <!-- Message shown during face acquisition when the user is too right relatively to sensor [CHAR LIMIT=50] --> - <string name="face_acquired_too_right">Please move sensor to the right.</string> + <string name="face_acquired_too_right">Move phone to the right.</string> <!-- Message shown during face acquisition when the user is too left relatively to sensor [CHAR LIMIT=50] --> - <string name="face_acquired_too_left">Please move sensor to the left.</string> + <string name="face_acquired_too_left">Move phone to the left.</string> <!-- Message shown during face acquisition when the user is not front facing the sensor [CHAR LIMIT=50] --> - <string name="face_acquired_poor_gaze">Please look at the sensor.</string> + <string name="face_acquired_poor_gaze">Look at the screen with your eyes open.</string> <!-- Message shown during face acquisition when the user is not detected [CHAR LIMIT=50] --> - <string name="face_acquired_not_detected">No face detected.</string> + <string name="face_acquired_not_detected">Can\u2019t see your face. Look at the phone.</string> <!-- Message shown during face acquisition when the device is not steady [CHAR LIMIT=50] --> - <string name="face_acquired_too_much_motion">Too much motion.</string> + <string name="face_acquired_too_much_motion">Too much motion. Hold phone steady.</string> <!-- Message shown during face acquisition when the sensor needs to be recalibrated [CHAR LIMIT=50] --> <string name="face_acquired_recalibrate">Please re-enroll your face.</string> <!-- Message shown during face enrollment when a different person's face is detected [CHAR LIMIT=50] --> - <string name="face_acquired_too_different">Different face detected.</string> + <string name="face_acquired_too_different">No longer able to recognize face. Try again.</string> <!-- Message shown during face enrollment when the face is too similar to a previous acquisition [CHAR LIMIT=50] --> <string name="face_acquired_too_similar">Too similar, please change your pose.</string> <!-- Message shown during acqusition when the user's face is turned too far left or right [CHAR LIMIT=50] --> - <string name="face_acquired_pan_too_extreme">Please look more directly at the camera.</string> + <string name="face_acquired_pan_too_extreme">Please look more directly at the screen.</string> <!-- Message shown during acqusition when the user's face is tilted too high or too low [CHAR LIMIT=50] --> - <string name="face_acquired_tilt_too_extreme">Please look more directly at the camera.</string> + <string name="face_acquired_tilt_too_extreme">Please look more directly at the screen.</string> <!-- Message shown during acquisiton when the user's face is tilted too far left or right [CHAR LIMIT=50] --> <string name="face_acquired_roll_too_extreme">Please straighten your head vertically.</string> <!-- Message shown during acquisition when the user's face is obscured [CHAR LIMIT=50] --> - <string name="face_acquired_obscured">Please uncover your face.</string> + <string name="face_acquired_obscured">Clear the space between your head and the phone.</string> + <!-- Message shown during acquisition when the sensor is dirty [CHAR LIMIT=50] --> + <string name="face_acquired_sensor_dirty">Please clean the camera.</string> <!-- Array containing custom messages shown during face acquisition from vendor. Vendor is expected to add and translate these strings --> <string-array name="face_acquired_vendor"> </string-array> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e5fc104ddb2d..17ccc31179f9 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2546,6 +2546,7 @@ <java-symbol type="string" name="face_acquired_tilt_too_extreme" /> <java-symbol type="string" name="face_acquired_roll_too_extreme" /> <java-symbol type="string" name="face_acquired_obscured" /> + <java-symbol type="string" name="face_acquired_sensor_dirty" /> <java-symbol type="array" name="face_acquired_vendor" /> <java-symbol type="string" name="face_name_template" /> <java-symbol type="string" name="face_authenticated_no_confirmation_required" /> diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java index 66bcd8650b95..9db7533044e2 100644 --- a/graphics/java/android/graphics/text/MeasuredText.java +++ b/graphics/java/android/graphics/text/MeasuredText.java @@ -65,6 +65,7 @@ public class MeasuredText { /** * Returns the characters in the paragraph used to compute this MeasuredText instance. + * @hide */ public @NonNull char[] getChars() { return mChars; diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java index 105af6e829f8..51c42520ccc9 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java @@ -16,7 +16,6 @@ package android.security.keystore; -import libcore.util.EmptyArray; import android.security.Credentials; import android.security.GateKeeper; import android.security.KeyStore; @@ -31,6 +30,8 @@ import android.security.keystore.SecureKeyImportUnavailableException; import android.security.keystore.WrappedKeyEntry; import android.util.Log; +import libcore.util.EmptyArray; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -123,7 +124,14 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { final Certificate[] caList; - final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid); + // Suppress the key not found warning for this call. It seems that this error is exclusively + // being thrown when there is a self signed certificate chain, so when the keystore service + // attempts to query for the CA details, it obviously fails to find them and returns a + // key not found exception. This is WAI, and throwing a stack trace here can be very + // misleading since the trace is not clear. + final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, + mUid, + true /* suppressKeyNotFoundWarning */); if (caBytes != null) { final Collection<X509Certificate> caChain = toCertificates(caBytes); diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp index c512a6b06ed1..9a95fdf80cb5 100644 --- a/libs/androidfw/Asset.cpp +++ b/libs/androidfw/Asset.cpp @@ -292,8 +292,10 @@ Asset::Asset(void) pAsset = new _FileAsset; result = pAsset->openChunk(dataMap); - if (result != NO_ERROR) + if (result != NO_ERROR) { + delete pAsset; return NULL; + } pAsset->mAccessMode = mode; return pAsset; diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 4f1b2a4fcbf8..ebba4cb79dfb 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -191,7 +191,7 @@ cc_defaults { "surfacetexture/EGLConsumer.cpp", "surfacetexture/ImageConsumer.cpp", "surfacetexture/SurfaceTexture.cpp", - "thread/TaskManager.cpp", + "thread/CommonPool.cpp", "utils/Blur.cpp", "utils/Color.cpp", "utils/GLUtils.cpp", @@ -308,6 +308,7 @@ cc_test { "tests/unit/main.cpp", "tests/unit/CacheManagerTests.cpp", "tests/unit/CanvasContextTests.cpp", + "tests/unit/CommonPoolTests.cpp", "tests/unit/DamageAccumulatorTests.cpp", "tests/unit/DeferredLayerUpdaterTests.cpp", "tests/unit/FatVectorTests.cpp", @@ -381,7 +382,6 @@ cc_benchmark { "tests/microbench/LinearAllocatorBench.cpp", "tests/microbench/PathParserBench.cpp", "tests/microbench/RenderNodeBench.cpp", - "tests/microbench/TaskManagerBench.cpp", ], } diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp index fe633e96b9d7..fb60a966c48f 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -28,6 +28,7 @@ #include "hwui/Bitmap.h" #include "utils/Color.h" #include "utils/MathUtils.h" +#include "utils/TraceUtils.h" using namespace android::uirenderer::renderthread; diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index a00a36f93501..721a115c1381 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -25,6 +25,7 @@ #include <SkPictureRecorder.h> #include "TreeInfo.h" #include "VectorDrawable.h" +#include "thread/CommonPool.h" #include "utils/TraceUtils.h" #include <unistd.h> @@ -49,10 +50,6 @@ SkiaPipeline::~SkiaPipeline() { unpinImages(); } -TaskManager* SkiaPipeline::getTaskManager() { - return mRenderThread.cacheManager().getTaskManager(); -} - void SkiaPipeline::onDestroyHardwareResources() { unpinImages(); mRenderThread.cacheManager().trimStaleResources(); @@ -225,42 +222,21 @@ void SkiaPipeline::renderVectorDrawableCache() { } } -class SkiaPipeline::SavePictureProcessor : public TaskProcessor<bool> { -public: - explicit SavePictureProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {} - - struct SavePictureTask : public Task<bool> { - sk_sp<SkData> data; - std::string filename; - }; - - void savePicture(const sk_sp<SkData>& data, const std::string& filename) { - sp<SavePictureTask> task(new SavePictureTask()); - task->data = data; - task->filename = filename; - TaskProcessor<bool>::add(task); - } - - virtual void onProcess(const sp<Task<bool>>& task) override { - ATRACE_NAME("SavePictureTask"); - SavePictureTask* t = static_cast<SavePictureTask*>(task.get()); - - if (0 == access(t->filename.c_str(), F_OK)) { - task->setResult(false); +static void savePictureAsync(const sk_sp<SkData>& data, const std::string& filename) { + CommonPool::post([data, filename] { + if (0 == access(filename.c_str(), F_OK)) { return; } - SkFILEWStream stream(t->filename.c_str()); + SkFILEWStream stream(filename.c_str()); if (stream.isValid()) { - stream.write(t->data->data(), t->data->size()); + stream.write(data->data(), data->size()); stream.flush(); SkDebugf("SKP Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(), - t->filename.c_str()); + filename.c_str()); } - - task->setResult(true); - } -}; + }); +} SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) { if (CC_UNLIKELY(Properties::skpCaptureEnabled)) { @@ -297,16 +273,10 @@ void SkiaPipeline::endCapture(SkSurface* surface) { ATRACE_END(); // offload saving to file in a different thread - if (!mSavePictureProcessor.get()) { - TaskManager* taskManager = getTaskManager(); - mSavePictureProcessor = new SavePictureProcessor( - taskManager->canRunTasks() ? taskManager : nullptr); - } if (1 == mCaptureSequence) { - mSavePictureProcessor->savePicture(data, mCapturedFile); + savePictureAsync(data, mCapturedFile); } else { - mSavePictureProcessor->savePicture( - data, + savePictureAsync(data, mCapturedFile + "_" + std::to_string(mCaptureSequence)); } mCaptureSequence--; diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index 7381e0417a2d..41d864653b67 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -33,8 +33,6 @@ public: explicit SkiaPipeline(renderthread::RenderThread& thread); virtual ~SkiaPipeline(); - TaskManager* getTaskManager() override; - void onDestroyHardwareResources() override; bool pinImages(std::vector<SkImage*>& mutableImages) override; @@ -157,11 +155,7 @@ private: * mCaptureSequence counts how many frames are left to take in the sequence. */ int mCaptureSequence = 0; - /** - * mSavePictureProcessor is used to run the file saving code in a separate thread. - */ - class SavePictureProcessor; - sp<SavePictureProcessor> mSavePictureProcessor; + /** * mRecorder holds the current picture recorder. We could store it on the stack to support * parallel tryCapture calls (not really needed). diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 6c04232ab7f5..8b02c11911ca 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -23,6 +23,7 @@ #include "pipeline/skia/SkiaMemoryTracer.h" #include "Properties.h" #include "renderstate/RenderState.h" +#include "thread/CommonPool.h" #include <GrContextOptions.h> #include <SkExecutor.h> @@ -76,29 +77,15 @@ void CacheManager::updateContextCacheSizes() { mGrContext->setResourceCacheLimits(mMaxResources, mMaxResourceBytes); } -class CacheManager::SkiaTaskProcessor : public TaskProcessor<bool>, public SkExecutor { +class CommonPoolExecutor : public SkExecutor { public: - explicit SkiaTaskProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {} - - // This is really a Task<void> but that doesn't really work when Future<> - // expects to be able to get/set a value - struct SkiaTask : public Task<bool> { - std::function<void()> func; - }; - virtual void add(std::function<void(void)> func) override { - sp<SkiaTask> task(new SkiaTask()); - task->func = func; - TaskProcessor<bool>::add(task); - } - - virtual void onProcess(const sp<Task<bool> >& task) override { - SkiaTask* t = static_cast<SkiaTask*>(task.get()); - t->func(); - task->setResult(true); + CommonPool::post(std::move(func)); } }; +static CommonPoolExecutor sDefaultExecutor; + void CacheManager::configureContext(GrContextOptions* contextOptions, const void* identity, ssize_t size) { contextOptions->fAllowPathMaskCaching = true; @@ -107,12 +94,7 @@ void CacheManager::configureContext(GrContextOptions* contextOptions, const void // provided to Skia. contextOptions->fGlyphCacheTextureMaximumBytes = GrNextSizePow2(mMaxSurfaceArea); - if (mTaskManager.canRunTasks()) { - if (!mTaskProcessor.get()) { - mTaskProcessor = new SkiaTaskProcessor(&mTaskManager); - } - contextOptions->fExecutor = mTaskProcessor.get(); - } + contextOptions->fExecutor = &sDefaultExecutor; auto& cache = skiapipeline::ShaderCache::get(); cache.initShaderDiskCache(identity, size); diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h index 66f04f1ba266..b0286e8cde25 100644 --- a/libs/hwui/renderthread/CacheManager.h +++ b/libs/hwui/renderthread/CacheManager.h @@ -24,8 +24,6 @@ #include <vector> #include "pipeline/skia/VectorDrawableAtlas.h" -#include "thread/TaskManager.h" -#include "thread/TaskProcessor.h" namespace android { @@ -54,8 +52,6 @@ public: size_t getCacheSize() const { return mMaxResourceBytes; } size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; } - TaskManager* getTaskManager() { return &mTaskManager; } - private: friend class RenderThread; @@ -78,10 +74,6 @@ private: }; sp<skiapipeline::VectorDrawableAtlas> mVectorDrawableAtlas; - - class SkiaTaskProcessor; - sp<SkiaTaskProcessor> mTaskProcessor; - TaskManager mTaskManager; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index f5b4d9359ee9..baa41c1ec698 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -27,8 +27,10 @@ #include "pipeline/skia/SkiaOpenGLPipeline.h" #include "pipeline/skia/SkiaPipeline.h" #include "pipeline/skia/SkiaVulkanPipeline.h" +#include "thread/CommonPool.h" #include "utils/GLUtils.h" #include "utils/TimeUtils.h" +#include "utils/TraceUtils.h" #include "../Properties.h" #include <cutils/properties.h> @@ -603,31 +605,14 @@ void CanvasContext::waitOnFences() { if (mFrameFences.size()) { ATRACE_CALL(); for (auto& fence : mFrameFences) { - fence->getResult(); + fence.get(); } mFrameFences.clear(); } } -class CanvasContext::FuncTaskProcessor : public TaskProcessor<bool> { -public: - explicit FuncTaskProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {} - - virtual void onProcess(const sp<Task<bool> >& task) override { - FuncTask* t = static_cast<FuncTask*>(task.get()); - t->func(); - task->setResult(true); - } -}; - void CanvasContext::enqueueFrameWork(std::function<void()>&& func) { - if (!mFrameWorkProcessor.get()) { - mFrameWorkProcessor = new FuncTaskProcessor(mRenderPipeline->getTaskManager()); - } - sp<FuncTask> task(new FuncTask()); - task->func = func; - mFrameFences.push_back(task); - mFrameWorkProcessor->add(task); + mFrameFences.push_back(CommonPool::async(std::move(func))); } int64_t CanvasContext::getFrameNumber() { diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 4da0eac85bb0..abca34225f98 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -28,8 +28,6 @@ #include "ReliableSurface.h" #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" -#include "thread/Task.h" -#include "thread/TaskProcessor.h" #include <EGL/egl.h> #include <SkBitmap.h> @@ -42,6 +40,7 @@ #include <set> #include <string> #include <vector> +#include <future> namespace android { namespace uirenderer { @@ -274,15 +273,7 @@ private: // Stores the bounds of the main content. Rect mContentDrawBounds; - // TODO: This is really a Task<void> but that doesn't really work - // when Future<> expects to be able to get/set a value - struct FuncTask : public Task<bool> { - std::function<void()> func; - }; - class FuncTaskProcessor; - - std::vector<sp<FuncTask>> mFrameFences; - sp<TaskProcessor<bool>> mFrameWorkProcessor; + std::vector<std::future<void>> mFrameFences; std::unique_ptr<IRenderPipeline> mRenderPipeline; std::vector<std::function<void(int64_t)>> mFrameCompleteCallbacks; diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index 2cfc8df38297..0502eb88b6a5 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -75,7 +75,6 @@ public: virtual void renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, bool opaque, const LightInfo& lightInfo) = 0; - virtual TaskManager* getTaskManager() = 0; virtual bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, ErrorHandler* errorHandler) = 0; virtual bool pinImages(std::vector<SkImage*>& mutableImages) = 0; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 34f76d9b3579..1bcb819509af 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -31,6 +31,7 @@ #include "renderthread/RenderThread.h" #include "utils/Macros.h" #include "utils/TimeUtils.h" +#include "utils/TraceUtils.h" #include <ui/GraphicBuffer.h> diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index c784d64f820c..def805adeeea 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -28,6 +28,7 @@ #include "renderstate/RenderState.h" #include "utils/FatVector.h" #include "utils/TimeUtils.h" +#include "utils/TraceUtils.h" #ifdef HWUI_GLES_WRAP_ENABLED #include "debug/GlesDriver.h" diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 6dacc7a0ea07..9916da5d9f10 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -23,6 +23,7 @@ #include "RenderThread.h" #include "renderstate/RenderState.h" #include "utils/FatVector.h" +#include "utils/TraceUtils.h" #include <GrBackendSemaphore.h> #include <GrBackendSurface.h> diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp index 0e61899ed58b..b45dbc8b832b 100644 --- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp +++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp @@ -21,6 +21,7 @@ #include "tests/common/TestContext.h" #include "tests/common/TestScene.h" #include "tests/common/scenes/TestSceneBase.h" +#include "utils/TraceUtils.h" #include <benchmark/benchmark.h> #include <gui/Surface.h> diff --git a/libs/hwui/tests/microbench/TaskManagerBench.cpp b/libs/hwui/tests/microbench/TaskManagerBench.cpp deleted file mode 100644 index 4153baec22b5..000000000000 --- a/libs/hwui/tests/microbench/TaskManagerBench.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <benchmark/benchmark.h> - -#include "thread/Task.h" -#include "thread/TaskManager.h" -#include "thread/TaskProcessor.h" -#include "thread/ThreadBase.h" - -#include <atomic> -#include <vector> - -using namespace android; -using namespace android::uirenderer; - -class TrivialTask : public Task<char> {}; - -class TrivialProcessor : public TaskProcessor<char> { -public: - explicit TrivialProcessor(TaskManager* manager) : TaskProcessor(manager) {} - virtual ~TrivialProcessor() {} - virtual void onProcess(const sp<Task<char>>& task) override { - TrivialTask* t = static_cast<TrivialTask*>(task.get()); - t->setResult(reinterpret_cast<intptr_t>(t) % 16 == 0 ? 'a' : 'b'); - } -}; - -class TestThread : public ThreadBase, public virtual RefBase {}; - -void BM_TaskManager_allocateTask(benchmark::State& state) { - std::vector<sp<TrivialTask>> tasks; - tasks.reserve(state.max_iterations); - - while (state.KeepRunning()) { - tasks.emplace_back(new TrivialTask); - benchmark::DoNotOptimize(tasks.back()); - } -} -BENCHMARK(BM_TaskManager_allocateTask); - -void BM_TaskManager_enqueueTask(benchmark::State& state) { - TaskManager taskManager; - sp<TrivialProcessor> processor(new TrivialProcessor(&taskManager)); - std::vector<sp<TrivialTask>> tasks; - tasks.reserve(state.max_iterations); - - while (state.KeepRunning()) { - tasks.emplace_back(new TrivialTask); - benchmark::DoNotOptimize(tasks.back()); - processor->add(tasks.back()); - } - - for (sp<TrivialTask>& task : tasks) { - task->getResult(); - } -} -BENCHMARK(BM_TaskManager_enqueueTask); - -void BM_TaskManager_enqueueRunDeleteTask(benchmark::State& state) { - TaskManager taskManager; - sp<TrivialProcessor> processor(new TrivialProcessor(&taskManager)); - std::vector<sp<TrivialTask>> tasks; - tasks.reserve(state.max_iterations); - - while (state.KeepRunning()) { - tasks.emplace_back(new TrivialTask); - benchmark::DoNotOptimize(tasks.back()); - processor->add(tasks.back()); - } - state.ResumeTiming(); - for (sp<TrivialTask>& task : tasks) { - benchmark::DoNotOptimize(task->getResult()); - } - tasks.clear(); - state.PauseTiming(); -} -BENCHMARK(BM_TaskManager_enqueueRunDeleteTask); - -void BM_Thread_enqueueTask(benchmark::State& state) { - sp<TestThread> thread{new TestThread}; - thread->start(); - - atomic_int counter(0); - int expected = 0; - while (state.KeepRunning()) { - expected++; - thread->queue().post([&counter]() { counter++; }); - } - thread->queue().runSync([]() {}); - - thread->requestExit(); - thread->join(); - if (counter != expected) { - printf("Ran %d lambads, should have been %d\n", counter.load(), expected); - } -} -BENCHMARK(BM_Thread_enqueueTask); - -void BM_Thread_enqueueRunDeleteTask(benchmark::State& state) { - sp<TestThread> thread{new TestThread}; - thread->start(); - std::vector<std::future<int>> tasks; - tasks.reserve(state.max_iterations); - - int expected = 0; - while (state.KeepRunning()) { - tasks.emplace_back(thread->queue().async([expected]() -> int { return expected + 1; })); - expected++; - } - state.ResumeTiming(); - expected = 0; - for (auto& future : tasks) { - if (future.get() != ++expected) { - printf("Mismatch expected %d vs. observed %d\n", expected, future.get()); - } - } - tasks.clear(); - state.PauseTiming(); -} -BENCHMARK(BM_Thread_enqueueRunDeleteTask);
\ No newline at end of file diff --git a/libs/hwui/tests/unit/CommonPoolTests.cpp b/libs/hwui/tests/unit/CommonPoolTests.cpp new file mode 100644 index 000000000000..c564ed632786 --- /dev/null +++ b/libs/hwui/tests/unit/CommonPoolTests.cpp @@ -0,0 +1,138 @@ +/* + * 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. + */ + +#include <gtest/gtest.h> + +#include "thread/CommonPool.h" + +#include <array> +#include <condition_variable> +#include <set> +#include <thread> +#include "unistd.h" + +using namespace android; +using namespace android::uirenderer; + +TEST(CommonPool, post) { + std::atomic_bool ran(false); + CommonPool::post([&ran] { ran = true; }); + for (int i = 0; !ran && i < 1000; i++) { + usleep(1); + } + EXPECT_TRUE(ran) << "Failed to flip atomic after 1 second"; +} + +TEST(CommonPool, threadCount) { + std::set<pid_t> threads; + std::array<std::future<pid_t>, 64> futures; + for (int i = 0; i < futures.size(); i++) { + futures[i] = CommonPool::async([] { + usleep(10); + return gettid(); + }); + } + for (auto& f : futures) { + threads.insert(f.get()); + } + EXPECT_EQ(threads.size(), CommonPool::THREAD_COUNT); + EXPECT_EQ(0, threads.count(gettid())); +} + +TEST(CommonPool, singleThread) { + std::mutex mutex; + std::condition_variable fence; + bool isProcessing = false; + bool queuedSecond = false; + + auto f1 = CommonPool::async([&] { + { + std::unique_lock lock{mutex}; + isProcessing = true; + fence.notify_all(); + while (!queuedSecond) { + fence.wait(lock); + } + } + return gettid(); + }); + + { + std::unique_lock lock{mutex}; + while (!isProcessing) { + fence.wait(lock); + } + } + + auto f2 = CommonPool::async([] { + return gettid(); + }); + + { + std::unique_lock lock{mutex}; + queuedSecond = true; + fence.notify_all(); + } + + auto tid1 = f1.get(); + auto tid2 = f2.get(); + EXPECT_EQ(tid1, tid2); + EXPECT_NE(gettid(), tid1); +} + +TEST(CommonPool, fullQueue) { + std::mutex lock; + std::condition_variable fence; + bool signaled = false; + static constexpr auto QUEUE_COUNT = CommonPool::THREAD_COUNT + CommonPool::QUEUE_SIZE + 10; + std::atomic_int queuedCount{0}; + std::array<std::future<void>, QUEUE_COUNT> futures; + + std::thread queueThread{[&] { + for (int i = 0; i < QUEUE_COUNT; i++) { + futures[i] = CommonPool::async([&] { + std::unique_lock _lock{lock}; + while (!signaled) { + fence.wait(_lock); + } + }); + queuedCount++; + } + }}; + + int previous; + do { + previous = queuedCount.load(); + usleep(10000); + } while (previous != queuedCount.load()); + + EXPECT_GT(queuedCount.load(), CommonPool::QUEUE_SIZE); + EXPECT_LT(queuedCount.load(), QUEUE_COUNT); + + { + std::unique_lock _lock{lock}; + signaled = true; + fence.notify_all(); + } + + queueThread.join(); + EXPECT_EQ(queuedCount.load(), QUEUE_COUNT); + + // Ensure all our tasks are finished before return as they have references to the stack + for (auto& f : futures) { + f.get(); + } +}
\ No newline at end of file diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp index 63d15403b607..83d888c310f0 100644 --- a/libs/hwui/tests/unit/main.cpp +++ b/libs/hwui/tests/unit/main.cpp @@ -22,9 +22,6 @@ #include "debug/NullGlesDriver.h" #include "hwui/Typeface.h" #include "tests/common/LeakChecker.h" -#include "thread/Task.h" -#include "thread/TaskManager.h" -#include "thread/TaskProcessor.h" #include <signal.h> diff --git a/libs/hwui/thread/Barrier.h b/libs/hwui/thread/Barrier.h deleted file mode 100644 index bb750ca0fa88..000000000000 --- a/libs/hwui/thread/Barrier.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -#ifndef ANDROID_HWUI_BARRIER_H -#define ANDROID_HWUI_BARRIER_H - -#include <utils/Condition.h> - -namespace android { -namespace uirenderer { - -class Barrier { -public: - explicit Barrier(Condition::WakeUpType type = Condition::WAKE_UP_ALL) - : mType(type), mOpened(false) {} - ~Barrier() {} - - void open() { - Mutex::Autolock l(mLock); - mOpened = true; - mCondition.signal(mType); - } - - void wait() const { - Mutex::Autolock l(mLock); - while (!mOpened) { - mCondition.wait(mLock); - } - } - -private: - Condition::WakeUpType mType; - volatile bool mOpened; - mutable Mutex mLock; - mutable Condition mCondition; -}; - -} // namespace uirenderer -} // namespace android - -#endif // ANDROID_HWUI_BARRIER_H diff --git a/libs/hwui/thread/CommonPool.cpp b/libs/hwui/thread/CommonPool.cpp new file mode 100644 index 000000000000..7f94a152cf8d --- /dev/null +++ b/libs/hwui/thread/CommonPool.cpp @@ -0,0 +1,90 @@ +/* + * 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. + */ + +#include "CommonPool.h" + +#include <sys/resource.h> +#include <utils/Trace.h> +#include "renderthread/RenderThread.h" + +#include <array> + +namespace android { +namespace uirenderer { + +CommonPool::CommonPool() { + ATRACE_CALL(); + + CommonPool* pool = this; + // Create 2 workers + for (int i = 0; i < THREAD_COUNT; i++) { + std::thread worker([pool, i] { + { + std::array<char, 20> name{"hwuiTask"}; + snprintf(name.data(), name.size(), "hwuiTask%d", i); + auto self = pthread_self(); + pthread_setname_np(self, name.data()); + setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND); + auto startHook = renderthread::RenderThread::getOnStartHook(); + if (startHook) { + startHook(name.data()); + } + } + pool->workerLoop(); + }); + worker.detach(); + } +} + +void CommonPool::post(Task&& task) { + static CommonPool pool; + pool.enqueue(std::move(task)); +} + +void CommonPool::enqueue(Task&& task) { + std::unique_lock lock(mLock); + while (!mWorkQueue.hasSpace()) { + lock.unlock(); + usleep(100); + lock.lock(); + } + mWorkQueue.push(std::move(task)); + if (mWaitingThreads == THREAD_COUNT || (mWaitingThreads > 0 && mWorkQueue.size() > 1)) { + mCondition.notify_one(); + } +} + +void CommonPool::workerLoop() { + std::unique_lock lock(mLock); + while (true) { + if (!mWorkQueue.hasWork()) { + mWaitingThreads++; + mCondition.wait(lock); + mWaitingThreads--; + } + // Need to double-check that work is still available now that we have the lock + // It may have already been grabbed by a different thread + while (mWorkQueue.hasWork()) { + auto work = mWorkQueue.pop(); + lock.unlock(); + work(); + lock.lock(); + } + } +} + +} // namespace uirenderer +} // namespace android
\ No newline at end of file diff --git a/libs/hwui/thread/CommonPool.h b/libs/hwui/thread/CommonPool.h new file mode 100644 index 000000000000..aef2990d6343 --- /dev/null +++ b/libs/hwui/thread/CommonPool.h @@ -0,0 +1,115 @@ +/* + * 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. + */ + +#ifndef FRAMEWORKS_BASE_COMMONPOOL_H +#define FRAMEWORKS_BASE_COMMONPOOL_H + +#include "utils/Macros.h" + +#include <log/log.h> + +#include <condition_variable> +#include <functional> +#include <future> +#include <mutex> + +namespace android { +namespace uirenderer { + +template <class T, int SIZE> +class ArrayQueue { + PREVENT_COPY_AND_ASSIGN(ArrayQueue); + static_assert(SIZE > 0, "Size must be positive"); + +public: + ArrayQueue() = default; + ~ArrayQueue() = default; + + constexpr size_t capacity() const { return SIZE; } + constexpr bool hasWork() const { return mHead != mTail; } + constexpr bool hasSpace() const { return ((mHead + 1) % SIZE) != mTail; } + constexpr int size() const { + if (mHead > mTail) { + return mHead - mTail; + } else { + return mTail - mHead + SIZE; + } + } + + constexpr void push(T&& t) { + int newHead = (mHead + 1) % SIZE; + LOG_ALWAYS_FATAL_IF(newHead == mTail, "no space"); + + mBuffer[mHead] = std::move(t); + mHead = newHead; + } + + constexpr T&& pop() { + LOG_ALWAYS_FATAL_IF(mTail == mHead, "empty"); + int index = mTail; + mTail = (mTail + 1) % SIZE; + return std::move(mBuffer[index]); + } + +private: + T mBuffer[SIZE]; + int mHead = 0; + int mTail = 0; +}; + +class CommonPool { + PREVENT_COPY_AND_ASSIGN(CommonPool); + +public: + using Task = std::function<void()>; + static constexpr auto THREAD_COUNT = 2; + static constexpr auto QUEUE_SIZE = 128; + + static void post(Task&& func); + + template <class F> + static auto async(F&& func) -> std::future<decltype(func())> { + typedef std::packaged_task<decltype(func())()> task_t; + auto task = std::make_shared<task_t>(std::forward<F>(func)); + post([task]() { std::invoke(*task); }); + return task->get_future(); + } + + template <class F> + static auto runSync(F&& func) -> decltype(func()) { + std::packaged_task<decltype(func())()> task{std::forward<F>(func)}; + post([&task]() { std::invoke(task); }); + return task.get_future().get(); + }; + +private: + CommonPool(); + ~CommonPool() {} + + void enqueue(Task&&); + + void workerLoop(); + + std::mutex mLock; + std::condition_variable mCondition; + int mWaitingThreads = 0; + ArrayQueue<Task, QUEUE_SIZE> mWorkQueue; +}; + +} // namespace uirenderer +} // namespace android + +#endif // FRAMEWORKS_BASE_COMMONPOOL_H diff --git a/libs/hwui/thread/Future.h b/libs/hwui/thread/Future.h deleted file mode 100644 index df53348e58fb..000000000000 --- a/libs/hwui/thread/Future.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -#ifndef ANDROID_HWUI_FUTURE_H -#define ANDROID_HWUI_FUTURE_H - -#include <utils/RefBase.h> - -#include "Barrier.h" - -namespace android { -namespace uirenderer { - -template <typename T> -class Future : public LightRefBase<Future<T> > { -public: - explicit Future(Condition::WakeUpType type = Condition::WAKE_UP_ONE) - : mBarrier(type), mResult() {} - ~Future() {} - - /** - * Returns the result of this future, blocking if - * the result is not available yet. - */ - T get() const { - mBarrier.wait(); - return mResult; - } - - /** - * This method must be called only once. - */ - void produce(T result) { - mResult = result; - mBarrier.open(); - } - -private: - Barrier mBarrier; - T mResult; -}; - -} // namespace uirenderer -} // namespace android - -#endif // ANDROID_HWUI_FUTURE_H diff --git a/libs/hwui/thread/Signal.h b/libs/hwui/thread/Signal.h deleted file mode 100644 index 6d33ac473ac4..000000000000 --- a/libs/hwui/thread/Signal.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -#ifndef ANDROID_HWUI_SIGNAL_H -#define ANDROID_HWUI_SIGNAL_H - -#include <stdint.h> -#include <sys/types.h> -#include <utils/threads.h> - -namespace android { -namespace uirenderer { - -class Signal { -public: - explicit Signal(Condition::WakeUpType type = Condition::WAKE_UP_ALL) - : mType(type), mSignaled(false) {} - ~Signal() {} - - void signal() { - { - Mutex::Autolock l(mLock); - mSignaled = true; - } - mCondition.signal(mType); - } - - void wait() { - Mutex::Autolock l(mLock); - while (!mSignaled) { - mCondition.wait(mLock); - } - mSignaled = false; - } - -private: - Condition::WakeUpType mType; - volatile bool mSignaled; - mutable Mutex mLock; - mutable Condition mCondition; -}; - -} // namespace uirenderer -} // namespace android - -#endif // ANDROID_HWUI_SIGNAL_H diff --git a/libs/hwui/thread/Task.h b/libs/hwui/thread/Task.h deleted file mode 100644 index 228ce19a2fd5..000000000000 --- a/libs/hwui/thread/Task.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -#ifndef ANDROID_HWUI_TASK_H -#define ANDROID_HWUI_TASK_H - -#include <utils/RefBase.h> -#include <utils/Trace.h> - -#include "Future.h" - -namespace android { -namespace uirenderer { - -class TaskBase : public RefBase { -public: - TaskBase() {} - virtual ~TaskBase() {} -}; - -template <typename T> -class Task : public TaskBase { -public: - Task() : mFuture(new Future<T>()) {} - virtual ~Task() {} - - T getResult() const { return mFuture->get(); } - - void setResult(T result) { mFuture->produce(result); } - -protected: - const sp<Future<T> >& future() const { return mFuture; } - -private: - sp<Future<T> > mFuture; -}; - -} // namespace uirenderer -} // namespace android - -#endif // ANDROID_HWUI_TASK_H diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp deleted file mode 100644 index de10ff1824ee..000000000000 --- a/libs/hwui/thread/TaskManager.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -#include <sys/resource.h> -#include <sys/sysinfo.h> - -#include "Task.h" -#include "TaskManager.h" -#include "TaskProcessor.h" -#include "utils/MathUtils.h" -#include "renderthread/RenderThread.h" - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Manager -/////////////////////////////////////////////////////////////////////////////// - -TaskManager::TaskManager() { - // Get the number of available CPUs. This value does not change over time. - int cpuCount = sysconf(_SC_NPROCESSORS_CONF); - - // Really no point in making more than 2 of these worker threads, but - // we do want to limit ourselves to 1 worker thread on dual-core devices. - int workerCount = cpuCount > 2 ? 2 : 1; - for (int i = 0; i < workerCount; i++) { - String8 name; - name.appendFormat("hwuiTask%d", i + 1); - mThreads.push_back(new WorkerThread(name)); - } -} - -TaskManager::~TaskManager() { - for (size_t i = 0; i < mThreads.size(); i++) { - mThreads[i]->exit(); - } -} - -bool TaskManager::canRunTasks() const { - return mThreads.size() > 0; -} - -void TaskManager::stop() { - for (size_t i = 0; i < mThreads.size(); i++) { - mThreads[i]->exit(); - } -} - -bool TaskManager::addTaskBase(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor) { - if (mThreads.size() > 0) { - TaskWrapper wrapper(task, processor); - - size_t minQueueSize = INT_MAX; - sp<WorkerThread> thread; - - for (size_t i = 0; i < mThreads.size(); i++) { - if (mThreads[i]->getTaskCount() < minQueueSize) { - thread = mThreads[i]; - minQueueSize = mThreads[i]->getTaskCount(); - } - } - - return thread->addTask(wrapper); - } - return false; -} - -/////////////////////////////////////////////////////////////////////////////// -// Thread -/////////////////////////////////////////////////////////////////////////////// - -status_t TaskManager::WorkerThread::readyToRun() { - setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND); - auto onStartHook = renderthread::RenderThread::getOnStartHook(); - if (onStartHook) { - onStartHook(mName.c_str()); - } - - return NO_ERROR; -} - -bool TaskManager::WorkerThread::threadLoop() { - mSignal.wait(); - std::vector<TaskWrapper> tasks; - { - Mutex::Autolock l(mLock); - tasks.swap(mTasks); - } - - for (size_t i = 0; i < tasks.size(); i++) { - const TaskWrapper& task = tasks[i]; - task.mProcessor->process(task.mTask); - } - - return true; -} - -bool TaskManager::WorkerThread::addTask(const TaskWrapper& task) { - if (!isRunning()) { - run(mName.string(), PRIORITY_DEFAULT); - } else if (exitPending()) { - return false; - } - - { - Mutex::Autolock l(mLock); - mTasks.push_back(task); - } - mSignal.signal(); - - return true; -} - -size_t TaskManager::WorkerThread::getTaskCount() const { - Mutex::Autolock l(mLock); - return mTasks.size(); -} - -void TaskManager::WorkerThread::exit() { - requestExit(); - mSignal.signal(); -} - -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h deleted file mode 100644 index 6c67222a12d7..000000000000 --- a/libs/hwui/thread/TaskManager.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -#ifndef ANDROID_HWUI_TASK_MANAGER_H -#define ANDROID_HWUI_TASK_MANAGER_H - -#include <utils/Mutex.h> -#include <utils/String8.h> -#include <utils/Thread.h> - -#include "Signal.h" - -#include <vector> - -namespace android { -namespace uirenderer { - -template <typename T> -class Task; -class TaskBase; - -template <typename T> -class TaskProcessor; -class TaskProcessorBase; - -class TaskManager { -public: - TaskManager(); - ~TaskManager(); - - /** - * Returns true if this task manager can run tasks, - * false otherwise. This method will typically return - * false on a single CPU core device. - */ - bool canRunTasks() const; - - /** - * Stops all allocated threads. Adding tasks will start - * the threads again as necessary. - */ - void stop(); - -private: - template <typename T> - friend class TaskProcessor; - - template <typename T> - bool addTask(const sp<Task<T> >& task, const sp<TaskProcessor<T> >& processor) { - return addTaskBase(sp<TaskBase>(task), sp<TaskProcessorBase>(processor)); - } - - bool addTaskBase(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor); - - struct TaskWrapper { - TaskWrapper() : mTask(), mProcessor() {} - - TaskWrapper(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor) - : mTask(task), mProcessor(processor) {} - - sp<TaskBase> mTask; - sp<TaskProcessorBase> mProcessor; - }; - - class WorkerThread : public Thread { - public: - explicit WorkerThread(const String8& name) - : Thread(false), mSignal(Condition::WAKE_UP_ONE), mName(name) {} - - bool addTask(const TaskWrapper& task); - size_t getTaskCount() const; - void exit(); - - private: - virtual status_t readyToRun() override; - virtual bool threadLoop() override; - - // Lock for the list of tasks - mutable Mutex mLock; - std::vector<TaskWrapper> mTasks; - - // Signal used to wake up the thread when a new - // task is available in the list - mutable Signal mSignal; - - const String8 mName; - }; - - std::vector<sp<WorkerThread> > mThreads; -}; - -} // namespace uirenderer -} // namespace android - -#endif // ANDROID_HWUI_TASK_MANAGER_H diff --git a/libs/hwui/thread/TaskProcessor.h b/libs/hwui/thread/TaskProcessor.h deleted file mode 100644 index 8117ae6409c4..000000000000 --- a/libs/hwui/thread/TaskProcessor.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -#ifndef ANDROID_HWUI_TASK_PROCESSOR_H -#define ANDROID_HWUI_TASK_PROCESSOR_H - -#include <utils/RefBase.h> - -#include "Task.h" -#include "TaskManager.h" - -namespace android { -namespace uirenderer { - -class TaskProcessorBase : public RefBase { -public: - TaskProcessorBase() {} - virtual ~TaskProcessorBase(){}; - - virtual void process(const sp<TaskBase>& task) = 0; -}; - -template <typename T> -class TaskProcessor : public TaskProcessorBase { -public: - explicit TaskProcessor(TaskManager* manager) : mManager(manager) {} - virtual ~TaskProcessor() {} - - void add(const sp<Task<T> >& task) { - if (!addImpl(task)) { - // fall back to immediate execution - process(task); - } - } - - virtual void onProcess(const sp<Task<T> >& task) = 0; - -private: - bool addImpl(const sp<Task<T> >& task); - - virtual void process(const sp<TaskBase>& task) override { - sp<Task<T> > realTask = static_cast<Task<T>*>(task.get()); - // This is the right way to do it but sp<> doesn't play nice - // sp<Task<T> > realTask = static_cast<sp<Task<T> > >(task); - onProcess(realTask); - } - - TaskManager* mManager; -}; - -template <typename T> -bool TaskProcessor<T>::addImpl(const sp<Task<T> >& task) { - if (mManager) { - sp<TaskProcessor<T> > self(this); - return mManager->addTask(task, self); - } - return false; -} - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_TASK_PROCESSOR_H diff --git a/libs/hwui/thread/WorkQueue.h b/libs/hwui/thread/WorkQueue.h index 7a6e638e0568..42f8503fd000 100644 --- a/libs/hwui/thread/WorkQueue.h +++ b/libs/hwui/thread/WorkQueue.h @@ -26,7 +26,6 @@ #include <functional> #include <future> #include <mutex> -#include <variant> #include <vector> namespace android::uirenderer { diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index dd179f3e9bab..050b2ec26c8d 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -167,11 +167,21 @@ public class LocationManager { /** * Broadcast intent action when the set of enabled location providers changes. To check the - * status of a provider, use {@link #isProviderEnabled(String)}. + * status of a provider, use {@link #isProviderEnabled(String)}. Depending on API level, may + * include a string intent extra, {@link #EXTRA_PROVIDER_NAME}, with the name of the provider + * whose state has changed. See {@link #EXTRA_PROVIDER_NAME} for the supported API level. + * + * @see #EXTRA_PROVIDER_NAME */ public static final String PROVIDERS_CHANGED_ACTION = "android.location.PROVIDERS_CHANGED"; /** + * Intent extra included with {@link #PROVIDERS_CHANGED_ACTION} broadcasts, containing the name + * of the location provider that has changed, to be used with location provider APIs. + */ + public static final String EXTRA_PROVIDER_NAME = "android.location.extra.PROVIDER_NAME"; + + /** * Broadcast intent action when the device location mode changes. To check the location mode, * use {@link #isLocationEnabled()}. */ diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 6a595d27d592..f9080a70206e 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1820,6 +1820,21 @@ public class AudioManager { "android.media.action.MICROPHONE_MUTE_CHANGED"; /** + * Broadcast Action: speakerphone state changed. + * + * You <em>cannot</em> receive this through components declared + * in manifests, only by explicitly registering for it with + * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter) + * Context.registerReceiver()}. + * + * <p>The intent has no extra values, use {@link #isSpeakerphoneOn} to check whether the + * speakerphone functionality is enabled or not. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SPEAKERPHONE_STATE_CHANGED = + "android.media.action.SPEAKERPHONE_STATE_CHANGED"; + + /** * Sets the audio mode. * <p> * The audio mode encompasses audio routing AND the behavior of diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 09cfa956a7a0..41e059bba92d 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -1014,9 +1014,9 @@ public class AudioTrack extends PlayerBase * 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. + * @param paddingInFrames number of frames to be ignored at the end of the stream. A value of 0 + * of 0 indicates no padding is to be applied. */ public void setOffloadDelayPadding(@IntRange(from = 0) int delayInFrames, @IntRange(from = 0) int paddingInFrames) { diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 4bc3897c4477..2ec935598c1b 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -3223,14 +3223,83 @@ public final class MediaCodecInfo { // These constants were originally in-line with OMX values, but this // correspondence is no longer maintained. + // Profiles and levels for AVC Codec, corresponding to the definitions in + // "SERIES H: AUDIOVISUAL AND MULTIMEDIA SYSTEMS, + // Infrastructure of audiovisual services – Coding of moving video + // Advanced video coding for generic audiovisual services" + // found at + // https://www.itu.int/rec/T-REC-H.264-201704-I + + /** + * AVC Baseline profile. + * See definition in + * <a href="https://www.itu.int/rec/T-REC-H.264-201704-I">H.264 recommendation</a>, + * Annex A. + */ public static final int AVCProfileBaseline = 0x01; + + /** + * AVC Main profile. + * See definition in + * <a href="https://www.itu.int/rec/T-REC-H.264-201704-I">H.264 recommendation</a>, + * Annex A. + */ public static final int AVCProfileMain = 0x02; + + /** + * AVC Extended profile. + * See definition in + * <a href="https://www.itu.int/rec/T-REC-H.264-201704-I">H.264 recommendation</a>, + * Annex A. + */ public static final int AVCProfileExtended = 0x04; + + /** + * AVC High profile. + * See definition in + * <a href="https://www.itu.int/rec/T-REC-H.264-201704-I">H.264 recommendation</a>, + * Annex A. + */ public static final int AVCProfileHigh = 0x08; + + /** + * AVC High 10 profile. + * See definition in + * <a href="https://www.itu.int/rec/T-REC-H.264-201704-I">H.264 recommendation</a>, + * Annex A. + */ public static final int AVCProfileHigh10 = 0x10; + + /** + * AVC High 4:2:2 profile. + * See definition in + * <a href="https://www.itu.int/rec/T-REC-H.264-201704-I">H.264 recommendation</a>, + * Annex A. + */ public static final int AVCProfileHigh422 = 0x20; + + /** + * AVC High 4:4:4 profile. + * See definition in + * <a href="https://www.itu.int/rec/T-REC-H.264-201704-I">H.264 recommendation</a>, + * Annex A. + */ public static final int AVCProfileHigh444 = 0x40; + + /** + * AVC Constrained Baseline profile. + * See definition in + * <a href="https://www.itu.int/rec/T-REC-H.264-201704-I">H.264 recommendation</a>, + * Annex A. + */ public static final int AVCProfileConstrainedBaseline = 0x10000; + + /** + * AVC Constrained High profile. + * See definition in + * <a href="https://www.itu.int/rec/T-REC-H.264-201704-I">H.264 recommendation</a>, + * Annex A. + */ public static final int AVCProfileConstrainedHigh = 0x80000; public static final int AVCLevel1 = 0x01; @@ -3420,8 +3489,34 @@ public final class MediaCodecInfo { public static final int DolbyVisionLevelUhd48 = 0x80; public static final int DolbyVisionLevelUhd60 = 0x100; + // Profiles and levels for AV1 Codec, corresponding to the + // definitions in + // "AV1 Bitstream & Decoding Process Specification", Annex A + // found at + // https://aomedia.org/av1-bitstream-and-decoding-process-specification/ + + /** + * AV1 Main profile. + * See definition in + * <a href="https://aomedia.org/av1-bitstream-and-decoding-process-specification/"> AV1 Bitstream and Decoding Process Specification</a> + * Annex A. + */ public static final int AV1Profile0 = 0x1; + + /** + * AV1 High profile. + * See definition in + * <a href="https://aomedia.org/av1-bitstream-and-decoding-process-specification/"> AV1 Bitstream and Decoding Process Specification</a> + * Annex A. + */ public static final int AV1Profile1 = 0x2; + + /** + * AV1 Professional profile. + * See definition in + * <a href="https://aomedia.org/av1-bitstream-and-decoding-process-specification/"> AV1 Bitstream and Decoding Process Specification</a> + * Annex A. + */ public static final int AV1Profile2 = 0x4; public static final int AV1Level2 = 0x1; diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index 579ee8debae5..aeb77cfddbbd 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -1588,13 +1588,13 @@ public final class MediaDrm implements AutoCloseable { * {@link #PROPERTY_DESCRIPTION}, {@link #PROPERTY_ALGORITHMS} */ @NonNull - public native String getPropertyString(@NonNull @StringProperty String propertyName); + public native String getPropertyString(@NonNull String propertyName); /** * Set a MediaDrm String property value, given the property name string * and new value for the property. */ - public native void setPropertyString(@NonNull @StringProperty String propertyName, + public native void setPropertyString(@NonNull String propertyName, @NonNull String value); /** @@ -1616,14 +1616,14 @@ public final class MediaDrm implements AutoCloseable { * Standard fields names are {@link #PROPERTY_DEVICE_UNIQUE_ID} */ @NonNull - public native byte[] getPropertyByteArray(@ArrayProperty String propertyName); + public native byte[] getPropertyByteArray(String propertyName); /** * Set a MediaDrm byte array property value, given the property name string * and new value for the property. */ - public native void setPropertyByteArray(@NonNull @ArrayProperty - String propertyName, @NonNull byte[] value); + public native void setPropertyByteArray( + @NonNull String propertyName, @NonNull byte[] value); private static final native void setCipherAlgorithmNative( @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm); diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java index d72476269e18..3838a99969f0 100644 --- a/media/java/android/media/MediaHTTPConnection.java +++ b/media/java/android/media/MediaHTTPConnection.java @@ -37,6 +37,7 @@ import java.net.URL; import java.net.UnknownServiceException; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; /** @hide */ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @@ -46,27 +47,23 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { // connection timeout - 30 sec private static final int CONNECT_TIMEOUT_MS = 30 * 1000; - @UnsupportedAppUsage - private long mCurrentOffset = -1; - @UnsupportedAppUsage - private URL mURL = null; - @UnsupportedAppUsage - private Map<String, String> mHeaders = null; - @UnsupportedAppUsage - private HttpURLConnection mConnection = null; - @UnsupportedAppUsage - private long mTotalSize = -1; - private InputStream mInputStream = null; - - @UnsupportedAppUsage - private boolean mAllowCrossDomainRedirect = true; - @UnsupportedAppUsage - private boolean mAllowCrossProtocolRedirect = true; - // from com.squareup.okhttp.internal.http private final static int HTTP_TEMP_REDIRECT = 307; private final static int MAX_REDIRECTS = 20; + class ConnectionState { + public HttpURLConnection mConnection = null; + public InputStream mInputStream = null; + public long mCurrentOffset = -1; + public Map<String, String> mHeaders = null; + public URL mURL = null; + public long mTotalSize = -1; + public boolean mAllowCrossDomainRedirect = true; + public boolean mAllowCrossProtocolRedirect = true; + } + private final AtomicReference<ConnectionState> mConnectionStateHolder = + new AtomicReference<ConnectionState>(); + @UnsupportedAppUsage public MediaHTTPConnection() { CookieHandler cookieHandler = CookieHandler.getDefault(); @@ -84,13 +81,23 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { Log.d(TAG, "connect: uri=" + uri + ", headers=" + headers); } + ConnectionState connectionState = mConnectionStateHolder.get(); + synchronized (this) { + if (connectionState == null) { + connectionState = new ConnectionState(); + mConnectionStateHolder.set(connectionState); + } + } + try { disconnect(); - mAllowCrossDomainRedirect = true; - mURL = new URL(uri); - mHeaders = convertHeaderStringToMap(headers); + connectionState.mAllowCrossDomainRedirect = true; + connectionState.mURL = new URL(uri); + connectionState.mHeaders = convertHeaderStringToMap(headers, connectionState); } catch (MalformedURLException e) { return null; + } finally { + mConnectionStateHolder.set(connectionState); } return native_getIMemory(); @@ -106,18 +113,21 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { } /* returns true iff header is internal */ - private boolean filterOutInternalHeaders(String key, String val) { + private boolean filterOutInternalHeaders( + String key, String val, ConnectionState connectionState) { if ("android-allow-cross-domain-redirect".equalsIgnoreCase(key)) { - mAllowCrossDomainRedirect = parseBoolean(val); + connectionState.mAllowCrossDomainRedirect = parseBoolean(val); // cross-protocol redirects are also controlled by this flag - mAllowCrossProtocolRedirect = mAllowCrossDomainRedirect; + connectionState.mAllowCrossProtocolRedirect = + connectionState.mAllowCrossDomainRedirect; } else { return false; } return true; } - private Map<String, String> convertHeaderStringToMap(String headers) { + private Map<String, String> convertHeaderStringToMap(String headers, + ConnectionState connectionState) { HashMap<String, String> map = new HashMap<String, String>(); String[] pairs = headers.split("\r\n"); @@ -127,7 +137,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { String key = pair.substring(0, colonPos); String val = pair.substring(colonPos + 1); - if (!filterOutInternalHeaders(key, val)) { + if (!filterOutInternalHeaders(key, val, connectionState)) { map.put(key, val); } } @@ -139,25 +149,28 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override @UnsupportedAppUsage public void disconnect() { - teardownConnection(); - mHeaders = null; - mURL = null; + ConnectionState connectionState = mConnectionStateHolder.getAndSet(null); + if (connectionState != null) { + teardownConnection(connectionState); + connectionState.mHeaders = null; + connectionState.mURL = null; + } } - private void teardownConnection() { - if (mConnection != null) { - if (mInputStream != null) { + private void teardownConnection(ConnectionState connectionState) { + if (connectionState.mConnection != null) { + if (connectionState.mInputStream != null) { try { - mInputStream.close(); + connectionState.mInputStream.close(); } catch (IOException e) { } - mInputStream = null; + connectionState.mInputStream = null; } - mConnection.disconnect(); - mConnection = null; + connectionState.mConnection.disconnect(); + connectionState.mConnection = null; - mCurrentOffset = -1; + connectionState.mCurrentOffset = -1; } } @@ -184,42 +197,44 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { return false; } - private void seekTo(long offset) throws IOException { - teardownConnection(); + private void seekTo(long offset, ConnectionState connectionState) throws IOException { + teardownConnection(connectionState); try { int response; int redirectCount = 0; - URL url = mURL; + URL url = connectionState.mURL; // do not use any proxy for localhost (127.0.0.1) boolean noProxy = isLocalHost(url); while (true) { if (noProxy) { - mConnection = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY); + connectionState.mConnection = + (HttpURLConnection) url.openConnection(Proxy.NO_PROXY); } else { - mConnection = (HttpURLConnection)url.openConnection(); + connectionState.mConnection = (HttpURLConnection) url.openConnection(); } - mConnection.setConnectTimeout(CONNECT_TIMEOUT_MS); + connectionState.mConnection.setConnectTimeout(CONNECT_TIMEOUT_MS); // handle redirects ourselves if we do not allow cross-domain redirect - mConnection.setInstanceFollowRedirects(mAllowCrossDomainRedirect); + connectionState.mConnection.setInstanceFollowRedirects( + connectionState.mAllowCrossDomainRedirect); - if (mHeaders != null) { - for (Map.Entry<String, String> entry : mHeaders.entrySet()) { - mConnection.setRequestProperty( + if (connectionState.mHeaders != null) { + for (Map.Entry<String, String> entry : connectionState.mHeaders.entrySet()) { + connectionState.mConnection.setRequestProperty( entry.getKey(), entry.getValue()); } } if (offset > 0) { - mConnection.setRequestProperty( + connectionState.mConnection.setRequestProperty( "Range", "bytes=" + offset + "-"); } - response = mConnection.getResponseCode(); + response = connectionState.mConnection.getResponseCode(); if (response != HttpURLConnection.HTTP_MULT_CHOICE && response != HttpURLConnection.HTTP_MOVED_PERM && response != HttpURLConnection.HTTP_MOVED_TEMP && @@ -233,7 +248,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { throw new NoRouteToHostException("Too many redirects: " + redirectCount); } - String method = mConnection.getRequestMethod(); + String method = connectionState.mConnection.getRequestMethod(); if (response == HTTP_TEMP_REDIRECT && !method.equals("GET") && !method.equals("HEAD")) { // "If the 307 status code is received in response to a @@ -241,34 +256,35 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { // automatically redirect the request" throw new NoRouteToHostException("Invalid redirect"); } - String location = mConnection.getHeaderField("Location"); + String location = connectionState.mConnection.getHeaderField("Location"); if (location == null) { throw new NoRouteToHostException("Invalid redirect"); } - url = new URL(mURL /* TRICKY: don't use url! */, location); + url = new URL(connectionState.mURL /* TRICKY: don't use url! */, location); if (!url.getProtocol().equals("https") && !url.getProtocol().equals("http")) { throw new NoRouteToHostException("Unsupported protocol redirect"); } - boolean sameProtocol = mURL.getProtocol().equals(url.getProtocol()); - if (!mAllowCrossProtocolRedirect && !sameProtocol) { + boolean sameProtocol = + connectionState.mURL.getProtocol().equals(url.getProtocol()); + if (!connectionState.mAllowCrossProtocolRedirect && !sameProtocol) { throw new NoRouteToHostException("Cross-protocol redirects are disallowed"); } - boolean sameHost = mURL.getHost().equals(url.getHost()); - if (!mAllowCrossDomainRedirect && !sameHost) { + boolean sameHost = connectionState.mURL.getHost().equals(url.getHost()); + if (!connectionState.mAllowCrossDomainRedirect && !sameHost) { throw new NoRouteToHostException("Cross-domain redirects are disallowed"); } if (response != HTTP_TEMP_REDIRECT) { // update effective URL, unless it is a Temporary Redirect - mURL = url; + connectionState.mURL = url; } } - if (mAllowCrossDomainRedirect) { + if (connectionState.mAllowCrossDomainRedirect) { // remember the current, potentially redirected URL if redirects // were handled by HttpURLConnection - mURL = mConnection.getURL(); + connectionState.mURL = connectionState.mConnection.getURL(); } if (response == HttpURLConnection.HTTP_PARTIAL) { @@ -276,10 +292,9 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { // because what we want is not just the length of the range // returned but the size of the full content if available. - String contentRange = - mConnection.getHeaderField("Content-Range"); + String contentRange = connectionState.mConnection.getHeaderField("Content-Range"); - mTotalSize = -1; + connectionState.mTotalSize = -1; if (contentRange != null) { // format is "bytes xxx-yyy/zzz // where "zzz" is the total number of bytes of the @@ -291,7 +306,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { contentRange.substring(lastSlashPos + 1); try { - mTotalSize = Long.parseLong(total); + connectionState.mTotalSize = Long.parseLong(total); } catch (NumberFormatException e) { } } @@ -299,7 +314,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { } else if (response != HttpURLConnection.HTTP_OK) { throw new IOException(); } else { - mTotalSize = mConnection.getContentLength(); + connectionState.mTotalSize = connectionState.mConnection.getContentLength(); } if (offset > 0 && response != HttpURLConnection.HTTP_PARTIAL) { @@ -308,14 +323,14 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { throw new ProtocolException(); } - mInputStream = - new BufferedInputStream(mConnection.getInputStream()); + connectionState.mInputStream = + new BufferedInputStream(connectionState.mConnection.getInputStream()); - mCurrentOffset = offset; + connectionState.mCurrentOffset = offset; } catch (IOException e) { - mTotalSize = -1; - teardownConnection(); - mCurrentOffset = -1; + connectionState.mTotalSize = -1; + teardownConnection(connectionState); + connectionState.mCurrentOffset = -1; throw e; } @@ -324,10 +339,14 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override @UnsupportedAppUsage public int readAt(long offset, int size) { - return native_readAt(offset, size); + ConnectionState connectionState = mConnectionStateHolder.get(); + if (connectionState != null) { + return native_readAt(offset, size, connectionState); + } + return -1; } - private int readAt(long offset, byte[] data, int size) { + private int readAt(long offset, byte[] data, int size, ConnectionState connectionState) { StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); @@ -335,12 +354,12 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { try { synchronized(this) { - if (offset != mCurrentOffset) { - seekTo(offset); + if (offset != connectionState.mCurrentOffset) { + seekTo(offset, connectionState); } } - int n = mInputStream.read(data, 0, size); + int n = connectionState.mInputStream.read(data, 0, size); if (n == -1) { // InputStream signals EOS using a -1 result, our semantics @@ -348,7 +367,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { n = 0; } - mCurrentOffset += n; + connectionState.mCurrentOffset += n; if (VERBOSE) { Log.d(TAG, "readAt " + offset + " / " + size + " => " + n); @@ -380,35 +399,47 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override public synchronized long getSize() { - if (mConnection == null) { - try { - seekTo(0); - } catch (IOException e) { - return -1; + ConnectionState connectionState = mConnectionStateHolder.get(); + if (connectionState != null) { + if (connectionState.mConnection == null) { + try { + seekTo(0, connectionState); + } catch (IOException e) { + return -1; + } } + return connectionState.mTotalSize; } - return mTotalSize; + return -1; } @Override @UnsupportedAppUsage public synchronized String getMIMEType() { - if (mConnection == null) { - try { - seekTo(0); - } catch (IOException e) { - return "application/octet-stream"; + ConnectionState connectionState = mConnectionStateHolder.get(); + if (connectionState != null) { + if (connectionState.mConnection == null) { + try { + seekTo(0, connectionState); + } catch (IOException e) { + return "application/octet-stream"; + } } + return connectionState.mConnection.getContentType(); } - return mConnection.getContentType(); + return null; } @Override @UnsupportedAppUsage public String getUri() { - return mURL.toString(); + ConnectionState connectionState = mConnectionStateHolder.get(); + if (connectionState != null) { + return connectionState.mURL.toString(); + } + return null; } @Override @@ -421,7 +452,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { private native final void native_finalize(); private native final IBinder native_getIMemory(); - private native final int native_readAt(long offset, int size); + private native int native_readAt(long offset, int size, ConnectionState connectionState); static { System.loadLibrary("media_jni"); diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl index cfcc2946bbdb..023484764a52 100644 --- a/media/java/android/media/session/ISessionManager.aidl +++ b/media/java/android/media/session/ISessionManager.aidl @@ -22,10 +22,10 @@ import android.media.session.IActiveSessionsListener; import android.media.session.ICallback; import android.media.session.IOnMediaKeyListener; import android.media.session.IOnVolumeKeyLongPressListener; +import android.media.session.ISession; import android.media.session.ISession2TokensListener; import android.media.session.MediaSession; import android.media.session.SessionCallbackLink; -import android.media.session.SessionLink; import android.os.Bundle; import android.view.KeyEvent; @@ -34,7 +34,7 @@ import android.view.KeyEvent; * @hide */ interface ISessionManager { - SessionLink createSession(String packageName, in SessionCallbackLink sessionCb, String tag, + ISession createSession(String packageName, in SessionCallbackLink sessionCb, String tag, in Bundle sessionInfo, int userId); void notifySession2Created(in Session2Token sessionToken); List<MediaSession.Token> getSessions(in ComponentName compName, int userId); diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 4fc436c7ed31..cbc6c9d6e451 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -36,6 +36,7 @@ import android.os.Handler; import android.os.Parcel; import android.os.Parcelable; import android.os.Process; +import android.os.RemoteException; import android.os.ResultReceiver; import android.service.media.MediaBrowserService; import android.text.TextUtils; @@ -163,11 +164,11 @@ public final class MediaSession { .getSystemService(Context.MEDIA_SESSION_SERVICE); try { SessionCallbackLink cbLink = new SessionCallbackLink(context); - SessionLink sessionLink = manager.createSession(cbLink, tag, sessionInfo); - mImpl = new MediaSessionEngine(context, sessionLink, cbLink); + ISession binder = manager.createSession(cbLink, tag, sessionInfo); + mImpl = new MediaSessionEngine(context, binder, cbLink); mMaxBitmapSize = context.getResources().getDimensionPixelSize( com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize); - } catch (RuntimeException e) { + } catch (RemoteException e) { throw new RuntimeException("Remote error creating session.", e); } } diff --git a/media/java/android/media/session/MediaSessionEngine.java b/media/java/android/media/session/MediaSessionEngine.java index 266bf3226c49..7c5243ac31cd 100644 --- a/media/java/android/media/session/MediaSessionEngine.java +++ b/media/java/android/media/session/MediaSessionEngine.java @@ -25,6 +25,7 @@ import android.content.Intent; import android.media.AudioAttributes; import android.media.MediaDescription; import android.media.MediaMetadata; +import android.media.MediaParceledListSlice; import android.media.Rating; import android.media.VolumeProvider; import android.media.session.MediaSessionManager.RemoteUserInfo; @@ -34,6 +35,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.Parcel; +import android.os.RemoteException; import android.os.ResultReceiver; import android.service.media.MediaBrowserService; import android.text.TextUtils; @@ -55,7 +57,7 @@ public final class MediaSessionEngine implements AutoCloseable { private final MediaSession.Token mSessionToken; private final MediaController mController; - private final SessionLink mSessionLink; + private final ISession mBinder; private CallbackMessageHandler mCallbackHandler; private VolumeProvider mVolumeProvider; @@ -70,14 +72,14 @@ public final class MediaSessionEngine implements AutoCloseable { * finished with the session. * * @param context The context to use to create the session. - * @param sessionLink A session link for the binder of MediaSessionRecord + * @param binder A session binder */ - public MediaSessionEngine(@NonNull Context context, @NonNull SessionLink sessionLink, - @NonNull SessionCallbackLink cbLink) { - mSessionLink = sessionLink; + public MediaSessionEngine(@NonNull Context context, @NonNull ISession binder, + @NonNull SessionCallbackLink cbLink) throws RemoteException { + mBinder = binder; cbLink.setSessionEngine(this); - mSessionToken = new MediaSession.Token(mSessionLink.getController()); + mSessionToken = new MediaSession.Token(mBinder.getController()); mController = new MediaController(context, mSessionToken); } @@ -134,8 +136,8 @@ public final class MediaSessionEngine implements AutoCloseable { */ public void setSessionActivity(@Nullable PendingIntent pi) { try { - mSessionLink.setLaunchPendingIntent(pi); - } catch (RuntimeException e) { + mBinder.setLaunchPendingIntent(pi); + } catch (RemoteException e) { Log.wtf(TAG, "Failure in setLaunchPendingIntent.", e); } } @@ -150,8 +152,8 @@ public final class MediaSessionEngine implements AutoCloseable { */ public void setMediaButtonReceiver(@Nullable PendingIntent mbr) { try { - mSessionLink.setMediaButtonReceiver(mbr); - } catch (RuntimeException e) { + mBinder.setMediaButtonReceiver(mbr); + } catch (RemoteException e) { Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e); } } @@ -163,8 +165,8 @@ public final class MediaSessionEngine implements AutoCloseable { */ public void setFlags(int flags) { try { - mSessionLink.setFlags(flags); - } catch (RuntimeException e) { + mBinder.setFlags(flags); + } catch (RemoteException e) { Log.wtf(TAG, "Failure in setFlags.", e); } } @@ -185,8 +187,8 @@ public final class MediaSessionEngine implements AutoCloseable { throw new IllegalArgumentException("Attributes cannot be null for local playback."); } try { - mSessionLink.setPlaybackToLocal(attributes); - } catch (RuntimeException e) { + mBinder.setPlaybackToLocal(attributes); + } catch (RemoteException e) { Log.wtf(TAG, "Failure in setPlaybackToLocal.", e); } } @@ -217,10 +219,10 @@ public final class MediaSessionEngine implements AutoCloseable { }); try { - mSessionLink.setPlaybackToRemote(volumeProvider.getVolumeControl(), + mBinder.setPlaybackToRemote(volumeProvider.getVolumeControl(), volumeProvider.getMaxVolume()); - mSessionLink.setCurrentVolume(volumeProvider.getCurrentVolume()); - } catch (RuntimeException e) { + mBinder.setCurrentVolume(volumeProvider.getCurrentVolume()); + } catch (RemoteException e) { Log.wtf(TAG, "Failure in setPlaybackToRemote.", e); } } @@ -238,9 +240,9 @@ public final class MediaSessionEngine implements AutoCloseable { return; } try { - mSessionLink.setActive(active); + mBinder.setActive(active); mActive = active; - } catch (RuntimeException e) { + } catch (RemoteException e) { Log.wtf(TAG, "Failure in setActive.", e); } } @@ -267,8 +269,8 @@ public final class MediaSessionEngine implements AutoCloseable { throw new IllegalArgumentException("event cannot be null or empty"); } try { - mSessionLink.sendEvent(event, extras); - } catch (RuntimeException e) { + mBinder.sendEvent(event, extras); + } catch (RemoteException e) { Log.wtf(TAG, "Error sending event", e); } } @@ -280,8 +282,8 @@ public final class MediaSessionEngine implements AutoCloseable { */ public void close() { try { - mSessionLink.destroySession(); - } catch (RuntimeException e) { + mBinder.destroySession(); + } catch (RemoteException e) { Log.wtf(TAG, "Error releasing session: ", e); } } @@ -316,8 +318,8 @@ public final class MediaSessionEngine implements AutoCloseable { public void setPlaybackState(@Nullable PlaybackState state) { mPlaybackState = state; try { - mSessionLink.setPlaybackState(state); - } catch (RuntimeException e) { + mBinder.setPlaybackState(state); + } catch (RemoteException e) { Log.wtf(TAG, "Dead object in setPlaybackState.", e); } } @@ -344,8 +346,8 @@ public final class MediaSessionEngine implements AutoCloseable { String metadataDescription = "size=" + fields + ", description=" + description; try { - mSessionLink.setMetadata(metadata, duration, metadataDescription); - } catch (RuntimeException e) { + mBinder.setMetadata(metadata, duration, metadataDescription); + } catch (RemoteException e) { Log.wtf(TAG, "Dead object in setPlaybackState.", e); } } @@ -363,8 +365,8 @@ public final class MediaSessionEngine implements AutoCloseable { */ public void setQueue(@Nullable List<MediaSession.QueueItem> queue) { try { - mSessionLink.setQueue(queue); - } catch (RuntimeException e) { + mBinder.setQueue(queue == null ? null : new MediaParceledListSlice(queue)); + } catch (RemoteException e) { Log.wtf("Dead object in setQueue.", e); } } @@ -378,8 +380,8 @@ public final class MediaSessionEngine implements AutoCloseable { */ public void setQueueTitle(@Nullable CharSequence title) { try { - mSessionLink.setQueueTitle(title); - } catch (RuntimeException e) { + mBinder.setQueueTitle(title); + } catch (RemoteException e) { Log.wtf("Dead object in setQueueTitle.", e); } } @@ -399,8 +401,8 @@ public final class MediaSessionEngine implements AutoCloseable { */ public void setRatingType(int type) { try { - mSessionLink.setRatingType(type); - } catch (RuntimeException e) { + mBinder.setRatingType(type); + } catch (RemoteException e) { Log.e(TAG, "Error in setRatingType.", e); } } @@ -414,8 +416,8 @@ public final class MediaSessionEngine implements AutoCloseable { */ public void setExtras(@Nullable Bundle extras) { try { - mSessionLink.setExtras(extras); - } catch (RuntimeException e) { + mBinder.setExtras(extras); + } catch (RemoteException e) { Log.wtf("Dead object in setExtras.", e); } } @@ -464,8 +466,8 @@ public final class MediaSessionEngine implements AutoCloseable { } } try { - mSessionLink.setCurrentVolume(provider.getCurrentVolume()); - } catch (RuntimeException e) { + mBinder.setCurrentVolume(provider.getCurrentVolume()); + } catch (RemoteException e) { Log.e(TAG, "Error in notifyVolumeChanged", e); } } diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index f530442bf087..7ca5c9398394 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -107,7 +107,7 @@ public final class MediaSessionManager { * @hide */ @NonNull - public SessionLink createSession(@NonNull SessionCallbackLink cbStub, @NonNull String tag, + public ISession createSession(@NonNull SessionCallbackLink cbStub, @NonNull String tag, @Nullable Bundle sessionInfo) { try { return mService.createSession(mContext.getPackageName(), cbStub, tag, sessionInfo, diff --git a/media/java/android/media/session/SessionLink.java b/media/java/android/media/session/SessionLink.java deleted file mode 100644 index a47c26214f25..000000000000 --- a/media/java/android/media/session/SessionLink.java +++ /dev/null @@ -1,450 +0,0 @@ -/* - * 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.media.session; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.PendingIntent; -import android.media.AudioAttributes; -import android.media.MediaMetadata; -import android.media.MediaParceledListSlice; -import android.media.session.MediaSession.QueueItem; -import android.os.Bundle; -import android.os.IBinder; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.RemoteException; - -import java.util.List; - -/** - * Handles incoming commands from {@link MediaSession}. - * @hide - */ -public final class SessionLink implements Parcelable { - public static final @android.annotation.NonNull Parcelable.Creator<SessionLink> CREATOR = - new Parcelable.Creator<SessionLink>() { - @Override - public SessionLink createFromParcel(Parcel in) { - return new SessionLink(in.readStrongBinder()); - } - - @Override - public SessionLink[] newArray(int size) { - return new SessionLink[size]; - } - }; - - final SessionStub mSessionStub; - final ISession mISession; - - /** - * Constructor for stub (Callee) - */ - public SessionLink(@NonNull SessionStub sessionStub) { - mSessionStub = sessionStub; - mISession = new StubProxy(); - } - - /** - * Constructor for interface (Caller) - */ - public SessionLink(IBinder binder) { - mSessionStub = null; - mISession = ISession.Stub.asInterface(binder); - } - - /** - * Tell system that the session sends an event to all the connected controllers. - * - * @param event the name of the event - * @param extras the extras included with the event - */ - void sendEvent(@NonNull String event, @Nullable Bundle extras) { - try { - mISession.sendEvent(event, extras); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** - * Gets the controller binder from the system. - */ - @NonNull - ISessionController getController() { - try { - return mISession.getController(); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** - * Tell system that the session sets the flags. - * - * @param flags the new session flags - */ - void setFlags(int flags) { - try { - mISession.setFlags(flags); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** - * Tell system that the session is (in)active. - * - * @param active the new activeness state - */ - void setActive(boolean active) { - try { - mISession.setActive(active); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** - * Tell system that the session sets the media button receiver. - * - * @param mbr the pending intent for media button receiver - */ - void setMediaButtonReceiver(@Nullable PendingIntent mbr) { - try { - mISession.setMediaButtonReceiver(mbr); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** - * Tell system that the session sets the pending intent for launching UI. - * - * @param pi the pending intent for launching UI - */ - void setLaunchPendingIntent(@Nullable PendingIntent pi) { - try { - mISession.setLaunchPendingIntent(pi); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** - * Tell system that the session is destroyed. - */ - void destroySession() { - try { - mISession.destroySession(); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** - * Tell system that the session sets the new metadata. - * - * @param metadata the new metadata - * @param duration the duration of the media in milliseconds - * @param metadataDescription the description of the metadata - */ - void setMetadata(@Nullable MediaMetadata metadata, long duration, - @Nullable String metadataDescription) { - try { - mISession.setMetadata(metadata, duration, metadataDescription); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** - * Tell system that the session sets the new playback state. - * - * @param state the new playback state - */ - void setPlaybackState(@Nullable PlaybackState state) { - try { - mISession.setPlaybackState(state); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** - * Tell system that the session sets the new queue. - * - * @param queue the new queue - */ - void setQueue(@Nullable List<QueueItem> queue) { - try { - mISession.setQueue(queue == null ? null : new MediaParceledListSlice(queue)); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** - * Tell system that the session sets the new queue title. - * - * @param title the new queue title - */ - void setQueueTitle(@Nullable CharSequence title) { - try { - mISession.setQueueTitle(title); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** - * Tell system that the session sets the new extras. - * - * @param extras the new extras - */ - void setExtras(@Nullable Bundle extras) { - try { - mISession.setExtras(extras); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** - * Tell system that the session sets the new rating type of the current media. - * - * @param type the rating type. - */ - void setRatingType(int type) { - try { - mISession.setRatingType(type); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** - * Tell system that the session represents a local playback. - * - * @param attributes the audio attributes of the local playback. - */ - void setPlaybackToLocal(@NonNull AudioAttributes attributes) { - try { - mISession.setPlaybackToLocal(attributes); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** - * Tell system that the session represents a remote playback. - * - * @param control the volume control type - * @param max the max volume - */ - void setPlaybackToRemote(int control, int max) { - try { - mISession.setPlaybackToRemote(control, max); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** - * Tell system that the session sets the new current volume. - * - * @param currentVolume the new current volume - */ - void setCurrentVolume(int currentVolume) { - try { - mISession.setCurrentVolume(currentVolume); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** Gets the binder */ - @NonNull - public IBinder getBinder() { - return mISession.asBinder(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeStrongBinder(mISession.asBinder()); - } - - /** - * Class for Stub implementation - */ - public abstract static class SessionStub { - /** Stub method for ISession.sendEvent */ - public void sendEvent(@NonNull String event, @Nullable Bundle data) { - } - - /** Stub method for ISession.getController */ - @NonNull - public ISessionController getController() { - return null; - } - - /** Stub method for ISession.setFlags */ - public void setFlags(int flags) { - } - - /** Stub method for ISession.setActive */ - public void setActive(boolean active) { - } - - /** Stub method for ISession.setMediaButtonReceiver */ - public void setMediaButtonReceiver(@Nullable PendingIntent mbr) { - } - - /** Stub method for ISession.setLaunchPendingIntent */ - public void setLaunchPendingIntent(@Nullable PendingIntent pi) { - } - - /** Stub method for ISession.destroySession */ - public void destroySession() { - } - - /** Stub method for ISession.setMetadata */ - public void setMetadata(@Nullable MediaMetadata metadata, long duration, - @Nullable String metadataDescription) { - } - - /** Stub method for ISession.setPlaybackState */ - public void setPlaybackState(@Nullable PlaybackState state) { - } - - /** Stub method for ISession.setQueue */ - public void setQueue(@Nullable List<QueueItem> queue) { - } - - /** Stub method for ISession.setQueueTitle */ - public void setQueueTitle(@Nullable CharSequence title) { - } - - /** Stub method for ISession.setExtras */ - public void setExtras(@Nullable Bundle extras) { - } - - /** Stub method for ISession.setRatingType */ - public void setRatingType(int type) { - } - - /** Stub method for ISession.setPlaybackToLocal */ - public void setPlaybackToLocal(@NonNull AudioAttributes attributes) { - } - - /** Stub method for ISession.setPlaybackToRemote */ - public void setPlaybackToRemote(int control, int max) { - } - - /** Stub method for ISession.setCurrentVolume */ - public void setCurrentVolume(int currentVolume) { - } - } - - private class StubProxy extends ISession.Stub { - @Override - public void sendEvent(String event, Bundle data) { - mSessionStub.sendEvent(event, data); - } - - @Override - public ISessionController getController() { - return mSessionStub.getController(); - } - - @Override - public void setFlags(int flags) { - mSessionStub.setFlags(flags); - } - - @Override - public void setActive(boolean active) { - mSessionStub.setActive(active); - } - - @Override - public void setMediaButtonReceiver(PendingIntent mbr) { - mSessionStub.setMediaButtonReceiver(mbr); - } - - @Override - public void setLaunchPendingIntent(PendingIntent pi) { - mSessionStub.setLaunchPendingIntent(pi); - } - - @Override - public void destroySession() { - mSessionStub.destroySession(); - } - - @Override - public void setMetadata(MediaMetadata metadata, long duration, String metadataDescription) { - mSessionStub.setMetadata(metadata, duration, metadataDescription); - } - - @Override - public void setPlaybackState(PlaybackState state) { - mSessionStub.setPlaybackState(state); - } - - @Override - public void setQueue(MediaParceledListSlice queue) { - mSessionStub.setQueue(queue == null ? null : queue.getList()); - } - - @Override - public void setQueueTitle(CharSequence title) { - mSessionStub.setQueueTitle(title); - } - - @Override - public void setExtras(Bundle extras) { - mSessionStub.setExtras(extras); - } - - @Override - public void setRatingType(int type) { - mSessionStub.setRatingType(type); - } - - @Override - public void setPlaybackToLocal(AudioAttributes attributes) { - mSessionStub.setPlaybackToLocal(attributes); - } - - @Override - public void setPlaybackToRemote(int control, int max) { - mSessionStub.setPlaybackToRemote(control, max); - } - - @Override - public void setCurrentVolume(int currentVolume) { - mSessionStub.setCurrentVolume(currentVolume); - } - } -} diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index 474b671c0c2b..dc2d17753b1d 100755 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java @@ -54,6 +54,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.IntStream; @@ -248,7 +249,7 @@ public class MtpDatabase implements AutoCloseable { public MtpDatabase(Context context, String volumeName, String[] subDirectories) { native_setup(); - mContext = context; + mContext = Objects.requireNonNull(context); mMediaProvider = context.getContentResolver() .acquireContentProviderClient(MediaStore.AUTHORITY); mVolumeName = volumeName; @@ -294,6 +295,10 @@ public class MtpDatabase implements AutoCloseable { } } + public Context getContext() { + return mContext; + } + @Override public void close() { mManager.close(); diff --git a/media/java/android/mtp/MtpServer.java b/media/java/android/mtp/MtpServer.java index a555d37df40d..f0ab9a329953 100644 --- a/media/java/android/mtp/MtpServer.java +++ b/media/java/android/mtp/MtpServer.java @@ -18,7 +18,12 @@ package android.mtp; import com.android.internal.util.Preconditions; +import android.content.Context; +import android.content.SharedPreferences; +import android.util.ByteStringUtils; + import java.io.FileDescriptor; +import java.util.Random; /** * Java wrapper for MTP/PTP support as USB responder. @@ -29,6 +34,12 @@ public class MtpServer implements Runnable { private long mNativeContext; // accessed by native methods private final MtpDatabase mDatabase; private final Runnable mOnTerminate; + private final Context mContext; + +// It requires "exactly 32 characters, including any leading 0s" in MTP spec +// (5.1.1.14 Serial Number) + private static final int sID_LEN_BYTES = 16; + private static final int sID_LEN_STR = (sID_LEN_BYTES * 2); static { System.loadLibrary("media_jni"); @@ -41,10 +52,41 @@ public class MtpServer implements Runnable { Runnable onTerminate, String deviceInfoManufacturer, String deviceInfoModel, - String deviceInfoDeviceVersion, - String deviceInfoSerialNumber) { + String deviceInfoDeviceVersion) { mDatabase = Preconditions.checkNotNull(database); mOnTerminate = Preconditions.checkNotNull(onTerminate); + mContext = mDatabase.getContext(); + + final String strID_PREFS_NAME = "mtp-cfg"; + final String strID_PREFS_KEY = "mtp-id"; + String strRandomId = null; + String deviceInfoSerialNumber; + + SharedPreferences sharedPref = + mContext.getSharedPreferences(strID_PREFS_NAME, Context.MODE_PRIVATE); + if (sharedPref.contains(strID_PREFS_KEY)) { + strRandomId = sharedPref.getString(strID_PREFS_KEY, null); + + // Check for format consistence (regenerate upon corruption) + if (strRandomId.length() != sID_LEN_STR) { + strRandomId = null; + } else { + // Only accept hex digit + for (int ii = 0; ii < strRandomId.length(); ii++) + if (Character.digit(strRandomId.charAt(ii), 16) == -1) { + strRandomId = null; + break; + } + } + } + + if (strRandomId == null) { + strRandomId = getRandId(); + sharedPref.edit().putString(strID_PREFS_KEY, strRandomId).apply(); + } + + deviceInfoSerialNumber = strRandomId; + native_setup( database, controlFd, @@ -56,6 +98,14 @@ public class MtpServer implements Runnable { database.setServer(this); } + private String getRandId() { + Random randomVal = new Random(); + byte[] randomBytes = new byte[sID_LEN_BYTES]; + + randomVal.nextBytes(randomBytes); + return ByteStringUtils.toHexString(randomBytes); + } + public void start() { Thread thread = new Thread(this, "MtpServer"); thread.start(); diff --git a/media/jni/android_media_MediaHTTPConnection.cpp b/media/jni/android_media_MediaHTTPConnection.cpp index 365e045689f0..d28c15c98d16 100644 --- a/media/jni/android_media_MediaHTTPConnection.cpp +++ b/media/jni/android_media_MediaHTTPConnection.cpp @@ -109,7 +109,8 @@ static void android_media_MediaHTTPConnection_native_init(JNIEnv *env) { gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J"); CHECK(gFields.context != NULL); - gFields.readAtMethodID = env->GetMethodID(clazz.get(), "readAt", "(J[BI)I"); + gFields.readAtMethodID = env->GetMethodID( + clazz.get(), "readAt", "(J[BILandroid/media/MediaHTTPConnection$ConnectionState;)I"); } static void android_media_MediaHTTPConnection_native_setup( @@ -132,7 +133,7 @@ static jobject android_media_MediaHTTPConnection_native_getIMemory( } static jint android_media_MediaHTTPConnection_native_readAt( - JNIEnv *env, jobject thiz, jlong offset, jint size) { + JNIEnv *env, jobject thiz, jlong offset, jint size, jobject connectionState) { sp<JMediaHTTPConnection> conn = getObject(env, thiz); if (size > JMediaHTTPConnection::kBufferSize) { size = JMediaHTTPConnection::kBufferSize; @@ -141,7 +142,7 @@ static jint android_media_MediaHTTPConnection_native_readAt( jbyteArray byteArrayObj = conn->getByteArrayObj(); jint n = env->CallIntMethod( - thiz, gFields.readAtMethodID, offset, byteArrayObj, size); + thiz, gFields.readAtMethodID, offset, byteArrayObj, size, connectionState); if (n > 0) { env->GetByteArrayRegion( @@ -158,7 +159,7 @@ static const JNINativeMethod gMethods[] = { { "native_getIMemory", "()Landroid/os/IBinder;", (void *)android_media_MediaHTTPConnection_native_getIMemory }, - { "native_readAt", "(JI)I", + { "native_readAt", "(JILandroid/media/MediaHTTPConnection$ConnectionState;)I", (void *)android_media_MediaHTTPConnection_native_readAt }, { "native_init", "()V", diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp index 9064ebe80da1..4c9629def113 100644 --- a/packages/CarSystemUI/Android.bp +++ b/packages/CarSystemUI/Android.bp @@ -58,8 +58,8 @@ android_app { manifest: "AndroidManifest.xml", - owner: "google", platform_apis: true, + product_specific: true, certificate: "platform", privileged: true, @@ -82,4 +82,6 @@ android_app { ], plugins: ["dagger2-compiler-2.19"], + + required: ["privapp_whitelist_com.android.systemui"], } diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp index 190247aecb2e..8872147b65ed 100644 --- a/packages/NetworkStack/Android.bp +++ b/packages/NetworkStack/Android.bp @@ -25,6 +25,7 @@ android_library { ":services-networkstack-shared-srcs", ], static_libs: [ + "ipmemorystore-client", "netd_aidl_interface-java", "networkstack-aidl-interfaces-java", "datastallprotosnano", diff --git a/packages/NetworkStack/jarjar-rules-shared.txt b/packages/NetworkStack/jarjar-rules-shared.txt index a8c712a3336d..7346b1ae81e6 100644 --- a/packages/NetworkStack/jarjar-rules-shared.txt +++ b/packages/NetworkStack/jarjar-rules-shared.txt @@ -8,12 +8,3 @@ rule android.net.shared.InetAddressUtils* android.net.networkstack.shared.InetAd rule android.net.DhcpResultsParcelable* @0 rule android.net.DhcpResults* android.net.networkstack.DhcpResults@1 rule android.net.LocalLog* android.net.networkstack.LocalLog@1 - -# TODO: remove from framework dependencies, then remove here -rule android.net.InterfaceConfigurationParcel* android.net.networkstack.InterfaceConfigurationParcel@1 -rule android.net.TetherStatsParcel* android.net.networkstack.TetherStatsParcel@1 - -# Used by UidRange, which is used by framework classes such as NetworkCapabilities. -rule android.net.UidRangeParcel* android.net.networkstack.UidRangeParcel@1 -# TODO: move TcpKeepalivePacketData to services.net and delete -rule android.net.TcpKeepalivePacketDataParcelable* android.net.networkstack.TcpKeepalivePacketDataParcelable@1
\ No newline at end of file diff --git a/packages/NetworkStack/src/android/net/NetworkStackIpMemoryStore.java b/packages/NetworkStack/src/android/net/NetworkStackIpMemoryStore.java new file mode 100644 index 000000000000..475f8261fdc1 --- /dev/null +++ b/packages/NetworkStack/src/android/net/NetworkStackIpMemoryStore.java @@ -0,0 +1,42 @@ +/* + * 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; + +import android.annotation.NonNull; +import android.content.Context; + +/** + * service used to communicate with the ip memory store service in network stack, + * which is running in the same module. + * @see com.android.server.connectivity.ipmemorystore.IpMemoryStoreService + * @hide + */ +public class NetworkStackIpMemoryStore extends IpMemoryStoreClient { + @NonNull private final IIpMemoryStore mService; + + public NetworkStackIpMemoryStore(@NonNull final Context context, + @NonNull final IIpMemoryStore service) { + super(context); + mService = service; + } + + @Override + @NonNull + protected IIpMemoryStore getService() { + return mService; + } +} diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java index 7c7cdbdb8530..b68fe235647a 100644 --- a/packages/NetworkStack/src/android/net/ip/IpClient.java +++ b/packages/NetworkStack/src/android/net/ip/IpClient.java @@ -29,6 +29,7 @@ import android.net.INetd; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; +import android.net.NetworkStackIpMemoryStore; import android.net.ProvisioningConfigurationParcelable; import android.net.ProxyInfo; import android.net.RouteInfo; @@ -61,6 +62,7 @@ import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.WakeupMessage; import com.android.server.NetworkObserverRegistry; +import com.android.server.NetworkStackService.NetworkStackServiceManager; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -100,6 +102,7 @@ public class IpClient extends StateMachine { // One holds StateMachine logs and the other connectivity packet logs. private static final ConcurrentHashMap<String, SharedLog> sSmLogs = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<String, LocalLog> sPktLogs = new ConcurrentHashMap<>(); + private final NetworkStackIpMemoryStore mIpMemoryStore; /** * Dump all state machine and connectivity packet logs to the specified writer. @@ -388,13 +391,14 @@ public class IpClient extends StateMachine { } public IpClient(Context context, String ifName, IIpClientCallbacks callback, - NetworkObserverRegistry observerRegistry) { - this(context, ifName, callback, observerRegistry, new Dependencies()); + NetworkObserverRegistry observerRegistry, NetworkStackServiceManager nssManager) { + this(context, ifName, callback, observerRegistry, nssManager, new Dependencies()); } @VisibleForTesting IpClient(Context context, String ifName, IIpClientCallbacks callback, - NetworkObserverRegistry observerRegistry, Dependencies deps) { + NetworkObserverRegistry observerRegistry, NetworkStackServiceManager nssManager, + Dependencies deps) { super(IpClient.class.getSimpleName() + "." + ifName); Preconditions.checkNotNull(ifName); Preconditions.checkNotNull(callback); @@ -408,6 +412,8 @@ public class IpClient extends StateMachine { mShutdownLatch = new CountDownLatch(1); mCm = mContext.getSystemService(ConnectivityManager.class); mObserverRegistry = observerRegistry; + mIpMemoryStore = + new NetworkStackIpMemoryStore(context, nssManager.getIpMemoryStoreService()); sSmLogs.putIfAbsent(mInterfaceName, new SharedLog(MAX_LOG_RECORDS, mTag)); mLog = sSmLogs.get(mInterfaceName); diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java index e7c8e8578f81..335d9513bddb 100644 --- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java +++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java @@ -29,6 +29,8 @@ import android.app.Service; import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; +import android.net.IIpMemoryStore; +import android.net.IIpMemoryStoreCallbacks; import android.net.INetd; import android.net.INetworkMonitor; import android.net.INetworkMonitorCallbacks; @@ -49,6 +51,7 @@ import android.os.RemoteException; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.IndentingPrintWriter; import com.android.server.connectivity.NetworkMonitor; +import com.android.server.connectivity.ipmemorystore.IpMemoryStoreService; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -86,7 +89,19 @@ public class NetworkStackService extends Service { return makeConnector(this); } - private static class NetworkStackConnector extends INetworkStackConnector.Stub { + /** + * An interface for internal clients of the network stack service that can return + * or create inline instances of the service it manages. + */ + public interface NetworkStackServiceManager { + /** + * Get an instance of the IpMemoryStoreService. + */ + IIpMemoryStore getIpMemoryStoreService(); + } + + private static class NetworkStackConnector extends INetworkStackConnector.Stub + implements NetworkStackServiceManager { private static final int NUM_VALIDATION_LOG_LINES = 20; private final Context mContext; private final INetd mNetd; @@ -94,6 +109,7 @@ public class NetworkStackService extends Service { private final ConnectivityManager mCm; @GuardedBy("mIpClients") private final ArrayList<WeakReference<IpClient>> mIpClients = new ArrayList<>(); + private final IpMemoryStoreService mIpMemoryStoreService; private static final int MAX_VALIDATION_LOGS = 10; @GuardedBy("mValidationLogs") @@ -116,6 +132,7 @@ public class NetworkStackService extends Service { (IBinder) context.getSystemService(Context.NETD_SERVICE)); mObserverRegistry = new NetworkObserverRegistry(); mCm = context.getSystemService(ConnectivityManager.class); + mIpMemoryStoreService = new IpMemoryStoreService(context); try { mObserverRegistry.register(mNetd); @@ -159,7 +176,7 @@ public class NetworkStackService extends Service { @Override public void makeIpClient(String ifName, IIpClientCallbacks cb) throws RemoteException { - final IpClient ipClient = new IpClient(mContext, ifName, cb, mObserverRegistry); + final IpClient ipClient = new IpClient(mContext, ifName, cb, mObserverRegistry, this); synchronized (mIpClients) { final Iterator<WeakReference<IpClient>> it = mIpClients.iterator(); @@ -176,6 +193,17 @@ public class NetworkStackService extends Service { } @Override + public IIpMemoryStore getIpMemoryStoreService() { + return mIpMemoryStoreService; + } + + @Override + public void fetchIpMemoryStore(@NonNull final IIpMemoryStoreCallbacks cb) + throws RemoteException { + cb.onIpMemoryStoreFetched(mIpMemoryStoreService); + } + + @Override protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout, @Nullable String[] args) { checkDumpPermission(); diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java index bcfc412c6f4e..9d91487fec82 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java @@ -72,7 +72,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.telephony.AccessNetworkConstants; import android.telephony.CellSignalStrength; -import android.telephony.NetworkRegistrationState; +import android.telephony.NetworkRegistrationInfo; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.TelephonyManager; @@ -1615,12 +1615,12 @@ public class NetworkMonitor extends StateMachine { return; } // See if the data sub is registered for PS services on cell. - final NetworkRegistrationState nrs = dataSs.getNetworkRegistrationState( - NetworkRegistrationState.DOMAIN_PS, + final NetworkRegistrationInfo nri = dataSs.getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); latencyBroadcast.putExtra( NetworkMonitorUtils.EXTRA_CELL_ID, - nrs == null ? null : nrs.getCellIdentity()); + nri == null ? null : nri.getCellIdentity()); latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_MOBILE); } else { return; diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java index bbecc6359a40..4d4ceed9cb52 100644 --- a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.net.ipmemorystore; +package com.android.server.connectivity.ipmemorystore; import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH; import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH; diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java index d43dc6a24260..f801b355c43e 100644 --- a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.android.server.net.ipmemorystore; +package com.android.server.connectivity.ipmemorystore; import static android.net.ipmemorystore.Status.ERROR_DATABASE_CANNOT_BE_OPENED; import static android.net.ipmemorystore.Status.ERROR_GENERIC; import static android.net.ipmemorystore.Status.ERROR_ILLEGAL_ARGUMENT; import static android.net.ipmemorystore.Status.SUCCESS; -import static com.android.server.net.ipmemorystore.IpMemoryStoreDatabase.EXPIRY_ERROR; +import static com.android.server.connectivity.ipmemorystore.IpMemoryStoreDatabase.EXPIRY_ERROR; import android.annotation.NonNull; import android.annotation.Nullable; @@ -40,7 +40,6 @@ import android.net.ipmemorystore.NetworkAttributesParcelable; import android.net.ipmemorystore.SameL3NetworkResponse; import android.net.ipmemorystore.Status; import android.net.ipmemorystore.StatusParcelable; -import android.net.ipmemorystore.Utils; import android.os.RemoteException; import android.util.Log; diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/RelevanceUtils.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RelevanceUtils.java index aa454008958d..38d55448aa2a 100644 --- a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/RelevanceUtils.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RelevanceUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.net.ipmemorystore; +package com.android.server.connectivity.ipmemorystore; import com.android.internal.annotations.VisibleForTesting; diff --git a/core/java/android/net/ipmemorystore/Utils.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/Utils.java index b361aca5a6f7..9cbf490505f4 100644 --- a/core/java/android/net/ipmemorystore/Utils.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/Utils.java @@ -14,10 +14,11 @@ * limitations under the License. */ -package android.net.ipmemorystore; +package com.android.server.connectivity.ipmemorystore; import android.annotation.NonNull; import android.annotation.Nullable; +import android.net.ipmemorystore.Blob; /** {@hide} */ public class Utils { diff --git a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java index 4536c473915a..eee12d6f8c7c 100644 --- a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java +++ b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java @@ -51,6 +51,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; import com.android.server.NetworkObserver; import com.android.server.NetworkObserverRegistry; +import com.android.server.NetworkStackService; import org.junit.Before; import org.junit.Test; @@ -90,6 +91,7 @@ public class IpClientTest { @Mock private AlarmManager mAlarm; @Mock private IpClient.Dependencies mDependencies; @Mock private ContentResolver mContentResolver; + @Mock private NetworkStackService.NetworkStackServiceManager mNetworkStackServiceManager; private NetworkObserver mObserver; private InterfaceParams mIfParams; @@ -118,7 +120,8 @@ public class IpClientTest { private IpClient makeIpClient(String ifname) throws Exception { setTestInterfaceParams(ifname); - final IpClient ipc = new IpClient(mContext, ifname, mCb, mObserverRegistry, mDependencies); + final IpClient ipc = new IpClient(mContext, ifname, mCb, mObserverRegistry, + mNetworkStackServiceManager, mDependencies); verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(ifname, false); verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(ifname); ArgumentCaptor<NetworkObserver> arg = ArgumentCaptor.forClass(NetworkObserver.class); @@ -142,8 +145,8 @@ public class IpClientTest { public void testNullInterfaceNameMostDefinitelyThrows() throws Exception { setTestInterfaceParams(null); try { - final IpClient ipc = new IpClient( - mContext, null, mCb, mObserverRegistry, mDependencies); + final IpClient ipc = new IpClient(mContext, null, mCb, mObserverRegistry, + mNetworkStackServiceManager, mDependencies); ipc.shutdown(); fail(); } catch (NullPointerException npe) { @@ -156,8 +159,8 @@ public class IpClientTest { final String ifname = "lo"; setTestInterfaceParams(ifname); try { - final IpClient ipc = new IpClient( - mContext, ifname, null, mObserverRegistry, mDependencies); + final IpClient ipc = new IpClient(mContext, ifname, null, mObserverRegistry, + mNetworkStackServiceManager, mDependencies); ipc.shutdown(); fail(); } catch (NullPointerException npe) { @@ -168,16 +171,16 @@ public class IpClientTest { @Test public void testInvalidInterfaceDoesNotThrow() throws Exception { setTestInterfaceParams(TEST_IFNAME); - final IpClient ipc = new IpClient( - mContext, TEST_IFNAME, mCb, mObserverRegistry, mDependencies); + final IpClient ipc = new IpClient(mContext, TEST_IFNAME, mCb, mObserverRegistry, + mNetworkStackServiceManager, mDependencies); ipc.shutdown(); } @Test public void testInterfaceNotFoundFailsImmediately() throws Exception { setTestInterfaceParams(null); - final IpClient ipc = new IpClient( - mContext, TEST_IFNAME, mCb, mObserverRegistry, mDependencies); + final IpClient ipc = new IpClient(mContext, TEST_IFNAME, mCb, mObserverRegistry, + mNetworkStackServiceManager, mDependencies); ipc.startProvisioning(new ProvisioningConfiguration()); verify(mCb, times(1)).onProvisioningFailure(any()); ipc.shutdown(); diff --git a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java index be1068065b05..d0e58b817e9d 100644 --- a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java +++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.net.ipmemorystore; +package com.android.server.connectivity.ipmemorystore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/tests/net/java/com/android/server/net/ipmemorystore/RelevanceUtilsTests.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/RelevanceUtilsTests.java index 7413b914dbe9..3d3aabc66e70 100644 --- a/tests/net/java/com/android/server/net/ipmemorystore/RelevanceUtilsTests.java +++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/RelevanceUtilsTests.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.net.ipmemorystore; +package com.android.server.connectivity.ipmemorystore; -import static com.android.server.net.ipmemorystore.RelevanceUtils.CAPPED_RELEVANCE; +import static com.android.server.connectivity.ipmemorystore.RelevanceUtils.CAPPED_RELEVANCE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml index e27ae7d6d439..da575dbd7fac 100644 --- a/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml +++ b/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml @@ -21,6 +21,8 @@ style="@style/EntityHeader" android:layout_width="match_parent" android:layout_height="wrap_content" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:orientation="horizontal"> <LinearLayout diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java index c9fbc7ba9f05..d4d0519fcc5f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java @@ -128,12 +128,12 @@ public class ApplicationsState { // to protect access to these. final ArrayList<Session> mSessions = new ArrayList<Session>(); final ArrayList<Session> mRebuildingSessions = new ArrayList<Session>(); - final InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges(); + private InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges(); // Map: userid => (Map: package name => AppEntry) final SparseArray<HashMap<String, AppEntry>> mEntriesMap = new SparseArray<HashMap<String, AppEntry>>(); final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>(); - List<ApplicationInfo> mApplications = new ArrayList<ApplicationInfo>(); + List<ApplicationInfo> mApplications = new ArrayList<>(); long mCurId = 1; UUID mCurComputingSizeUuid; String mCurComputingSizePkg; @@ -166,7 +166,7 @@ public class ApplicationsState { /** * Flags to configure the session to request various types of info. */ - @IntDef(prefix = { "FLAG_SESSION_" }, value = { + @IntDef(prefix = {"FLAG_SESSION_"}, value = { FLAG_SESSION_REQUEST_HOME_APP, FLAG_SESSION_REQUEST_ICONS, FLAG_SESSION_REQUEST_SIZES, @@ -174,7 +174,13 @@ public class ApplicationsState { FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER }) @Retention(RetentionPolicy.SOURCE) - public @interface SessionFlags {} + public @interface SessionFlags { + } + + @VisibleForTesting + void setInterestingConfigChanges(InterestingConfigChanges interestingConfigChanges) { + mInterestingConfigChanges = interestingConfigChanges; + } public static final @SessionFlags int DEFAULT_SESSION_FLAGS = FLAG_SESSION_REQUEST_HOME_APP | FLAG_SESSION_REQUEST_ICONS | @@ -190,6 +196,7 @@ public class ApplicationsState { for (int userId : mUm.getProfileIdsWithDisabled(UserHandle.myUserId())) { mEntriesMap.put(userId, new HashMap<String, AppEntry>()); } + mThread = new HandlerThread("ApplicationsState.Loader", Process.THREAD_PRIORITY_BACKGROUND); mThread.start(); @@ -256,12 +263,14 @@ public class ApplicationsState { mPackageIntentReceiver = new PackageIntentReceiver(); mPackageIntentReceiver.registerReceiver(); } - mApplications = new ArrayList<ApplicationInfo>(); + + final List<ApplicationInfo> prevApplications = mApplications; + mApplications = new ArrayList<>(); for (UserInfo user : mUm.getProfiles(UserHandle.myUserId())) { try { // If this user is new, it needs a map created. if (mEntriesMap.indexOfKey(user.id) < 0) { - mEntriesMap.put(user.id, new HashMap<String, AppEntry>()); + mEntriesMap.put(user.id, new HashMap<>()); } @SuppressWarnings("unchecked") ParceledListSlice<ApplicationInfo> list = @@ -279,14 +288,14 @@ public class ApplicationsState { // should completely reload the app entries. clearEntries(); } else { - for (int i=0; i<mAppEntries.size(); i++) { + for (int i = 0; i < mAppEntries.size(); i++) { mAppEntries.get(i).sizeStale = true; } } mHaveDisabledApps = false; mHaveInstantApps = false; - for (int i=0; i<mApplications.size(); i++) { + for (int i = 0; i < mApplications.size(); i++) { final ApplicationInfo info = mApplications.get(i); // Need to trim out any applications that are disabled by // something different than the user. @@ -312,8 +321,9 @@ public class ApplicationsState { entry.info = info; } } - if (mAppEntries.size() > mApplications.size()) { - // There are less apps now, some must have been uninstalled. + + if (anyAppIsRemoved(prevApplications, mApplications)) { + // some apps have been uninstalled. clearEntries(); } mCurComputingSizePkg = null; @@ -322,6 +332,82 @@ public class ApplicationsState { } } + /* The original design is mAppEntries.size() > mApplications.size(). + It's correct if there is only the owner user and only one app is removed. + Problem 1: + If there is a user profile, the size of mAppEntries < mApplications is normal because + the number of app entries on UI (mAppEntries) should be equal to the number of apps got + from PMS (mApplications). + + owner only case: + mApplications: user 0: 191 + mAppEntries : user 0: 191 + total mAppEntries: 191, mApplications: 191 + If an app is removed, cached mAppEntries: 191 , mApplications: 191 -> 190, it is detected + as the number of apps becomes less. + + If there is a work profile, mAppEntries removes some apps that are not installed for the + owner user. + + For example, in the following case, 6 apps are removed from mAppEntries for the owner. + mApplications: user 0: 197, user 10: 189 => total 386 + mAppEntries : user 0: 191, user 10: 189 => total 380 + If an app is removed, cached mAppEntries: 380 , mApplications: 386 -> 385, the size of + mAppEntries is still not larger than mApplications, then does not clear mAppEntries. + + Problem 2: + If remove an app and add another app outside Settings (e.g. Play Store) and back to + Settings, the amount of apps are not changed, it causes the entries keep the removed app. + + Another case, if adding more apps than removing apps (e.g. add 2 apps and remove 1 app), + the final number of apps (mApplications) is even increased, + + Therefore, should not only count on number of apps to determine any app is removed. + Compare the change of applications instead. + */ + private static boolean anyAppIsRemoved(List<ApplicationInfo> prevApplications, + List<ApplicationInfo> applications) { + + // No cache + if (prevApplications.size() == 0) { + return false; + } + + if (applications.size() < prevApplications.size()) { + return true; + } + + // build package sets of all applications <userId, HashSet of packages> + final HashMap<String, HashSet<String>> packageMap = new HashMap<>(); + for (ApplicationInfo application : applications) { + final String userId = String.valueOf(UserHandle.getUserId(application.uid)); + + HashSet<String> appPackages = packageMap.get(userId); + if (appPackages == null) { + appPackages = new HashSet<>(); + packageMap.put(userId, appPackages); + } + if (hasFlag(application.flags, ApplicationInfo.FLAG_INSTALLED)) { + appPackages.add(application.packageName); + } + } + + // detect any previous app is removed + for (ApplicationInfo prevApplication : prevApplications) { + if (!hasFlag(prevApplication.flags, ApplicationInfo.FLAG_INSTALLED)) { + continue; + } + final String userId = String.valueOf(UserHandle.getUserId(prevApplication.uid)); + + final HashSet<String> packagesSet = packageMap.get(userId); + if (packagesSet == null || !packagesSet.remove(prevApplication.packageName)) { + return true; + } + } + + return false; + } + @VisibleForTesting void clearEntries() { for (int i = 0; i < mEntriesMap.size(); i++) { @@ -346,7 +432,7 @@ public class ApplicationsState { if (!mResumed) { return; } - for (int i=0; i<mSessions.size(); i++) { + for (int i = 0; i < mSessions.size(); i++) { if (mSessions.get(i).mResumed) { return; } @@ -449,7 +535,7 @@ public class ApplicationsState { if (DEBUG_LOCKING) Log.v(TAG, "sumCacheSizes about to acquire lock..."); synchronized (mEntriesMap) { if (DEBUG_LOCKING) Log.v(TAG, "-> sumCacheSizes now has lock"); - for (int i=mAppEntries.size()-1; i>=0; i--) { + for (int i = mAppEntries.size() - 1; i >= 0; i--) { sum += mAppEntries.get(i).cacheSize; } if (DEBUG_LOCKING) Log.v(TAG, "...sumCacheSizes releasing lock"); @@ -458,7 +544,7 @@ public class ApplicationsState { } int indexOfApplicationInfoLocked(String pkgName, int userId) { - for (int i=mApplications.size()-1; i>=0; i--) { + for (int i = mApplications.size() - 1; i >= 0; i--) { ApplicationInfo appInfo = mApplications.get(i); if (appInfo.packageName.equals(pkgName) && UserHandle.getUserId(appInfo.uid) == userId) { @@ -642,7 +728,7 @@ public class ApplicationsState { return; } mActiveSessions.clear(); - for (int i=0; i<mSessions.size(); i++) { + for (int i = 0; i < mSessions.size(); i++) { Session s = mSessions.get(i); if (s.mResumed) { mActiveSessions.add(new WeakReference<>(s)); @@ -784,7 +870,7 @@ public class ApplicationsState { ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>(); if (DEBUG) Log.i(TAG, "Rebuilding..."); - for (int i=0; i<apps.size(); i++) { + for (int i = 0; i < apps.size(); i++) { AppEntry entry = apps.get(i); if (entry != null && (filter == null || filter.filterApp(entry))) { synchronized (mEntriesMap) { @@ -954,7 +1040,7 @@ public class ApplicationsState { } } if (rebuildingSessions != null) { - for (int i=0; i<rebuildingSessions.size(); i++) { + for (int i = 0; i < rebuildingSessions.size(); i++) { rebuildingSessions.get(i).handleRebuildList(); } } @@ -1047,9 +1133,9 @@ public class ApplicationsState { // If we do not specify MATCH_DIRECT_BOOT_AWARE or // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags // according to the user's lock state. When the user is locked, - // components - // with ComponentInfo#directBootAware == false will be filtered. We should - // explicitly include both direct boot aware and unaware components here. + // components with ComponentInfo#directBootAware == false will be + // filtered. W should explicitly include both direct boot aware and + // unaware component here. List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser( launchIntent, PackageManager.MATCH_DISABLED_COMPONENTS @@ -1128,8 +1214,10 @@ public class ApplicationsState { synchronized (mEntriesMap) { if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock"); if (mCurComputingSizePkg != null) { - if (DEBUG_LOCKING) Log.v(TAG, - "MSG_LOAD_SIZES releasing: currently computing"); + if (DEBUG_LOCKING) { + Log.v(TAG, + "MSG_LOAD_SIZES releasing: currently computing"); + } return; } @@ -1181,8 +1269,10 @@ public class ApplicationsState { }); } - if (DEBUG_LOCKING) Log.v(TAG, - "MSG_LOAD_SIZES releasing: now computing"); + if (DEBUG_LOCKING) { + Log.v(TAG, + "MSG_LOAD_SIZES releasing: now computing"); + } return; } } @@ -1255,8 +1345,10 @@ public class ApplicationsState { entry.internalSizeStr = getSizeStr(entry.internalSize); entry.externalSize = getTotalExternalSize(stats); entry.externalSizeStr = getSizeStr(entry.externalSize); - if (DEBUG) Log.i(TAG, "Set size of " + entry.label + " " + entry - + ": " + entry.sizeStr); + if (DEBUG) { + Log.i(TAG, "Set size of " + entry.label + " " + entry + + ": " + entry.sizeStr); + } sizeChanged = true; } } @@ -1299,9 +1391,11 @@ public class ApplicationsState { userFilter.addAction(Intent.ACTION_USER_REMOVED); mContext.registerReceiver(this, userFilter); } + void unregisterReceiver() { mContext.unregisterReceiver(this); } + @Override public void onReceive(Context context, Intent intent) { String actionStr = intent.getAction(); @@ -1354,12 +1448,19 @@ public class ApplicationsState { public interface Callbacks { void onRunningStateChanged(boolean running); + void onPackageListChanged(); + void onRebuildComplete(ArrayList<AppEntry> apps); + void onPackageIconChanged(); + void onPackageSizeChanged(String packageName); + void onAllSizesComputed(); + void onLauncherInfoChanged(); + void onLoadEntriesCompleted(); } @@ -1491,6 +1592,7 @@ public class ApplicationsState { */ public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() { private final Collator sCollator = Collator.getInstance(); + @Override public int compare(AppEntry object1, AppEntry object2) { int compareResult = sCollator.compare(object1.label, object2.label); @@ -1504,6 +1606,7 @@ public class ApplicationsState { return compareResult; } } + return object1.info.uid - object2.info.uid; } }; @@ -1540,9 +1643,11 @@ public class ApplicationsState { public interface AppFilter { void init(); + default void init(Context context) { init(); } + boolean filterApp(AppEntry info); } @@ -1697,7 +1802,8 @@ public class ApplicationsState { @Override public boolean filterApp(AppEntry entry) { return !AppUtils.isInstant(entry.info) - && hasFlag(entry.info.privateFlags, ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS); + && hasFlag(entry.info.privateFlags, + ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS); } }; @@ -1707,7 +1813,7 @@ public class ApplicationsState { @Override public void init(Context context) { mHidePackageNames = context.getResources() - .getStringArray(R.array.config_hideWhenDisabled_packageNames); + .getStringArray(R.array.config_hideWhenDisabled_packageNames); } @Override @@ -1720,7 +1826,7 @@ public class ApplicationsState { if (!entry.info.enabled) { return false; } else if (entry.info.enabledSetting == - PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { + PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { return false; } } @@ -1798,7 +1904,7 @@ public class ApplicationsState { @Override public boolean filterApp(AppEntry entry) { boolean isMusicApp; - synchronized(entry) { + synchronized (entry) { isMusicApp = entry.info.category == ApplicationInfo.CATEGORY_AUDIO; } return isMusicApp; @@ -1813,7 +1919,7 @@ public class ApplicationsState { @Override public boolean filterApp(AppEntry entry) { boolean isMovieApp; - synchronized(entry) { + synchronized (entry) { isMovieApp = entry.info.category == ApplicationInfo.CATEGORY_VIDEO; } return isMovieApp; @@ -1823,7 +1929,8 @@ public class ApplicationsState { public static final AppFilter FILTER_PHOTOS = new AppFilter() { @Override - public void init() {} + public void init() { + } @Override public boolean filterApp(AppEntry entry) { @@ -1838,7 +1945,8 @@ public class ApplicationsState { public static final AppFilter FILTER_OTHER_APPS = new AppFilter() { @Override - public void init() {} + public void init() { + } @Override public boolean filterApp(AppEntry entry) { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java index a098ecc17b3d..b27efd0edc8b 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java @@ -16,11 +16,17 @@ package com.android.settingslib.applications; +import static android.os.UserHandle.MU_ENABLED; + import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.shadow.api.Shadow.extract; @@ -40,10 +46,13 @@ import android.content.pm.ModuleInfo; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; +import android.content.res.Resources; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; +import android.os.RemoteException; import android.os.UserHandle; +import android.os.UserManager; import android.text.TextUtils; import android.util.IconDrawableFactory; @@ -70,6 +79,7 @@ import org.robolectric.shadows.ShadowContextImpl; import org.robolectric.shadows.ShadowLooper; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -82,6 +92,19 @@ public class ApplicationsStateRoboTest { private final static String HOME_PACKAGE_NAME = "com.android.home"; private final static String LAUNCHABLE_PACKAGE_NAME = "com.android.launchable"; + private static final int PROFILE_USERID = 10; + + private static final String PKG_1 = "PKG1"; + private static final int OWNER_UID_1 = 1001; + private static final int PROFILE_UID_1 = UserHandle.getUid(PROFILE_USERID, OWNER_UID_1); + + private static final String PKG_2 = "PKG2"; + private static final int OWNER_UID_2 = 1002; + private static final int PROFILE_UID_2 = UserHandle.getUid(PROFILE_USERID, OWNER_UID_2); + + private static final String PKG_3 = "PKG3"; + private static final int OWNER_UID_3 = 1003; + /** Class under test */ private ApplicationsState mApplicationsState; private Session mSession; @@ -171,7 +194,7 @@ public class ApplicationsStateRoboTest { storageStats.dataBytes = 20; storageStats.cacheBytes = 30; when(mStorageStatsManager.queryStatsForPackage(any(UUID.class), - anyString(), any(UserHandle.class))).thenReturn(storageStats); + anyString(), any(UserHandle.class))).thenReturn(storageStats); // Set up 3 installed apps, in which 1 is hidden module final List<ApplicationInfo> infos = new ArrayList<>(); @@ -195,11 +218,16 @@ public class ApplicationsStateRoboTest { } private ApplicationInfo createApplicationInfo(String packageName) { + return createApplicationInfo(packageName, 0); + } + + private ApplicationInfo createApplicationInfo(String packageName, int uid) { ApplicationInfo appInfo = new ApplicationInfo(); appInfo.sourceDir = "foo"; appInfo.flags |= ApplicationInfo.FLAG_INSTALLED; appInfo.storageUuid = UUID.randomUUID(); appInfo.packageName = packageName; + appInfo.uid = uid; return appInfo; } @@ -211,10 +239,14 @@ public class ApplicationsStateRoboTest { } private void addApp(String packageName, int id) { - ApplicationInfo appInfo = createApplicationInfo(packageName); + addApp(packageName, id, 0); + } + + private void addApp(String packageName, int id, int userId) { + ApplicationInfo appInfo = createApplicationInfo(packageName, id); AppEntry appEntry = createAppEntry(appInfo, id); mApplicationsState.mAppEntries.add(appEntry); - mApplicationsState.mEntriesMap.get(0).put(appInfo.packageName, appEntry); + mApplicationsState.mEntriesMap.get(userId).put(appInfo.packageName, appEntry); } private void processAllMessages() { @@ -351,4 +383,328 @@ public class ApplicationsStateRoboTest { assertThat(mApplications.get(1).packageName).isEqualTo("test.package.3"); } + @Test + public void removeAndInstall_noWorkprofile_doResumeIfNeededLocked_shouldClearEntries() + throws RemoteException { + // scenario: only owner user + // (PKG_1, PKG_2) -> (PKG_2, PKG_3) + // PKG_1 is removed and PKG_3 is installed before app is resumed. + ApplicationsState.sInstance = null; + mApplicationsState = spy( + ApplicationsState + .getInstance(RuntimeEnvironment.application, mock(IPackageManager.class))); + + // Previous Applications: + ApplicationInfo appInfo; + final ArrayList<ApplicationInfo> prevAppList = new ArrayList<>(); + appInfo = createApplicationInfo(PKG_1, OWNER_UID_1); + prevAppList.add(appInfo); + appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); + prevAppList.add(appInfo); + mApplicationsState.mApplications = prevAppList; + + // Previous Entries: + // (PKG_1, PKG_2) + addApp(PKG_1, OWNER_UID_1, 0); + addApp(PKG_2, OWNER_UID_2, 0); + + // latest Applications: + // (PKG_2, PKG_3) + final ArrayList<ApplicationInfo> appList = new ArrayList<>(); + appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); + appList.add(appInfo); + appInfo = createApplicationInfo(PKG_3, OWNER_UID_3); + appList.add(appInfo); + setupDoResumeIfNeededLocked(appList, null); + + mApplicationsState.doResumeIfNeededLocked(); + + verify(mApplicationsState).clearEntries(); + } + + @Test + public void noAppRemoved_noWorkprofile_doResumeIfNeededLocked_shouldNotClearEntries() + throws RemoteException { + // scenario: only owner user + // (PKG_1, PKG_2) + ApplicationsState.sInstance = null; + mApplicationsState = spy( + ApplicationsState + .getInstance(RuntimeEnvironment.application, mock(IPackageManager.class))); + + ApplicationInfo appInfo; + // Previous Applications + final ArrayList<ApplicationInfo> prevAppList = new ArrayList<>(); + appInfo = createApplicationInfo(PKG_1, OWNER_UID_1); + prevAppList.add(appInfo); + appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); + prevAppList.add(appInfo); + mApplicationsState.mApplications = prevAppList; + + // Previous Entries: + // (pk1, PKG_2) + addApp(PKG_1, OWNER_UID_1, 0); + addApp(PKG_2, OWNER_UID_2, 0); + + // latest Applications: + // (PKG_2, PKG_3) + final ArrayList<ApplicationInfo> appList = new ArrayList<>(); + appInfo = createApplicationInfo(PKG_1, OWNER_UID_1); + appList.add(appInfo); + appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); + appList.add(appInfo); + setupDoResumeIfNeededLocked(appList, null); + + mApplicationsState.doResumeIfNeededLocked(); + + verify(mApplicationsState, never()).clearEntries(); + } + + @Test + public void removeProfileApp_workprofileExists_doResumeIfNeededLocked_shouldClearEntries() + throws RemoteException { + if (!MU_ENABLED) { + return; + } + // [Preconditions] + // 2 apps (PKG_1, PKG_2) for owner, PKG_1 is not in installed state + // 2 apps (PKG_1, PKG_2) for non-owner. + // + // [Actions] + // profile user's PKG_2 is removed before resume + // + // Applications: + // owner - (PKG_1 - uninstalled, PKG_2) -> (PKG_1 - uninstalled, PKG_2) + // profile - (PKG_1, PKG_2) -> (PKG_1) + // + // Previous Entries: + // owner - (PKG_2) + // profile - (PKG_1, PKG_2) + + ShadowUserManager shadowUserManager = Shadow + .extract(RuntimeEnvironment.application.getSystemService(UserManager.class)); + shadowUserManager.addProfile(PROFILE_USERID, "profile"); + + ApplicationsState.sInstance = null; + mApplicationsState = spy( + ApplicationsState + .getInstance(RuntimeEnvironment.application, mock(IPackageManager.class))); + + ApplicationInfo appInfo; + // Previous Applications + // owner - (PKG_1 - uninstalled, PKG_2) + // profile - (PKG_1, PKG_2) + final ArrayList<ApplicationInfo> prevAppList = new ArrayList<>(); + appInfo = createApplicationInfo(PKG_1, OWNER_UID_1); + appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED; + prevAppList.add(appInfo); + appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); + prevAppList.add(appInfo); + + appInfo = createApplicationInfo(PKG_1, PROFILE_UID_1); + prevAppList.add(appInfo); + appInfo = createApplicationInfo(PKG_2, PROFILE_UID_2); + prevAppList.add(appInfo); + + mApplicationsState.mApplications = prevAppList; + // Previous Entries: + // owner (PKG_2), profile (pk1, PKG_2) + // PKG_1 is not installed for owner, hence it's removed from entries + addApp(PKG_2, OWNER_UID_2, 0); + addApp(PKG_1, PROFILE_UID_1, PROFILE_USERID); + addApp(PKG_2, PROFILE_UID_2, PROFILE_USERID); + + // latest Applications: + // owner (PKG_1, PKG_2), profile (PKG_1) + // owner's PKG_1 is still listed and is in non-installed state + // profile user's PKG_2 is removed by a user before resume + //owner + final ArrayList<ApplicationInfo> ownerAppList = new ArrayList<>(); + appInfo = createApplicationInfo(PKG_1, OWNER_UID_1); + appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED; + ownerAppList.add(appInfo); + appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); + ownerAppList.add(appInfo); + //profile + appInfo = createApplicationInfo(PKG_1, PROFILE_UID_1); + setupDoResumeIfNeededLocked(ownerAppList, new ArrayList<>(Arrays.asList(appInfo))); + + mApplicationsState.doResumeIfNeededLocked(); + + verify(mApplicationsState).clearEntries(); + } + + @Test + public void removeOwnerApp_workprofileExists_doResumeIfNeededLocked_shouldClearEntries() + throws RemoteException { + if (!MU_ENABLED) { + return; + } + // [Preconditions] + // 2 apps (PKG_1, PKG_2) for owner, PKG_1 is not in installed state + // 2 apps (PKG_1, PKG_2) for non-owner. + // + // [Actions] + // Owner user's PKG_2 is removed before resume + // + // Applications: + // owner - (PKG_1 - uninstalled, PKG_2) -> (PKG_1 - uninstalled, PKG_2 - uninstalled) + // profile - (PKG_1, PKG_2) -> (PKG_1, PKG_2) + // + // Previous Entries: + // owner - (PKG_2) + // profile - (PKG_1, PKG_2) + + ShadowUserManager shadowUserManager = Shadow + .extract(RuntimeEnvironment.application.getSystemService(UserManager.class)); + shadowUserManager.addProfile(PROFILE_USERID, "profile"); + + ApplicationsState.sInstance = null; + mApplicationsState = spy( + ApplicationsState + .getInstance(RuntimeEnvironment.application, mock(IPackageManager.class))); + + ApplicationInfo appInfo; + // Previous Applications: + // owner - (PKG_1 - uninstalled, PKG_2) + // profile - (PKG_1, PKG_2) + final ArrayList<ApplicationInfo> prevAppList = new ArrayList<>(); + appInfo = createApplicationInfo(PKG_1, OWNER_UID_1); + appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED; + prevAppList.add(appInfo); + appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); + prevAppList.add(appInfo); + + appInfo = createApplicationInfo(PKG_1, PROFILE_UID_1); + prevAppList.add(appInfo); + appInfo = createApplicationInfo(PKG_2, PROFILE_UID_2); + prevAppList.add(appInfo); + + mApplicationsState.mApplications = prevAppList; + + // Previous Entries: + // owner (PKG_2), profile (pk1, PKG_2) + // PKG_1 is not installed for owner, hence it's removed from entries + addApp(PKG_2, OWNER_UID_2, 0); + addApp(PKG_1, PROFILE_UID_1, PROFILE_USERID); + addApp(PKG_2, PROFILE_UID_2, PROFILE_USERID); + + // latest Applications: + // owner (PKG_1 - uninstalled, PKG_2 - uninstalled), profile (PKG_1, PKG_2) + // owner's PKG_1, PKG_2 is still listed and is in non-installed state + // profile user's PKG_2 is removed before resume + //owner + final ArrayList<ApplicationInfo> ownerAppList = new ArrayList<>(); + appInfo = createApplicationInfo(PKG_1, OWNER_UID_1); + appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED; + ownerAppList.add(appInfo); + appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); + appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED; + ownerAppList.add(appInfo); + + //profile + final ArrayList<ApplicationInfo> profileAppList = new ArrayList<>(); + appInfo = createApplicationInfo(PKG_1, PROFILE_UID_1); + profileAppList.add(appInfo); + appInfo = createApplicationInfo(PKG_2, PROFILE_UID_2); + profileAppList.add(appInfo); + setupDoResumeIfNeededLocked(ownerAppList, profileAppList); + + mApplicationsState.doResumeIfNeededLocked(); + + verify(mApplicationsState).clearEntries(); + } + + @Test + public void noAppRemoved_workprofileExists_doResumeIfNeededLocked_shouldNotClearEntries() + throws RemoteException { + if (!MU_ENABLED) { + return; + } + // [Preconditions] + // 2 apps (PKG_1, PKG_2) for owner, PKG_1 is not in installed state + // 2 apps (PKG_1, PKG_2) for non-owner. + // + // Applications: + // owner - (PKG_1 - uninstalled, PKG_2) + // profile - (PKG_1, PKG_2) + // + // Previous Entries: + // owner - (PKG_2) + // profile - (PKG_1, PKG_2) + + ShadowUserManager shadowUserManager = Shadow + .extract(RuntimeEnvironment.application.getSystemService(UserManager.class)); + shadowUserManager.addProfile(PROFILE_USERID, "profile"); + + ApplicationsState.sInstance = null; + mApplicationsState = spy( + ApplicationsState + .getInstance(RuntimeEnvironment.application, mock(IPackageManager.class))); + + ApplicationInfo appInfo; + // Previous Applications: + // owner - (PKG_1 - uninstalled, PKG_2) + // profile - (PKG_1, PKG_2) + final ArrayList<ApplicationInfo> prevAppList = new ArrayList<>(); + appInfo = createApplicationInfo(PKG_1, OWNER_UID_1); + appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED; + prevAppList.add(appInfo); + appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); + prevAppList.add(appInfo); + + appInfo = createApplicationInfo(PKG_1, PROFILE_UID_1); + prevAppList.add(appInfo); + appInfo = createApplicationInfo(PKG_2, PROFILE_UID_2); + prevAppList.add(appInfo); + + mApplicationsState.mApplications = prevAppList; + // Previous Entries: + // owner (PKG_2), profile (pk1, PKG_2) + // PKG_1 is not installed for owner, hence it's removed from entries + addApp(PKG_2, OWNER_UID_2, 0); + addApp(PKG_1, PROFILE_UID_1, PROFILE_USERID); + addApp(PKG_2, PROFILE_UID_2, PROFILE_USERID); + + // latest Applications: + // owner (PKG_1 - uninstalled, PKG_2), profile (PKG_1, PKG_2) + // owner's PKG_1 is still listed and is in non-installed state + + // owner + final ArrayList<ApplicationInfo> ownerAppList = new ArrayList<>(); + appInfo = createApplicationInfo(PKG_1, OWNER_UID_1); + appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED; + ownerAppList.add(appInfo); + appInfo = createApplicationInfo(PKG_2, OWNER_UID_2); + ownerAppList.add(appInfo); + + // profile + final ArrayList<ApplicationInfo> profileAppList = new ArrayList<>(); + appInfo = createApplicationInfo(PKG_1, PROFILE_UID_1); + profileAppList.add(appInfo); + appInfo = createApplicationInfo(PKG_2, PROFILE_UID_2); + profileAppList.add(appInfo); + setupDoResumeIfNeededLocked(ownerAppList, profileAppList); + + mApplicationsState.doResumeIfNeededLocked(); + + verify(mApplicationsState, never()).clearEntries(); + } + + private void setupDoResumeIfNeededLocked(ArrayList<ApplicationInfo> ownerApps, + ArrayList<ApplicationInfo> profileApps) + throws RemoteException { + + if (ownerApps != null) { + when(mApplicationsState.mIpm.getInstalledApplications(anyInt(), eq(0))) + .thenReturn(new ParceledListSlice<>(ownerApps)); + } + if (profileApps != null) { + when(mApplicationsState.mIpm.getInstalledApplications(anyInt(), eq(PROFILE_USERID))) + .thenReturn(new ParceledListSlice<>(profileApps)); + } + final InterestingConfigChanges configChanges = mock(InterestingConfigChanges.class); + when(configChanges.applyNewConfig(any(Resources.class))).thenReturn(false); + mApplicationsState.setInterestingConfigChanges(configChanges); + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java index c50d646c0861..ca1eefcad7de 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java @@ -29,6 +29,7 @@ import java.util.List; @Implements(value = UserManager.class) public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager { + private List<UserInfo> mUserInfos = addProfile(0, "Owner"); @Implementation protected static UserManager get(Context context) { @@ -37,16 +38,24 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager @Implementation protected int[] getProfileIdsWithDisabled(int userId) { - return new int[]{0}; + return mUserInfos.stream().mapToInt(s -> s.id).toArray(); } @Implementation protected List<UserInfo> getProfiles() { - UserInfo userInfo = new UserInfo(); - userInfo.id = 0; - List<UserInfo> userInfos = new ArrayList<>(); - userInfos.add(userInfo); - return userInfos; + return mUserInfos; + } + + public List<UserInfo> addProfile(int id, String name) { + List<UserInfo> userInfoList = mUserInfos; + if (userInfoList == null) { + userInfoList = new ArrayList<>(); + } + final UserInfo userInfo = new UserInfo(); + userInfo.id = id; + userInfo.name = name; + userInfoList.add(userInfo); + return userInfoList; } @Implementation diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_message_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_message_area.xml index e1bf6cbd2198..5da7611a4462 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_message_area.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_message_area.xml @@ -17,15 +17,24 @@ */ --> -<!-- This contains emergency call button and carrier as shared by pin/pattern/password screens --> -<com.android.keyguard.KeyguardMessageArea - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - style="@style/Keyguard.TextView" - android:id="@+id/keyguard_message_area" - android:singleLine="true" - android:ellipsize="marquee" - android:focusable="true" /> - +<!-- This contains error message field and padlock shared by pin/pattern/password screens --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" > + <FrameLayout + android:id="@+id/lock_icon_container" + android:layout_gravity="center" + android:layout_marginBottom="@dimen/keyguard_lock_padding" + android:layout_width="@dimen/keyguard_lock_width" + android:layout_height="@dimen/keyguard_lock_height" /> + <com.android.keyguard.KeyguardMessageArea + android:id="@+id/keyguard_message_area" + style="@style/Keyguard.TextView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:singleLine="true" + android:ellipsize="marquee" + android:focusable="true" /> +</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/values-sw320dp/dimens.xml b/packages/SystemUI/res-keyguard/values-sw320dp/dimens.xml index 38d2ecc3ddb5..91ca5c52c015 100644 --- a/packages/SystemUI/res-keyguard/values-sw320dp/dimens.xml +++ b/packages/SystemUI/res-keyguard/values-sw320dp/dimens.xml @@ -21,6 +21,6 @@ <!-- Height of the sliding KeyguardSecurityContainer (includes 2x keyguard_security_view_top_margin) --> - <dimen name="keyguard_security_height">345dp</dimen> + <dimen name="keyguard_security_height">395dp</dimen> </resources> diff --git a/packages/SystemUI/res-keyguard/values-sw360dp/dimens.xml b/packages/SystemUI/res-keyguard/values-sw360dp/dimens.xml index 90c4795d1e56..d7c9975ada41 100644 --- a/packages/SystemUI/res-keyguard/values-sw360dp/dimens.xml +++ b/packages/SystemUI/res-keyguard/values-sw360dp/dimens.xml @@ -21,5 +21,5 @@ <!-- Height of the sliding KeyguardSecurityContainer (includes 2x keyguard_security_view_top_margin) --> - <dimen name="keyguard_security_height">400dp</dimen> + <dimen name="keyguard_security_height">450dp</dimen> </resources> diff --git a/packages/SystemUI/res-keyguard/values-sw540dp-port/dimens.xml b/packages/SystemUI/res-keyguard/values-sw540dp-port/dimens.xml index 9ea04dc72ba9..a3c37e420f29 100644 --- a/packages/SystemUI/res-keyguard/values-sw540dp-port/dimens.xml +++ b/packages/SystemUI/res-keyguard/values-sw540dp-port/dimens.xml @@ -20,5 +20,5 @@ <resources> <!-- Height of the sliding KeyguardSecurityContainer (includes 2x keyguard_security_view_top_margin) --> - <dimen name="keyguard_security_height">500dp</dimen> + <dimen name="keyguard_security_height">550dp</dimen> </resources> diff --git a/packages/SystemUI/res-keyguard/values-sw720dp/dimens.xml b/packages/SystemUI/res-keyguard/values-sw720dp/dimens.xml index 9157822db960..1dc61c501beb 100644 --- a/packages/SystemUI/res-keyguard/values-sw720dp/dimens.xml +++ b/packages/SystemUI/res-keyguard/values-sw720dp/dimens.xml @@ -20,7 +20,7 @@ <!-- Height of the sliding KeyguardSecurityContainer (includes 2x keyguard_security_view_top_margin) --> - <dimen name="keyguard_security_height">420dp</dimen> + <dimen name="keyguard_security_height">470dp</dimen> <dimen name="widget_big_font_size">100dp</dimen> </resources> diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index b6a41c19ec32..d67c98a337e8 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -27,11 +27,11 @@ <!-- Height of the sliding KeyguardSecurityContainer (includes 2x keyguard_security_view_top_margin) --> - <dimen name="keyguard_security_height">400dp</dimen> + <dimen name="keyguard_security_height">450dp</dimen> <!-- Max Height of the sliding KeyguardSecurityContainer (includes 2x keyguard_security_view_top_margin) --> - <dimen name="keyguard_security_max_height">455dp</dimen> + <dimen name="keyguard_security_max_height">505dp</dimen> <!-- Margin around the various security views --> <dimen name="keyguard_security_view_top_margin">8dp</dimen> diff --git a/packages/SystemUI/res/layout/biometric_dialog.xml b/packages/SystemUI/res/layout/biometric_dialog.xml index 1e8cd5a7e13b..83557f266da0 100644 --- a/packages/SystemUI/res/layout/biometric_dialog.xml +++ b/packages/SystemUI/res/layout/biometric_dialog.xml @@ -39,152 +39,158 @@ android:layout_height="0dp" android:layout_weight="1" /> - <LinearLayout + <ScrollView android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal"> - - <!-- This is not a Space since Spaces cannot be clicked. The width of this changes depending - on horizontal/portrait orientation --> - <View - android:id="@+id/left_space" - android:layout_weight="1" - android:layout_width="0dp" - android:layout_height="match_parent"/> + android:layout_height="wrap_content"> <LinearLayout - android:id="@+id/dialog" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" - android:background="@drawable/biometric_dialog_bg" - android:layout_marginBottom="@dimen/biometric_dialog_border_padding" - android:layout_marginLeft="@dimen/biometric_dialog_border_padding" - android:layout_marginRight="@dimen/biometric_dialog_border_padding"> - - <TextView - android:id="@+id/title" - android:fontFamily="@*android:string/config_headlineFontFamilyMedium" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginEnd="24dp" - android:layout_marginStart="24dp" - android:layout_marginTop="24dp" - android:gravity="@integer/biometric_dialog_text_gravity" - android:textSize="20sp" - android:maxLines="1" - android:singleLine="true" - android:ellipsize="marquee" - android:marqueeRepeatLimit="marquee_forever" - android:textColor="?android:attr/textColorPrimary"/> - - <TextView - android:id="@+id/subtitle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:layout_marginStart="24dp" - android:layout_marginEnd="24dp" - android:gravity="@integer/biometric_dialog_text_gravity" - android:textSize="16sp" - android:maxLines="1" - android:singleLine="true" - android:ellipsize="marquee" - android:marqueeRepeatLimit="marquee_forever" - android:textColor="?android:attr/textColorPrimary"/> - - <TextView - android:id="@+id/description" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginEnd="24dp" - android:layout_marginStart="24dp" - android:gravity="@integer/biometric_dialog_text_gravity" - android:paddingTop="8dp" - android:textSize="16sp" - android:maxLines="4" - android:textColor="?android:attr/textColorPrimary"/> - - <ImageView - android:id="@+id/biometric_icon" - android:layout_width="@dimen/biometric_dialog_biometric_icon_size" - android:layout_height="@dimen/biometric_dialog_biometric_icon_size" - android:layout_gravity="center_horizontal" - android:layout_marginTop="48dp" - android:scaleType="fitXY" /> - - <TextView - android:id="@+id/error" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginEnd="24dp" - android:layout_marginStart="24dp" - android:paddingTop="16dp" - android:paddingBottom="24dp" - android:textSize="12sp" - android:gravity="center_horizontal" - android:accessibilityLiveRegion="polite" - android:contentDescription="@string/accessibility_biometric_dialog_help_area" - android:textColor="?android:attr/textColorSecondary"/> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="72dip" - android:paddingTop="24dp" - android:layout_gravity="center_vertical" - style="?android:attr/buttonBarStyle" - android:orientation="horizontal" - android:measureWithLargestChild="true"> - <Space android:id="@+id/leftSpacer" - android:layout_width="12dp" - android:layout_height="match_parent" - android:visibility="visible" /> - <!-- Negative Button --> - <Button android:id="@+id/button2" - android:layout_width="wrap_content" - android:layout_height="match_parent" - style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored" - android:gravity="center" - android:maxLines="2" /> - <Space android:id="@+id/middleSpacer" - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="1" - android:visibility="visible" /> - <!-- Positive Button --> - <Button android:id="@+id/button1" - android:layout_width="wrap_content" - android:layout_height="match_parent" - style="@*android:style/Widget.DeviceDefault.Button.Colored" - android:gravity="center" - android:maxLines="2" - android:text="@string/biometric_dialog_confirm" - android:visibility="gone"/> - <!-- Try Again Button --> - <Button android:id="@+id/button_try_again" - android:layout_width="wrap_content" - android:layout_height="match_parent" - style="@*android:style/Widget.DeviceDefault.Button.Colored" - android:gravity="center" - android:maxLines="2" - android:text="@string/biometric_dialog_try_again" - android:visibility="gone"/> - <Space android:id="@+id/rightSpacer" - android:layout_width="12dip" - android:layout_height="match_parent" - android:visibility="visible" /> - </LinearLayout> - </LinearLayout> + android:orientation="horizontal"> + + <!-- This is not a Space since Spaces cannot be clicked. The width of this changes + depending on horizontal/portrait orientation --> + <View + android:id="@+id/left_space" + android:layout_weight="1" + android:layout_width="0dp" + android:layout_height="match_parent"/> + + <LinearLayout + android:id="@+id/dialog" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:background="@drawable/biometric_dialog_bg" + android:layout_marginBottom="@dimen/biometric_dialog_border_padding" + android:layout_marginLeft="@dimen/biometric_dialog_border_padding" + android:layout_marginRight="@dimen/biometric_dialog_border_padding"> + + <TextView + android:id="@+id/title" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginEnd="24dp" + android:layout_marginStart="24dp" + android:layout_marginTop="24dp" + android:gravity="@integer/biometric_dialog_text_gravity" + android:textSize="20sp" + android:maxLines="1" + android:singleLine="true" + android:ellipsize="marquee" + android:marqueeRepeatLimit="marquee_forever" + android:textColor="?android:attr/textColorPrimary"/> + + <TextView + android:id="@+id/subtitle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:layout_marginStart="24dp" + android:layout_marginEnd="24dp" + android:gravity="@integer/biometric_dialog_text_gravity" + android:textSize="16sp" + android:maxLines="1" + android:singleLine="true" + android:ellipsize="marquee" + android:marqueeRepeatLimit="marquee_forever" + android:textColor="?android:attr/textColorPrimary"/> + + <TextView + android:id="@+id/description" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginEnd="24dp" + android:layout_marginStart="24dp" + android:gravity="@integer/biometric_dialog_text_gravity" + android:paddingTop="8dp" + android:textSize="16sp" + android:maxLines="4" + android:textColor="?android:attr/textColorPrimary"/> + + <ImageView + android:id="@+id/biometric_icon" + android:layout_width="@dimen/biometric_dialog_biometric_icon_size" + android:layout_height="@dimen/biometric_dialog_biometric_icon_size" + android:layout_gravity="center_horizontal" + android:layout_marginTop="48dp" + android:scaleType="fitXY" /> + + <TextView + android:id="@+id/error" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginEnd="24dp" + android:layout_marginStart="24dp" + android:paddingTop="16dp" + android:paddingBottom="24dp" + android:textSize="12sp" + android:gravity="center_horizontal" + android:accessibilityLiveRegion="polite" + android:contentDescription="@string/accessibility_biometric_dialog_help_area" + android:textColor="?android:attr/textColorSecondary"/> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="72dip" + android:paddingTop="24dp" + android:layout_gravity="center_vertical" + style="?android:attr/buttonBarStyle" + android:orientation="horizontal" + android:measureWithLargestChild="true"> + <Space android:id="@+id/leftSpacer" + android:layout_width="12dp" + android:layout_height="match_parent" + android:visibility="visible" /> + <!-- Negative Button --> + <Button android:id="@+id/button2" + android:layout_width="wrap_content" + android:layout_height="match_parent" + style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored" + android:gravity="center" + android:maxLines="2" /> + <Space android:id="@+id/middleSpacer" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:visibility="visible" /> + <!-- Positive Button --> + <Button android:id="@+id/button1" + android:layout_width="wrap_content" + android:layout_height="match_parent" + style="@*android:style/Widget.DeviceDefault.Button.Colored" + android:gravity="center" + android:maxLines="2" + android:text="@string/biometric_dialog_confirm" + android:visibility="gone"/> + <!-- Try Again Button --> + <Button android:id="@+id/button_try_again" + android:layout_width="wrap_content" + android:layout_height="match_parent" + style="@*android:style/Widget.DeviceDefault.Button.Colored" + android:gravity="center" + android:maxLines="2" + android:text="@string/biometric_dialog_try_again" + android:visibility="gone"/> + <Space android:id="@+id/rightSpacer" + android:layout_width="12dip" + android:layout_height="match_parent" + android:visibility="visible" /> + </LinearLayout> + </LinearLayout> + + <!-- This is not a Space since Spaces cannot be clicked. The width of this changes + depending on horizontal/portrait orientation --> + <View + android:id="@+id/right_space" + android:layout_weight="1" + android:layout_width="0dp" + android:layout_height="match_parent" /> - <!-- This is not a Space since Spaces cannot be clicked. The width of this changes depending - on horizontal/portrait orientation --> - <View - android:id="@+id/right_space" - android:layout_weight="1" - android:layout_width="0dp" - android:layout_height="match_parent" /> + </LinearLayout> - </LinearLayout> + </ScrollView> </LinearLayout> diff --git a/packages/SystemUI/res/layout/global_actions_grid.xml b/packages/SystemUI/res/layout/global_actions_grid.xml index d90d5e9f676b..8651e5a463c9 100644 --- a/packages/SystemUI/res/layout/global_actions_grid.xml +++ b/packages/SystemUI/res/layout/global_actions_grid.xml @@ -31,7 +31,6 @@ android:paddingTop="@dimen/global_actions_grid_vertical_padding" android:paddingBottom="@dimen/global_actions_grid_vertical_padding" android:orientation="vertical" - android:background="?android:attr/colorBackgroundFloating" android:gravity="center" android:translationZ="@dimen/global_actions_translate" /> @@ -48,7 +47,6 @@ android:paddingRight="@dimen/global_actions_grid_horizontal_padding" android:paddingTop="@dimen/global_actions_grid_vertical_padding" android:paddingBottom="@dimen/global_actions_grid_vertical_padding" - android:background="?android:attr/colorBackgroundFloating" > <LinearLayout android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index 5803bf129c24..d74d25846e3c 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -47,4 +47,16 @@ <!-- The color of the background in the bottom part of QSCustomizer --> <color name="qs_customize_decoration">@color/GM2_grey_800</color> + + <!-- The color of the background in the separated list of the Global Actions menu --> + <color name="global_actions_separated_background">@color/GM2_grey_900</color> + + <!-- The color of the background in the grid of the Global Actions menu --> + <color name="global_actions_grid_background">@color/GM2_grey_800</color> + + <!-- The color of the text in the Global Actions menu --> + <color name="global_actions_text">@color/GM2_grey_200</color> + + <!-- The color of the text in the Global Actions menu --> + <color name="global_actions_alert_text">@color/GM2_red_300</color> </resources>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 0e4ffee81165..b82c250e2bd2 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -38,6 +38,18 @@ <color name="qs_customize_background">@color/GM2_grey_50</color> <color name="qs_customize_decoration">@color/GM2_grey_100</color> + <!-- The color of the background in the separated list of the Global Actions menu --> + <color name="global_actions_separated_background">@color/GM2_grey_300</color> + + <!-- The color of the background in the grid of the Global Actions menu --> + <color name="global_actions_grid_background">@color/GM2_grey_200</color> + + <!-- The color of the text in the Global Actions menu --> + <color name="global_actions_text">@color/GM2_grey_900</color> + + <!-- The color of the text in the Global Actions menu --> + <color name="global_actions_alert_text">@color/GM2_red_500</color> + <!-- Tint color for the content on the notification overflow card. --> <color name="keyguard_overflow_content_color">#ff686868</color> @@ -149,4 +161,7 @@ <color name="GM2_grey_700">#5F6368</color> <color name="GM2_grey_800">#3C4043</color> <color name="GM2_grey_900">#202124</color> + + <color name="GM2_red_300">#F28B82</color> + <color name="GM2_red_500">#B71C1C</color> </resources> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index d40fa661192c..ce65b5a3a3a3 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -60,8 +60,8 @@ public class KeyguardClockSwitch extends RelativeLayout { */ private ViewGroup mBigClockContainer; /** - * Status area (date and other stuff) shown below the clock. Plugin can decide whether - * or not to show it below the alternate clock. + * Status area (date and other stuff) shown below the clock. Plugin can decide whether or not to + * show it below the alternate clock. */ private View mKeyguardStatusArea; /** @@ -75,24 +75,19 @@ public class KeyguardClockSwitch extends RelativeLayout { private boolean mSupportsDarkText; private int[] mColorPalette; + /** + * Track the state of the status bar to know when to hide the big_clock_container. + */ + private int mStatusBarState; + private final StatusBarStateController.StateListener mStateListener = new StatusBarStateController.StateListener() { @Override public void onStateChanged(int newState) { - if (mBigClockContainer == null) { - return; - } - if (newState == StatusBarState.SHADE) { - if (mBigClockContainer.getVisibility() == View.VISIBLE) { - mBigClockContainer.setVisibility(View.INVISIBLE); - } - } else { - if (mBigClockContainer.getVisibility() == View.INVISIBLE) { - mBigClockContainer.setVisibility(View.VISIBLE); - } - } + mStatusBarState = newState; + updateBigClockVisibility(); } - }; + }; private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin; @@ -139,7 +134,9 @@ public class KeyguardClockSwitch extends RelativeLayout { protected void onAttachedToWindow() { super.onAttachedToWindow(); Dependency.get(ClockManager.class).addOnClockChangedListener(mClockChangedListener); - Dependency.get(StatusBarStateController.class).addCallback(mStateListener); + StatusBarStateController stateController = Dependency.get(StatusBarStateController.class); + stateController.addCallback(mStateListener); + mStateListener.onStateChanged(stateController.getState()); SysuiColorExtractor colorExtractor = Dependency.get(SysuiColorExtractor.class); colorExtractor.addOnColorsChangedListener(mColorsListener); updateColors(colorExtractor); @@ -151,7 +148,7 @@ public class KeyguardClockSwitch extends RelativeLayout { Dependency.get(ClockManager.class).removeOnClockChangedListener(mClockChangedListener); Dependency.get(StatusBarStateController.class).removeCallback(mStateListener); Dependency.get(SysuiColorExtractor.class) - .removeOnColorsChangedListener(mColorsListener); + .removeOnColorsChangedListener(mColorsListener); setClockPlugin(null); } @@ -164,7 +161,7 @@ public class KeyguardClockSwitch extends RelativeLayout { } if (mBigClockContainer != null) { mBigClockContainer.removeAllViews(); - mBigClockContainer.setVisibility(View.GONE); + updateBigClockVisibility(); } mClockPlugin = null; } @@ -184,7 +181,7 @@ public class KeyguardClockSwitch extends RelativeLayout { View bigClockView = plugin.getBigClockView(); if (bigClockView != null && mBigClockContainer != null) { mBigClockContainer.addView(bigClockView); - mBigClockContainer.setVisibility(View.VISIBLE); + updateBigClockVisibility(); } // Hide default clock. if (!plugin.shouldShowStatusArea()) { @@ -208,12 +205,10 @@ public class KeyguardClockSwitch extends RelativeLayout { View bigClockView = mClockPlugin.getBigClockView(); if (bigClockView != null) { container.addView(bigClockView); - if (container.getVisibility() == View.GONE) { - container.setVisibility(View.VISIBLE); - } } } mBigClockContainer = container; + updateBigClockVisibility(); } /** @@ -254,6 +249,7 @@ public class KeyguardClockSwitch extends RelativeLayout { /** * Set the amount (ratio) that the device has transitioned to doze. + * * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake. */ public void setDarkAmount(float darkAmount) { @@ -307,9 +303,24 @@ public class KeyguardClockSwitch extends RelativeLayout { } } + private void updateBigClockVisibility() { + if (mBigClockContainer == null) { + return; + } + final boolean inDisplayState = mStatusBarState == StatusBarState.KEYGUARD + || mStatusBarState == StatusBarState.SHADE_LOCKED; + final int visibility = + inDisplayState && mBigClockContainer.getChildCount() != 0 ? View.VISIBLE + : View.GONE; + if (mBigClockContainer.getVisibility() != visibility) { + mBigClockContainer.setVisibility(visibility); + } + } + /** - * Sets if the keyguard slice is showing a center-aligned header. We need a smaller clock - * in these cases. + * Sets if the keyguard slice is showing a center-aligned header. We need a smaller clock in + * these + * cases. */ public void setKeyguardShowingHeader(boolean hasHeader) { if (mShowingHeader == hasHeader || hasCustomClock()) { @@ -327,12 +338,12 @@ public class KeyguardClockSwitch extends RelativeLayout { mClockView.getPaddingRight(), paddingBottom); } - @VisibleForTesting (otherwise = VisibleForTesting.NONE) + @VisibleForTesting(otherwise = VisibleForTesting.NONE) ClockManager.ClockChangedListener getClockChangedListener() { return mClockChangedListener; } - @VisibleForTesting (otherwise = VisibleForTesting.NONE) + @VisibleForTesting(otherwise = VisibleForTesting.NONE) StatusBarStateController.StateListener getStateListener() { return mStateListener; } @@ -352,7 +363,8 @@ public class KeyguardClockSwitch extends RelativeLayout { /** * Special layout transition that scales the clock view as its bounds change, to make it look - * like the text is shrinking. + * like + * the text is shrinking. */ private class ClockBoundsTransition extends ChangeBounds { diff --git a/packages/SystemUI/src/com/android/systemui/HardwareBgDrawable.java b/packages/SystemUI/src/com/android/systemui/HardwareBgDrawable.java index 97d799909ed9..d631cf3551d9 100644 --- a/packages/SystemUI/src/com/android/systemui/HardwareBgDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/HardwareBgDrawable.java @@ -115,4 +115,4 @@ public class HardwareBgDrawable extends LayerDrawable { public void setRotatedBackground(boolean rotatedBackground) { mRotatedBackground = rotatedBackground; } -} +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java index 016b8fe93093..402810902d39 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java @@ -191,32 +191,41 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba mCurrentDialogArgs = args; final int type = args.argi1; + // Create a new dialog but do not replace the current one yet. + BiometricDialogView newDialog; if (type == BiometricAuthenticator.TYPE_FINGERPRINT) { - mCurrentDialog = new FingerprintDialogView(mContext, mCallback); + newDialog = new FingerprintDialogView(mContext, mCallback); } else if (type == BiometricAuthenticator.TYPE_FACE) { - mCurrentDialog = new FaceDialogView(mContext, mCallback); + newDialog = new FaceDialogView(mContext, mCallback); } else { Log.e(TAG, "Unsupported type: " + type); + return; } - if (savedState != null) { - mCurrentDialog.restoreState(savedState); - } - - if (DEBUG) Log.d(TAG, "handleShowDialog, isAnimatingAway: " - + mCurrentDialog.isAnimatingAway() + " type: " + type); + if (DEBUG) Log.d(TAG, "handleShowDialog, " + + " savedState: " + savedState + + " mCurrentDialog: " + mCurrentDialog + + " newDialog: " + newDialog + + " type: " + type); - if (mCurrentDialog.isAnimatingAway()) { + if (savedState != null) { + // SavedState is only non-null if it's from onConfigurationChanged. Restore the state + // even though it may be removed / re-created again + newDialog.restoreState(savedState); + } else if (mCurrentDialog != null && mDialogShowing) { + // If somehow we're asked to show a dialog, the old one doesn't need to be animated + // away. This can happen if the app cancels and re-starts auth during configuration + // change. This is ugly because we also have to do things on onConfigurationChanged + // here. mCurrentDialog.forceRemove(); - } else if (mDialogShowing) { - Log.w(TAG, "Dialog already showing"); - return; } + mReceiver = (IBiometricServiceReceiverInternal) args.arg2; - mCurrentDialog.setBundle((Bundle)args.arg1); - mCurrentDialog.setRequireConfirmation((boolean) args.arg3); - mCurrentDialog.setUserId(args.argi2); - mCurrentDialog.setSkipIntro(skipAnimation); + newDialog.setBundle((Bundle) args.arg1); + newDialog.setRequireConfirmation((boolean) args.arg3); + newDialog.setUserId(args.argi2); + newDialog.setSkipIntro(skipAnimation); + mCurrentDialog = newDialog; mWindowManager.addView(mCurrentDialog, mCurrentDialog.getLayoutParams()); mDialogShowing = true; } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index b98ce269872a..f07887ee6353 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -92,8 +92,6 @@ import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.plugins.GlobalActionsPanelPlugin; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.ExtensionController; -import com.android.systemui.statusbar.policy.ExtensionController.Extension; import com.android.systemui.util.EmergencyDialerConstants; import com.android.systemui.util.leak.RotationUtils; import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator; @@ -161,9 +159,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final EmergencyAffordanceManager mEmergencyAffordanceManager; private final ScreenshotHelper mScreenshotHelper; private final ScreenRecordHelper mScreenRecordHelper; - - private final Extension<GlobalActionsPanelPlugin> mPanelExtension; - private ActivityStarter mActivityStarter; + private final ActivityStarter mActivityStarter; + private GlobalActionsPanelPlugin mPanelPlugin; /** * @param context everything needs a context :( @@ -209,10 +206,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, Dependency.get(ConfigurationController.class).addCallback(this); - mPanelExtension = Dependency.get(ExtensionController.class) - .newExtension(GlobalActionsPanelPlugin.class) - .withPlugin(GlobalActionsPanelPlugin.class) - .build(); mActivityStarter = Dependency.get(ActivityStarter.class); } @@ -221,9 +214,11 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, * * @param keyguardShowing True if keyguard is showing */ - public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) { + public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned, + GlobalActionsPanelPlugin panelPlugin) { mKeyguardShowing = keyguardShowing; mDeviceProvisioned = isDeviceProvisioned; + mPanelPlugin = panelPlugin; if (mDialog != null) { mDialog.dismiss(); mDialog = null; @@ -383,8 +378,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mHasLogoutButton = true; } } else if (GLOBAL_ACTION_KEY_EMERGENCY.equals(actionKey)) { - if (shouldUseSeparatedView() - && !mEmergencyAffordanceManager.needsEmergencyAffordance()) { + if (!mEmergencyAffordanceManager.needsEmergencyAffordance()) { mItems.add(new EmergencyDialerAction()); } } else { @@ -395,14 +389,14 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } if (mEmergencyAffordanceManager.needsEmergencyAffordance()) { - mItems.add(getEmergencyAction()); + mItems.add(new EmergencyAffordanceAction()); } mAdapter = new MyAdapter(); GlobalActionsPanelPlugin.PanelViewController panelViewController = - mPanelExtension.get() != null - ? mPanelExtension.get().onPanelShown( + mPanelPlugin != null + ? mPanelPlugin.onPanelShown( new GlobalActionsPanelPlugin.Callbacks() { @Override public void dismissGlobalActionsMenu() { @@ -484,22 +478,33 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } } - private class EmergencyDialerAction extends SinglePressAction { - private EmergencyDialerAction() { - super(R.drawable.ic_faster_emergency, - R.string.global_action_emergency); + private abstract class EmergencyAction extends SinglePressAction { + EmergencyAction(int iconResId, int messageResId) { + super(iconResId, messageResId); } @Override - public void onPress() { - MetricsLogger.action(mContext, MetricsEvent.ACTION_EMERGENCY_DIALER_FROM_POWER_MENU); - Intent intent = new Intent(EmergencyDialerConstants.ACTION_DIAL); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS - | Intent.FLAG_ACTIVITY_CLEAR_TOP); - intent.putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE, - EmergencyDialerConstants.ENTRY_TYPE_POWER_MENU); - mContext.startActivityAsUser(intent, UserHandle.CURRENT); + public boolean shouldBeSeparated() { + return shouldUseSeparatedView(); + } + + @Override + public View create( + Context context, View convertView, ViewGroup parent, LayoutInflater inflater) { + View v = super.create(context, convertView, parent, inflater); + int textColor; + if (shouldBeSeparated()) { + textColor = v.getResources().getColor( + com.android.systemui.R.color.global_actions_alert_text); + } else { + textColor = v.getResources().getColor( + com.android.systemui.R.color.global_actions_text); + } + TextView messageView = v.findViewById(R.id.message); + messageView.setTextColor(textColor); + ImageView icon = (ImageView) v.findViewById(R.id.icon); + icon.getDrawable().setTint(textColor); + return v; } @Override @@ -511,10 +516,36 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, public boolean showBeforeProvisioning() { return true; } + } + + private class EmergencyAffordanceAction extends EmergencyAction { + EmergencyAffordanceAction() { + super(R.drawable.emergency_icon, + R.string.global_action_emergency); + } @Override - public boolean shouldBeSeparated() { - return true; + public void onPress() { + mEmergencyAffordanceManager.performEmergencyCall(); + } + } + + private class EmergencyDialerAction extends EmergencyAction { + private EmergencyDialerAction() { + super(R.drawable.ic_faster_emergency, + R.string.global_action_emergency); + } + + @Override + public void onPress() { + MetricsLogger.action(mContext, MetricsEvent.ACTION_EMERGENCY_DIALER_FROM_POWER_MENU); + Intent intent = new Intent(EmergencyDialerConstants.ACTION_DIAL); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS + | Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE, + EmergencyDialerConstants.ENTRY_TYPE_POWER_MENU); + mContext.startActivityAsUser(intent, UserHandle.CURRENT); } } @@ -703,32 +734,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, }; } - private Action getEmergencyAction() { - Drawable emergencyIcon = mContext.getDrawable(R.drawable.emergency_icon); - if (!shouldUseSeparatedView()) { - // use un-colored legacy treatment - emergencyIcon.setTintList(null); - } - - return new SinglePressAction(R.drawable.emergency_icon, - R.string.global_action_emergency) { - @Override - public void onPress() { - mEmergencyAffordanceManager.performEmergencyCall(); - } - - @Override - public boolean showDuringKeyguard() { - return true; - } - - @Override - public boolean showBeforeProvisioning() { - return true; - } - }; - } - private Action getAssistAction() { return new SinglePressAction(R.drawable.ic_action_assist_focused, R.string.global_action_assist) { @@ -1494,12 +1499,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final Context mContext; private final MyAdapter mAdapter; private MultiListLayout mGlobalActionsLayout; - private final Drawable mBackgroundDrawable; + private Drawable mBackgroundDrawable; private final ColorExtractor mColorExtractor; private final GlobalActionsPanelPlugin.PanelViewController mPanelController; private boolean mKeyguardShowing; private boolean mShowing; - private final float mScrimAlpha; + private float mScrimAlpha; ActionsDialog(Context context, MyAdapter adapter, GlobalActionsPanelPlugin.PanelViewController plugin) { @@ -1526,49 +1531,37 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY); - - initializeLayout(); - setTitle(R.string.global_actions); mPanelController = plugin; - View panelView = initializePanel(); + initializeLayout(); + } + + private boolean initializePanel() { + if (!isPanelEnabled(mContext) || mPanelController == null) { + return false; + } + View panelView = mPanelController.getPanelContent(); if (panelView == null) { - mBackgroundDrawable = new GradientDrawable(context); - mScrimAlpha = ScrimController.GRADIENT_SCRIM_ALPHA; - } else { - mBackgroundDrawable = context.getDrawable( - com.android.systemui.R.drawable.global_action_panel_scrim); - mScrimAlpha = 1f; - addContentView( - panelView, - new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - } - window.setBackgroundDrawable(mBackgroundDrawable); - } - - private View initializePanel() { - if (isPanelEnabled(mContext) && mPanelController != null) { - View panelView = mPanelController.getPanelContent(); - if (panelView != null) { - FrameLayout panelContainer = new FrameLayout(mContext); - FrameLayout.LayoutParams panelParams = - new FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - FrameLayout.LayoutParams.WRAP_CONTENT); - panelContainer.addView(panelView, panelParams); - return panelContainer; - } + return false; } - return null; + FrameLayout panelContainer = new FrameLayout(mContext); + FrameLayout.LayoutParams panelParams = + new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.WRAP_CONTENT); + panelContainer.addView(panelView, panelParams); + addContentView( + panelContainer, + new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + return true; } private void initializeLayout() { setContentView(getGlobalActionsLayoutId(mContext)); - mGlobalActionsLayout = (MultiListLayout) - findViewById(com.android.systemui.R.id.global_actions_view); + mGlobalActionsLayout = findViewById(com.android.systemui.R.id.global_actions_view); mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss()); mGlobalActionsLayout.setListViewAccessibilityDelegate(new View.AccessibilityDelegate() { @Override @@ -1581,8 +1574,18 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, }); mGlobalActionsLayout.setRotationListener(this::onRotate); mGlobalActionsLayout.setAdapter(mAdapter); - mGlobalActionsLayout.setSnapToEdge(isPanelEnabled(mContext) - && mPanelController != null); + + boolean panelEnabled = initializePanel(); + if (!panelEnabled) { + mBackgroundDrawable = new GradientDrawable(mContext); + mScrimAlpha = ScrimController.GRADIENT_SCRIM_ALPHA; + } else { + mBackgroundDrawable = mContext.getDrawable( + com.android.systemui.R.drawable.global_action_panel_scrim); + mScrimAlpha = 1f; + } + mGlobalActionsLayout.setSnapToEdge(panelEnabled); + getWindow().setBackgroundDrawable(mBackgroundDrawable); } private int getGlobalActionsLayoutId(Context context) { @@ -1738,7 +1741,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, */ private static boolean isPanelEnabled(Context context) { return FeatureFlagUtils.isEnabled( - context, FeatureFlagUtils.GLOBAL_ACTIONS_PANEL_ENABLED); } + context, FeatureFlagUtils.GLOBAL_ACTIONS_PANEL_ENABLED); + } /** * Determines whether the Global Actions menu should use a separated view for emergency actions. diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java index 058ea605bc87..9a0759c70b5b 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java @@ -42,11 +42,16 @@ public class GlobalActionsGridLayout extends MultiListLayout { } private void setBackgrounds() { + int gridBackgroundColor = getResources().getColor( + com.android.systemui.R.color.global_actions_grid_background, null); + int separatedBackgroundColor = getResources().getColor( + com.android.systemui.R.color.global_actions_separated_background, null); HardwareBgDrawable listBackground = new HardwareBgDrawable(true, true, getContext()); - HardwareBgDrawable separatedViewBackground = new HardwareBgDrawable(true, true, - getContext()); + HardwareBgDrawable separatedBackground = new HardwareBgDrawable(true, true, getContext()); + listBackground.setTint(gridBackgroundColor); + separatedBackground.setTint(separatedBackgroundColor); getListView().setBackground(listBackground); - getSeparatedView().setBackground(separatedViewBackground); + getSeparatedView().setBackground(separatedBackground); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java index 19a7ceaaaac3..4cf58b736eb9 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java @@ -37,8 +37,10 @@ import com.android.systemui.Dependency; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.plugins.GlobalActions; +import com.android.systemui.plugins.GlobalActionsPanelPlugin; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.KeyguardMonitor; public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks { @@ -48,6 +50,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks private final Context mContext; private final KeyguardMonitor mKeyguardMonitor; private final DeviceProvisionedController mDeviceProvisionedController; + private final ExtensionController.Extension<GlobalActionsPanelPlugin> mPanelExtension; private GlobalActionsDialog mGlobalActions; private boolean mDisabled; @@ -56,6 +59,10 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); SysUiServiceProvider.getComponent(context, CommandQueue.class).addCallback(this); + mPanelExtension = Dependency.get(ExtensionController.class) + .newExtension(GlobalActionsPanelPlugin.class) + .withPlugin(GlobalActionsPanelPlugin.class) + .build(); } @Override @@ -74,7 +81,8 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks mGlobalActions = new GlobalActionsDialog(mContext, manager); } mGlobalActions.showDialog(mKeyguardMonitor.isShowing(), - mDeviceProvisionedController.isDeviceProvisioned()); + mDeviceProvisionedController.isDeviceProvisioned(), + mPanelExtension.get()); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 295abcbcfd17..2f99cf311eec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -596,7 +596,8 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< @Override public void appTransitionCancelled(int displayId) { synchronized (mLock) { - mHandler.obtainMessage(MSG_APP_TRANSITION_CANCELLED, displayId).sendToTarget(); + mHandler.obtainMessage(MSG_APP_TRANSITION_CANCELLED, displayId, 0 /* unused */) + .sendToTarget(); } } @@ -624,7 +625,8 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< @Override public void appTransitionFinished(int displayId) { synchronized (mLock) { - mHandler.obtainMessage(MSG_APP_TRANSITION_FINISHED, displayId).sendToTarget(); + mHandler.obtainMessage(MSG_APP_TRANSITION_FINISHED, displayId, 0 /* unused */) + .sendToTarget(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java index 0a395499347d..2da68249d8c4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java @@ -492,13 +492,10 @@ public class KeyguardAffordanceView extends ImageView { int currentAlpha = getImageAlpha(); ValueAnimator animator = ValueAnimator.ofInt(currentAlpha, endAlpha); mAlphaAnimator = animator; - animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - int alpha = (int) animation.getAnimatedValue(); - if (background != null) background.mutate().setAlpha(alpha); - setImageAlpha(alpha); - } + animator.addUpdateListener(animation -> { + int alpha1 = (int) animation.getAnimatedValue(); + if (background != null) background.mutate().setAlpha(alpha1); + setImageAlpha(alpha1); }); animator.addListener(mAlphaEndListener); if (interpolator == null) { @@ -520,6 +517,10 @@ public class KeyguardAffordanceView extends ImageView { } } + public boolean isAnimatingAlpha() { + return mAlphaAnimator != null; + } + private Animator.AnimatorListener getEndListener(final Runnable runnable) { return new AnimatorListenerAdapter() { boolean mCancelled; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java index 14dc272d1c5e..2bb6e3e32dc7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java @@ -32,6 +32,7 @@ import android.view.IWindowManager; import android.view.View; import android.view.WindowManagerGlobal; +import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.CommandQueue.Callbacks; @@ -58,7 +59,8 @@ public class NavigationBarController implements Callbacks { private final DisplayManager mDisplayManager; /** A displayId - nav bar maps. */ - private SparseArray<NavigationBarFragment> mNavigationBars = new SparseArray<>(); + @VisibleForTesting + SparseArray<NavigationBarFragment> mNavigationBars = new SparseArray<>(); @Inject public NavigationBarController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) { @@ -101,7 +103,8 @@ public class NavigationBarController implements Callbacks { * * @param display the display to add navigation bar on. */ - private void createNavigationBar(Display display) { + @VisibleForTesting + void createNavigationBar(Display display) { if (display == null) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java index 2bbc53c05cbe..fdf8ccee3f62 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java @@ -28,6 +28,7 @@ import android.view.IWindowManager; import android.view.MotionEvent; import android.view.View; +import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.statusbar.CommandQueue; @@ -48,8 +49,10 @@ public class AutoHideController implements CommandQueue.Callbacks { private StatusBar mStatusBar; private NavigationBarFragment mNavigationBar; - private int mDisplayId; - private int mSystemUiVisibility; + @VisibleForTesting + int mDisplayId; + @VisibleForTesting + int mSystemUiVisibility; // last value sent to window manager private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE; @@ -123,7 +126,8 @@ public class AutoHideController implements CommandQueue.Callbacks { } } - private void notifySystemUiVisibilityChanged(int vis) { + @VisibleForTesting + void notifySystemUiVisibilityChanged(int vis) { try { if (mLastDispatchedSystemUiVisibility != vis) { mWindowManagerService.statusBarVisibilityChanged(mDisplayId, vis); @@ -213,11 +217,12 @@ public class AutoHideController implements CommandQueue.Callbacks { return mask; } - private boolean hasNavigationBar() { + boolean hasNavigationBar() { return mNavigationBar != null; } - private boolean hasStatusBar() { + @VisibleForTesting + boolean hasStatusBar() { return mStatusBar != null; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 6fe23fbbbf86..847f3ff9f24d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -831,6 +831,15 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } } + /** + * Sets the alpha of the indication areas and affordances, excluding the lock icon. + */ + public void setAffordanceAlpha(float alpha) { + mLeftAffordanceView.setAlpha(alpha); + mRightAffordanceView.setAlpha(alpha); + mIndicationArea.setAlpha(alpha); + } + private class DefaultLeftButton implements IntentButton { private IconState mIconState = new IconState(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index 7d1367971ed6..19373ac8e28b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -88,6 +88,7 @@ public class KeyguardBouncer { private int mBouncerPromptReason; private boolean mIsAnimatingAway; private boolean mIsScrimmed; + private ViewGroup mLockIconContainer; public KeyguardBouncer(Context context, ViewMediatorCallback callback, LockPatternUtils lockPatternUtils, ViewGroup container, @@ -171,6 +172,10 @@ public class KeyguardBouncer { return isShowing() && mIsScrimmed; } + public ViewGroup getLockIconContainer() { + return mRoot == null || mRoot.getVisibility() != View.VISIBLE ? null : mLockIconContainer; + } + /** * This method must be called at the end of the bouncer animation when * the translation is performed manually by the user, otherwise FalsingManager @@ -401,6 +406,7 @@ public class KeyguardBouncer { removeView(); mHandler.removeCallbacks(mRemoveViewRunnable); mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null); + mLockIconContainer = mRoot.findViewById(R.id.lock_icon_container); mKeyguardView = mRoot.findViewById(R.id.keyguard_host_view); mKeyguardView.setLockPatternUtils(mLockPatternUtils); mKeyguardView.setViewMediatorCallback(mCallback); @@ -420,6 +426,7 @@ public class KeyguardBouncer { if (mRoot != null && mRoot.getParent() == mContainer) { mContainer.removeView(mRoot); mRoot = null; + mLockIconContainer = null; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java index 7a42b03947ae..1478a0780eb7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.hardware.input.InputManager; import android.os.Handler; import android.os.SystemClock; -import android.view.HapticFeedbackConstants; import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; @@ -103,7 +102,6 @@ public class NavigationBackAction extends NavigationGestureAction { private void performBack() { sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK); sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK); - mNavigationBarView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); } private boolean shouldExecuteBackOnUp() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java index fcf5893eafcc..4c7fdb0a6a7a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java @@ -22,6 +22,7 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PixelFormat; +import android.os.SystemClock; import android.util.FloatProperty; import android.util.MathUtils; import android.view.Gravity; @@ -31,6 +32,7 @@ import android.view.View; import android.view.WindowManager; import com.android.systemui.R; +import com.android.systemui.shared.system.QuickStepContract; public class NavigationBarEdgePanel extends View { private static final String TAG = "NavigationBarEdgePanel"; @@ -48,6 +50,7 @@ public class NavigationBarEdgePanel extends View { private static final float START_POINTING_RATIO = 0.3f; private static final float POINTEDNESS_BEFORE_SNAP_RATIO = 0.4f; private static final int ANIM_DURATION_MS = 150; + private static final long HAPTIC_TIMEOUT_MS = 200; private final Paint mPaint = new Paint(); private final Paint mProtectionPaint = new Paint(); @@ -65,6 +68,8 @@ public class NavigationBarEdgePanel extends View { private float mStartY; private float mStartX; + private boolean mDragSlopPassed; + private long mLastSlopHapticTime; private boolean mGestureDetected; private boolean mArrowsPointLeft; private float mGestureLength; @@ -169,6 +174,7 @@ public class NavigationBarEdgePanel extends View { public boolean onTouchEvent(MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN : { + mDragSlopPassed = false; show(event.getX(), event.getY()); break; } @@ -263,6 +269,13 @@ public class NavigationBarEdgePanel extends View { private void handleNewSwipePoint(float x) { float dist = MathUtils.abs(x - mStartX); + // Apply a haptic on drag slop passed + if (!mDragSlopPassed && dist > QuickStepContract.getQuickStepDragSlopPx()) { + mDragSlopPassed = true; + performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK); + mLastSlopHapticTime = SystemClock.uptimeMillis(); + } + setDragProgress(MathUtils.constrainedMap( 0, 1.0f, 0, mGestureLength * TRACK_LENGTH_MULTIPLIER, @@ -286,7 +299,10 @@ public class NavigationBarEdgePanel extends View { } } else { if (!mGestureDetected) { - performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK); + // Prevent another haptic if it was just used + if (SystemClock.uptimeMillis() - mLastSlopHapticTime > HAPTIC_TIMEOUT_MS) { + performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK); + } mGestureDetected = true; mLegAnimator.setFloatValues(1f); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 09789019d6ef..ea30451175d4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -159,7 +159,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback private OverviewProxyService mOverviewProxyService; - private int mDisplayId; + @VisibleForTesting + public int mDisplayId; private boolean mIsOnDefaultDisplay; public boolean mHomeBlockedThisTouch; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index babee5393e8c..253bdfb1a206 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -192,7 +192,6 @@ public class NotificationPanelView extends PanelView implements protected int mQsMinExpansionHeight; protected int mQsMaxExpansionHeight; private int mQsPeekHeight; - private int mBouncerTop; private boolean mStackScrollerOverscrolling; private boolean mQsExpansionFromOverscroll; private float mLastOverscroll; @@ -325,6 +324,7 @@ public class NotificationPanelView extends PanelView implements private final ShadeController mShadeController = Dependency.get(ShadeController.class); private int mDisplayId; + private KeyguardBouncer mBouncer; /** * Cache the resource id of the theme to avoid unnecessary work in onThemeChanged. @@ -690,11 +690,6 @@ public class NotificationPanelView extends PanelView implements return count; } - public void setBouncerTop(int bouncerTop) { - mBouncerTop = bouncerTop; - positionClockAndNotifications(); - } - private void updateClock() { if (!mKeyguardStatusViewAnimating) { mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha); @@ -1741,7 +1736,6 @@ public class NotificationPanelView extends PanelView implements } updateExpandedHeight(expandedHeight); updateHeader(); - updateUnlockIcon(); updateNotificationTranslucency(); updatePanelExpanded(); if (DEBUG) { @@ -1834,19 +1828,6 @@ public class NotificationPanelView extends PanelView implements return mNotificationStackScroller.getCurrentOverScrolledPixels(true /* top */); } - private void updateUnlockIcon() { - if (mBarState == StatusBarState.KEYGUARD - || mBarState == StatusBarState.SHADE_LOCKED) { - boolean active = getMaxPanelHeight() - getExpandedHeight() > mUnlockMoveDistance; - KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon(); - if (active != mUnlockIconActive && mTracking) { - lockIcon.setImageAlpha(lockIcon.getRestingAlpha(), true, 150, - Interpolators.FAST_OUT_LINEAR_IN, null); - } - mUnlockIconActive = active; - } - } - /** * Hides the header when notifications are colliding with it. */ @@ -1913,7 +1894,7 @@ public class NotificationPanelView extends PanelView implements ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f, getExpandedFraction()); float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction()); - mKeyguardBottomArea.setAlpha(alpha); + mKeyguardBottomArea.setAffordanceAlpha(alpha); mKeyguardBottomArea.setImportantForAccessibility(alpha == 0f ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS : IMPORTANT_FOR_ACCESSIBILITY_AUTO); @@ -2061,11 +2042,6 @@ public class NotificationPanelView extends PanelView implements mAffordanceHelper.reset(true); } } - if (!expand && (mBarState == StatusBarState.KEYGUARD - || mBarState == StatusBarState.SHADE_LOCKED)) { - KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon(); - lockIcon.setImageAlpha(0.0f, true, 100, Interpolators.FAST_OUT_LINEAR_IN, null); - } } @Override @@ -2215,22 +2191,6 @@ public class NotificationPanelView extends PanelView implements return; } super.startUnlockHintAnimation(); - startHighlightIconAnimation(getCenterIcon()); - } - - /** - * Starts the highlight (making it fully opaque) animation on an icon. - */ - private void startHighlightIconAnimation(final KeyguardAffordanceView icon) { - icon.setImageAlpha(1.0f, true, KeyguardAffordanceHelper.HINT_PHASE1_DURATION, - Interpolators.FAST_OUT_SLOW_IN, new Runnable() { - @Override - public void run() { - icon.setImageAlpha(icon.getRestingAlpha(), - true /* animate */, KeyguardAffordanceHelper.HINT_PHASE1_DURATION, - Interpolators.FAST_OUT_SLOW_IN, null); - } - }); } @Override @@ -2969,6 +2929,7 @@ public class NotificationPanelView extends PanelView implements public void onBouncerPreHideAnimation() { setKeyguardStatusViewVisibility(mBarState, true /* keyguardFadingAway */, false /* goingToFullShade */); + updateLockIcon(); } @Override @@ -3058,4 +3019,54 @@ public class NotificationPanelView extends PanelView implements public void showTransientIndication(int id) { mKeyguardBottomArea.showTransientIndication(id); } + + /** + * Sets the reference to the {@link KeyguardBouncer}. + */ + public void setBouncer(KeyguardBouncer bouncer) { + mBouncer = bouncer; + updateLockIcon(); + } + + public void updateLockIcon() { + if (mBouncer == null) { + return; + } + + ViewGroup bouncerContainer = mBouncer.getLockIconContainer(); + LockIcon lockIcon = mKeyguardBottomArea.getLockIcon(); + + if (mBouncer.isAnimatingAway()) { + if (!lockIcon.isAnimatingAlpha() && lockIcon.getAlpha() != 0) { + lockIcon.setImageAlpha(0, true /* animate */); + } + // Let's not re-apply the translation if the bouncer is animating, its + // animation also includes translation and transition would be jarring. + return; + } + + float translation = 0; + if (bouncerContainer != null) { + float bottomAreaContainerY = getCommonTop(lockIcon); + float bouncerLockY = getCommonTop(bouncerContainer); + if (bouncerLockY < bottomAreaContainerY) { + translation = bouncerLockY - bottomAreaContainerY; + } + } + lockIcon.setTranslationY(translation); + + if (lockIcon.getAlpha() != 1) { + lockIcon.setImageAlpha(1, false /* animate */); + } + } + + private static float getCommonTop(View view) { + float y = view.getTop(); + ViewGroup parent = (ViewGroup) view.getParent(); + while (!(parent instanceof StatusBarWindowView)) { + y += parent.getY(); + parent = (ViewGroup) parent.getParent(); + } + return y; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 2495d2253abb..99345d26abc9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -1115,6 +1115,7 @@ public abstract class PanelView extends FrameLayout { View[] viewsToAnimate = { mKeyguardBottomArea.getIndicationArea(), + mKeyguardBottomArea.getLockIcon(), mStatusBar.getAmbientIndicationContainer()}; for (View v : viewsToAnimate) { if (v == null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 8db08221ef73..7e330e95fd69 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -882,7 +882,6 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationPanel.getLockIcon()); mNotificationPanel.setKeyguardIndicationController(mKeyguardIndicationController); - mAmbientIndicationContainer = mStatusBarWindow.findViewById( R.id.ambient_indication_container); @@ -1220,6 +1219,7 @@ public class StatusBar extends SystemUI implements DemoMode, mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback(); mLightBarController.setBiometricUnlockController(mBiometricUnlockController); mMediaManager.setBiometricUnlockController(mBiometricUnlockController); + mNotificationPanel.setBouncer(mStatusBarKeyguardViewManager.getBouncer()); Dependency.get(KeyguardDismissUtil.class).setDismissHandler(this::executeWhenUnlocked); Trace.endSection(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 5014783c43b1..e8a5c7a5c14a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -166,16 +166,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext, mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry, mExpansionCallback); - mContainer.addOnLayoutChangeListener(this::onContainerLayout); mNotificationPanelView = notificationPanelView; notificationPanelView.setExpansionListener(this::onPanelExpansionChanged); } - private void onContainerLayout(View v, int left, int top, int right, int bottom, - int oldLeft, int oldTop, int oldRight, int oldBottom) { - mNotificationPanelView.setBouncerTop(mBouncer.getTop()); - } - @VisibleForTesting void onPanelExpansionChanged(float expansion, boolean tracking) { // We don't want to translate the bounce when: @@ -198,6 +192,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */); } } + mNotificationPanelView.updateLockIcon(); } /** @@ -795,6 +790,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb setDozing(isDozing); } + public KeyguardBouncer getBouncer() { + return mBouncer; + } + private static class DismissWithActionRequest { final OnDismissAction dismissAction; final Runnable cancelAction; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java index d4049826a50a..024404d2175b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.policy; import static android.view.Display.INVALID_DISPLAY; +import static android.view.KeyEvent.KEYCODE_UNKNOWN; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK; @@ -101,7 +102,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KeyButtonView, defStyle, 0); - mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, 0); + mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, KEYCODE_UNKNOWN); mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true); mPlaySounds = a.getBoolean(R.styleable.KeyButtonView_playSound, true); @@ -124,7 +125,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { @Override public boolean isClickable() { - return mCode != 0 || super.isClickable(); + return mCode != KEYCODE_UNKNOWN || super.isClickable(); } public void setCode(int code) { @@ -163,7 +164,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); - if (mCode != 0) { + if (mCode != KEYCODE_UNKNOWN) { info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK, null)); if (mSupportsLongpress || isLongClickable()) { info.addAction( @@ -182,13 +183,13 @@ public class KeyButtonView extends ImageView implements ButtonInterface { @Override public boolean performAccessibilityActionInternal(int action, Bundle arguments) { - if (action == ACTION_CLICK && mCode != 0) { + if (action == ACTION_CLICK && mCode != KEYCODE_UNKNOWN) { sendEvent(KeyEvent.ACTION_DOWN, 0, SystemClock.uptimeMillis()); sendEvent(KeyEvent.ACTION_UP, 0); sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); playSoundEffect(SoundEffectConstants.CLICK); return true; - } else if (action == ACTION_LONG_CLICK && mCode != 0) { + } else if (action == ACTION_LONG_CLICK && mCode != KEYCODE_UNKNOWN) { sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS); sendEvent(KeyEvent.ACTION_UP, 0); sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); @@ -197,6 +198,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { return super.performAccessibilityActionInternal(action, arguments); } + @Override public boolean onTouchEvent(MotionEvent ev) { final boolean showSwipeUI = mOverviewProxyService.shouldShowSwipeUpUI(); final int action = ev.getAction(); @@ -218,7 +220,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { // Use raw X and Y to detect gestures in case a parent changes the x and y values mTouchDownX = (int) ev.getRawX(); mTouchDownY = (int) ev.getRawY(); - if (mCode != 0) { + if (mCode != KEYCODE_UNKNOWN) { sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime); } else { // Provide the same haptic feedback that the system offers for virtual keys. @@ -249,7 +251,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { break; case MotionEvent.ACTION_CANCEL: setPressed(false); - if (mCode != 0) { + if (mCode != KEYCODE_UNKNOWN) { sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED); } removeCallbacks(mCheckLongPress); @@ -269,7 +271,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { // and it feels weird to sometimes get a release haptic and other times not. performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE); } - if (mCode != 0) { + if (mCode != KEYCODE_UNKNOWN) { if (doIt) { sendEvent(KeyEvent.ACTION_UP, 0); sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); @@ -299,7 +301,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { sendEvent(action, flags, SystemClock.uptimeMillis()); } - void sendEvent(int action, int flags, long when) { + private void sendEvent(int action, int flags, long when) { mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_NAV_BUTTON_EVENT) .setType(MetricsEvent.TYPE_ACTION) .setSubtype(mCode) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 3deede091a05..d3164a09f7e6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -22,7 +22,7 @@ import android.net.NetworkCapabilities; import android.os.Handler; import android.os.Looper; import android.provider.Settings.Global; -import android.telephony.NetworkRegistrationState; +import android.telephony.NetworkRegistrationInfo; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SignalStrength; @@ -475,7 +475,7 @@ public class MobileSignalController extends SignalController< if (mServiceState == null) return null; int nrStatus = mServiceState.getNrStatus(); - if (nrStatus == NetworkRegistrationState.NR_STATUS_CONNECTED) { + if (nrStatus == NetworkRegistrationInfo.NR_STATUS_CONNECTED) { // Check if the NR 5G is using millimeter wave and the icon is config. if (mServiceState.getNrFrequencyRange() == ServiceState.FREQUENCY_RANGE_MMWAVE) { if (mConfig.nr5GIconMap.containsKey(Config.NR_CONNECTED_MMWAVE)) { @@ -488,11 +488,11 @@ public class MobileSignalController extends SignalController< if (mConfig.nr5GIconMap.containsKey(Config.NR_CONNECTED)) { return mConfig.nr5GIconMap.get(Config.NR_CONNECTED); } - } else if (nrStatus == NetworkRegistrationState.NR_STATUS_NOT_RESTRICTED) { + } else if (nrStatus == NetworkRegistrationInfo.NR_STATUS_NOT_RESTRICTED) { if (mConfig.nr5GIconMap.containsKey(Config.NR_NOT_RESTRICTED)) { return mConfig.nr5GIconMap.get(Config.NR_NOT_RESTRICTED); } - } else if (nrStatus == NetworkRegistrationState.NR_STATUS_RESTRICTED) { + } else if (nrStatus == NetworkRegistrationInfo.NR_STATUS_RESTRICTED) { if (mConfig.nr5GIconMap.containsKey(Config.NR_RESTRICTED)) { return mConfig.nr5GIconMap.get(Config.NR_RESTRICTED); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java index b0d1106ecb24..29505a2046bb 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -101,6 +101,8 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { // AND the plugin returns a view for the big clock ClockPlugin plugin = mock(ClockPlugin.class); when(plugin.getBigClockView()).thenReturn(mBigClock); + // AND in the keyguard state + mStateListener.onStateChanged(StatusBarState.KEYGUARD); // WHEN the plugin is connected mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); // THEN the big clock container is visible and it is the parent of the @@ -166,6 +168,8 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { ClockPlugin plugin = mock(ClockPlugin.class); TextClock pluginView = new TextClock(getContext()); when(plugin.getBigClockView()).thenReturn(pluginView); + // AND in the keyguard state + mStateListener.onStateChanged(StatusBarState.KEYGUARD); // WHEN the plugin is connected and then disconnected mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); mKeyguardClockSwitch.getClockChangedListener().onClockChanged(null); @@ -245,21 +249,25 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { } @Test - public void onStateChanged_InvisibleInShade() { + public void onStateChanged_GoneInShade() { // GIVEN that the big clock container is visible mBigClockContainer.setVisibility(View.VISIBLE); mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer); // WHEN transitioned to SHADE state mStateListener.onStateChanged(StatusBarState.SHADE); - // THEN the container is invisible. - assertThat(mBigClockContainer.getVisibility()).isEqualTo(View.INVISIBLE); + // THEN the container is gone. + assertThat(mBigClockContainer.getVisibility()).isEqualTo(View.GONE); } @Test public void onStateChanged_VisibleInKeyguard() { - // GIVEN that the big clock container is invisible - mBigClockContainer.setVisibility(View.INVISIBLE); + // GIVEN that the big clock container is gone + mBigClockContainer.setVisibility(View.GONE); mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer); + // AND GIVEN that a plugin is active. + ClockPlugin plugin = mock(ClockPlugin.class); + when(plugin.getBigClockView()).thenReturn(mBigClock); + mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); // WHEN transitioned to KEYGUARD state mStateListener.onStateChanged(StatusBarState.KEYGUARD); // THEN the container is visible. @@ -274,6 +282,8 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { ClockPlugin plugin = mock(ClockPlugin.class); when(plugin.getBigClockView()).thenReturn(mBigClock); mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); + // AND in the keyguard state + mStateListener.onStateChanged(StatusBarState.KEYGUARD); // WHEN the container is associated with the clock switch mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer); // THEN the container remains visible. @@ -281,20 +291,6 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { } @Test - public void setBigClockContainer_invisible() { - // GIVEN that the big clock container is invisible - mBigClockContainer.setVisibility(View.INVISIBLE); - // AND GIVEN that a plugin is active. - ClockPlugin plugin = mock(ClockPlugin.class); - when(plugin.getBigClockView()).thenReturn(mBigClock); - mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); - // WHEN the container is associated with the clock switch - mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer); - // THEN the container remains invisible. - assertThat(mBigClockContainer.getVisibility()).isEqualTo(View.INVISIBLE); - } - - @Test public void setBigClockContainer_gone() { // GIVEN that the big clock container is gone mBigClockContainer.setVisibility(View.GONE); @@ -302,6 +298,8 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { ClockPlugin plugin = mock(ClockPlugin.class); when(plugin.getBigClockView()).thenReturn(mBigClock); mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); + // AND in the keyguard state + mStateListener.onStateChanged(StatusBarState.KEYGUARD); // WHEN the container is associated with the clock switch mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer); // THEN the container is made visible. diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java index dd2636803b94..c2f55e2f9b99 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java @@ -41,6 +41,7 @@ public class CommandQueueTest extends SysuiTestCase { private CommandQueue mCommandQueue; private Callbacks mCallbacks; + private static final int SECONDARY_DISPLAY = 1; @Before public void setup() { @@ -68,7 +69,6 @@ public class CommandQueueTest extends SysuiTestCase { verify(mCallbacks).removeIcon(eq(slot)); } - // TODO(b/117478341): add test case for multi-display @Test public void testDisable() { int state1 = 14; @@ -79,6 +79,15 @@ public class CommandQueueTest extends SysuiTestCase { } @Test + public void testDisableForSecondaryDisplay() { + int state1 = 14; + int state2 = 42; + mCommandQueue.disable(SECONDARY_DISPLAY, state1, state2); + waitForIdleSync(); + verify(mCallbacks).disable(eq(SECONDARY_DISPLAY), eq(state1), eq(state2), eq(true)); + } + + @Test public void testExpandNotifications() { mCommandQueue.animateExpandNotificationsPanel(); waitForIdleSync(); @@ -100,7 +109,6 @@ public class CommandQueueTest extends SysuiTestCase { verify(mCallbacks).animateExpandSettingsPanel(eq(panel)); } - // TODO(b/117478341): add test case for multi-display @Test public void testSetSystemUiVisibility() { Rect r = new Rect(); @@ -110,7 +118,15 @@ public class CommandQueueTest extends SysuiTestCase { eq(null), eq(r)); } - // TODO(b/117478341): add test case for multi-display + @Test + public void testSetSystemUiVisibilityForSecondaryDisplay() { + Rect r = new Rect(); + mCommandQueue.setSystemUiVisibility(SECONDARY_DISPLAY, 1, 2, 3, 4, null, r); + waitForIdleSync(); + verify(mCallbacks).setSystemUiVisibility(eq(SECONDARY_DISPLAY), eq(1), eq(2), eq(3), eq(4), + eq(null), eq(r)); + } + @Test public void testTopAppWindowChanged() { mCommandQueue.topAppWindowChanged(DEFAULT_DISPLAY, true); @@ -118,7 +134,13 @@ public class CommandQueueTest extends SysuiTestCase { verify(mCallbacks).topAppWindowChanged(eq(DEFAULT_DISPLAY), eq(true)); } - // TODO(b/117478341): add test case for multi-display + @Test + public void testTopAppWindowChangedForSecondaryDisplay() { + mCommandQueue.topAppWindowChanged(SECONDARY_DISPLAY, true); + waitForIdleSync(); + verify(mCallbacks).topAppWindowChanged(eq(SECONDARY_DISPLAY), eq(true)); + } + @Test public void testShowImeButton() { mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, 1, 2, true); @@ -128,6 +150,14 @@ public class CommandQueueTest extends SysuiTestCase { } @Test + public void testShowImeButtonForSecondaryDisplay() { + mCommandQueue.setImeWindowStatus(SECONDARY_DISPLAY, null, 1, 2, true); + waitForIdleSync(); + verify(mCallbacks).setImeWindowStatus( + eq(SECONDARY_DISPLAY), eq(null), eq(1), eq(2), eq(true)); + } + + @Test public void testShowRecentApps() { mCommandQueue.showRecentApps(true); waitForIdleSync(); @@ -176,7 +206,6 @@ public class CommandQueueTest extends SysuiTestCase { verify(mCallbacks).toggleKeyboardShortcutsMenu(eq(1)); } - // TODO(b/117478341): add test case for multi-display @Test public void testSetWindowState() { mCommandQueue.setWindowState(DEFAULT_DISPLAY, 1, 2); @@ -185,13 +214,19 @@ public class CommandQueueTest extends SysuiTestCase { } @Test + public void testSetWindowStateForSecondaryDisplay() { + mCommandQueue.setWindowState(SECONDARY_DISPLAY, 1, 2); + waitForIdleSync(); + verify(mCallbacks).setWindowState(eq(SECONDARY_DISPLAY), eq(1), eq(2)); + } + + @Test public void testScreenPinRequest() { mCommandQueue.showScreenPinningRequest(1); waitForIdleSync(); verify(mCallbacks).showScreenPinningRequest(eq(1)); } - // TODO(b/117478341): add test case for multi-display @Test public void testAppTransitionPending() { mCommandQueue.appTransitionPending(DEFAULT_DISPLAY); @@ -199,7 +234,13 @@ public class CommandQueueTest extends SysuiTestCase { verify(mCallbacks).appTransitionPending(eq(DEFAULT_DISPLAY), eq(false)); } - // TODO(b/117478341): add test case for multi-display + @Test + public void testAppTransitionPendingForSecondaryDisplay() { + mCommandQueue.appTransitionPending(SECONDARY_DISPLAY); + waitForIdleSync(); + verify(mCallbacks).appTransitionPending(eq(SECONDARY_DISPLAY), eq(false)); + } + @Test public void testAppTransitionCancelled() { mCommandQueue.appTransitionCancelled(DEFAULT_DISPLAY); @@ -207,7 +248,13 @@ public class CommandQueueTest extends SysuiTestCase { verify(mCallbacks).appTransitionCancelled(eq(DEFAULT_DISPLAY)); } - // TODO(b/117478341): add test case for multi-display + @Test + public void testAppTransitionCancelledForSecondaryDisplay() { + mCommandQueue.appTransitionCancelled(SECONDARY_DISPLAY); + waitForIdleSync(); + verify(mCallbacks).appTransitionCancelled(eq(SECONDARY_DISPLAY)); + } + @Test public void testAppTransitionStarting() { mCommandQueue.appTransitionStarting(DEFAULT_DISPLAY, 1, 2); @@ -216,7 +263,14 @@ public class CommandQueueTest extends SysuiTestCase { eq(DEFAULT_DISPLAY), eq(1L), eq(2L), eq(false)); } - // TODO(b/117478341): add test case for multi-display + @Test + public void testAppTransitionStartingForSecondaryDisplay() { + mCommandQueue.appTransitionStarting(SECONDARY_DISPLAY, 1, 2); + waitForIdleSync(); + verify(mCallbacks).appTransitionStarting( + eq(SECONDARY_DISPLAY), eq(1L), eq(2L), eq(false)); + } + @Test public void testAppTransitionFinished() { mCommandQueue.appTransitionFinished(DEFAULT_DISPLAY); @@ -225,6 +279,13 @@ public class CommandQueueTest extends SysuiTestCase { } @Test + public void testAppTransitionFinishedForSecondaryDisplay() { + mCommandQueue.appTransitionFinished(SECONDARY_DISPLAY); + waitForIdleSync(); + verify(mCallbacks).appTransitionFinished(eq(SECONDARY_DISPLAY)); + } + + @Test public void testAssistDisclosure() { mCommandQueue.showAssistDisclosure(); waitForIdleSync(); @@ -290,4 +351,25 @@ public class CommandQueueTest extends SysuiTestCase { waitForIdleSync(); verify(mCallbacks).handleSystemKey(eq(1)); } + + @Test + public void testOnDisplayReady() { + mCommandQueue.onDisplayReady(DEFAULT_DISPLAY); + waitForIdleSync(); + verify(mCallbacks).onDisplayReady(eq(DEFAULT_DISPLAY)); + } + + @Test + public void testOnDisplayReadyForSecondaryDisplay() { + mCommandQueue.onDisplayReady(SECONDARY_DISPLAY); + waitForIdleSync(); + verify(mCallbacks).onDisplayReady(eq(SECONDARY_DISPLAY)); + } + + @Test + public void testOnDisplayRemoved() { + mCommandQueue.onDisplayRemoved(SECONDARY_DISPLAY); + waitForIdleSync(); + verify(mCallbacks).onDisplayRemoved(eq(SECONDARY_DISPLAY)); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NavigationBarControllerTest.java new file mode 100644 index 000000000000..34a726d526a3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NavigationBarControllerTest.java @@ -0,0 +1,243 @@ +/* + * 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.systemui.statusbar; + +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.INVALID_DISPLAY; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.hardware.display.DisplayManager; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; +import android.util.SparseArray; +import android.view.Display; +import android.view.WindowManager; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.Dependency; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.phone.NavigationBarFragment; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** atest NavigationBarControllerTest */ +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +@SmallTest +public class NavigationBarControllerTest extends SysuiTestCase { + + private NavigationBarController mNavigationBarController; + private Display mDisplay; + private NavigationBarFragment mDefaultNavBar; + private NavigationBarFragment mSecondaryNavBar; + + private static final int SECONDARY_DISPLAY = 1; + + @Before + public void setUp() { + mContext.putComponent(CommandQueue.class, mock(CommandQueue.class)); + mNavigationBarController = spy( + new NavigationBarController(mContext, Dependency.get(Dependency.MAIN_HANDLER))); + initializeNavigationBars(); + } + + private void initializeNavigationBars() { + mNavigationBarController.mNavigationBars = mock(SparseArray.class); + mDefaultNavBar = mock(NavigationBarFragment.class); + mDefaultNavBar.mDisplayId = DEFAULT_DISPLAY; + doReturn(mDefaultNavBar) + .when(mNavigationBarController.mNavigationBars).get(DEFAULT_DISPLAY); + + mSecondaryNavBar = mock(NavigationBarFragment.class); + mSecondaryNavBar.mDisplayId = SECONDARY_DISPLAY; + doReturn(mSecondaryNavBar) + .when(mNavigationBarController.mNavigationBars).get(SECONDARY_DISPLAY); + } + + @After + public void tearDown() { + mNavigationBarController = null; + mDisplay = null; + mDefaultNavBar = null; + mSecondaryNavBar = null; + } + + @Test + public void testCreateNavigationBarsIncludeDefaultTrue() { + initializeDisplayManager(); + doNothing().when(mNavigationBarController).createNavigationBar(any()); + + mNavigationBarController.createNavigationBars(true); + + verify(mNavigationBarController).createNavigationBar(any(Display.class)); + } + + @Test + public void testCreateNavigationBarsIncludeDefaultFalse() { + initializeDisplayManager(); + doNothing().when(mNavigationBarController).createNavigationBar(any()); + + mNavigationBarController.createNavigationBars(false); + + verify(mNavigationBarController, never()).createNavigationBar(any()); + } + + private void initializeDisplayManager() { + DisplayManager displayManager = mock(DisplayManager.class); + mDisplay = mContext.getSystemService(WindowManager.class).getDefaultDisplay(); + Display[] displays = {mDisplay}; + when(displayManager.getDisplays()).thenReturn(displays); + mContext.addMockSystemService(Context.DISPLAY_SERVICE, displayManager); + } + + // Tests if NPE occurs when call checkNavBarModes() with invalid display. + @Test + public void testCheckNavBarModesWithInvalidDisplay() { + mNavigationBarController.checkNavBarModes(INVALID_DISPLAY); + } + + @Test + public void testCheckNavBarModesWithDefaultDisplay() { + doNothing().when(mDefaultNavBar).checkNavBarModes(); + + mNavigationBarController.checkNavBarModes(DEFAULT_DISPLAY); + + verify(mDefaultNavBar).checkNavBarModes(); + } + + @Test + public void testCheckNavBarModesWithSecondaryDisplay() { + doNothing().when(mSecondaryNavBar).checkNavBarModes(); + + mNavigationBarController.checkNavBarModes(SECONDARY_DISPLAY); + + verify(mSecondaryNavBar).checkNavBarModes(); + } + + // Tests if NPE occurs when call finishBarAnimations() with invalid display. + @Test + public void testFinishBarAnimationsWithInvalidDisplay() { + mNavigationBarController.finishBarAnimations(INVALID_DISPLAY); + } + + @Test + public void testFinishBarAnimationsWithDefaultDisplay() { + doNothing().when(mDefaultNavBar).finishBarAnimations(); + + mNavigationBarController.finishBarAnimations(DEFAULT_DISPLAY); + + verify(mDefaultNavBar).finishBarAnimations(); + } + + @Test + public void testFinishBarAnimationsWithSecondaryDisplay() { + doNothing().when(mSecondaryNavBar).finishBarAnimations(); + + mNavigationBarController.finishBarAnimations(SECONDARY_DISPLAY); + + verify(mSecondaryNavBar).finishBarAnimations(); + } + + // Tests if NPE occurs when call touchAutoDim() with invalid display. + @Test + public void testTouchAutoDimWithInvalidDisplay() { + mNavigationBarController.touchAutoDim(INVALID_DISPLAY); + } + + @Test + public void testTouchAutoDimWithDefaultDisplay() { + doNothing().when(mDefaultNavBar).touchAutoDim(); + + mNavigationBarController.touchAutoDim(DEFAULT_DISPLAY); + + verify(mDefaultNavBar).touchAutoDim(); + } + + @Test + public void testTouchAutoDimWithSecondaryDisplay() { + doNothing().when(mSecondaryNavBar).touchAutoDim(); + + mNavigationBarController.touchAutoDim(SECONDARY_DISPLAY); + + verify(mSecondaryNavBar).touchAutoDim(); + } + + // Tests if NPE occurs when call transitionTo() with invalid display. + @Test + public void testTransitionToWithInvalidDisplay() { + mNavigationBarController.transitionTo(INVALID_DISPLAY, 3, true); + } + + @Test + public void testTransitionToWithDefaultDisplay() { + doNothing().when(mDefaultNavBar).transitionTo(anyInt(), anyBoolean()); + + mNavigationBarController.transitionTo(DEFAULT_DISPLAY, 3, true); + + verify(mDefaultNavBar).transitionTo(eq(3), eq(true)); + } + + @Test + public void testTransitionToWithSecondaryDisplay() { + doNothing().when(mSecondaryNavBar).transitionTo(anyInt(), anyBoolean()); + + mNavigationBarController.transitionTo(SECONDARY_DISPLAY, 3, true); + + verify(mSecondaryNavBar).transitionTo(eq(3), eq(true)); + } + + // Tests if NPE occurs when call disableAnimationsDuringHide() with invalid display. + @Test + public void testDisableAnimationsDuringHideWithInvalidDisplay() { + mNavigationBarController.disableAnimationsDuringHide(INVALID_DISPLAY, 500L); + } + + @Test + public void testDisableAnimationsDuringHideWithDefaultDisplay() { + doNothing().when(mDefaultNavBar).disableAnimationsDuringHide(anyLong()); + + mNavigationBarController.disableAnimationsDuringHide(DEFAULT_DISPLAY, 500L); + + verify(mDefaultNavBar).disableAnimationsDuringHide(eq(500L)); + } + + @Test + public void testDisableAnimationsDuringHideWithSecondaryDisplay() { + doNothing().when(mSecondaryNavBar).disableAnimationsDuringHide(anyLong()); + + mNavigationBarController.disableAnimationsDuringHide(SECONDARY_DISPLAY, 500L); + + verify(mSecondaryNavBar).disableAnimationsDuringHide(eq(500L)); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java new file mode 100644 index 000000000000..1b34a7584994 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java @@ -0,0 +1,118 @@ +/* + * 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.systemui.statusbar.phone; + +import static android.view.Display.DEFAULT_DISPLAY; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.graphics.Rect; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.Dependency; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.CommandQueue; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** atest AutoHideControllerTest */ +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +@SmallTest +public class AutoHideControllerTest extends SysuiTestCase { + + private AutoHideController mAutoHideController; + + private static final int FULL_MASK = 0xffffffff; + + @Before + public void setUp() { + mContext.putComponent(CommandQueue.class, mock(CommandQueue.class)); + mAutoHideController = + spy(new AutoHideController(mContext, Dependency.get(Dependency.MAIN_HANDLER))); + mAutoHideController.mDisplayId = DEFAULT_DISPLAY; + mAutoHideController.mSystemUiVisibility = View.VISIBLE; + } + + @After + public void tearDown() { + mAutoHideController = null; + } + + @Test + public void testSetSystemUiVisibilityEarlyReturnWithDifferentDisplay() { + mAutoHideController.setSystemUiVisibility(1, 1, 2, 3, 4, null, new Rect()); + + verify(mAutoHideController, never()).notifySystemUiVisibilityChanged(anyInt()); + } + + @Test + public void testSetSystemUiVisibilityEarlyReturnWithSameVisibility() { + mAutoHideController + .setSystemUiVisibility(DEFAULT_DISPLAY, View.VISIBLE, 2, 3, 4, null, new Rect()); + + verify(mAutoHideController, never()).notifySystemUiVisibilityChanged(anyInt()); + } + + // Test if status bar unhide status doesn't change without status bar. + @Test + public void testSetSystemUiVisibilityWithoutStatusBar() { + doReturn(false).when(mAutoHideController).hasStatusBar(); + int expectedStatus = View.STATUS_BAR_UNHIDE; + mAutoHideController.mSystemUiVisibility = + View.SYSTEM_UI_FLAG_FULLSCREEN | View.STATUS_BAR_UNHIDE; + + mAutoHideController.setSystemUiVisibility( + DEFAULT_DISPLAY, expectedStatus, 2, 3, FULL_MASK, null, new Rect()); + + assertEquals("System UI visibility should not be changed", + expectedStatus, mAutoHideController.mSystemUiVisibility); + verify(mAutoHideController, times(1)).notifySystemUiVisibilityChanged(eq(expectedStatus)); + } + + @Test + public void testSetSystemUiVisibilityWithVisChanged() { + doReturn(true).when(mAutoHideController).hasStatusBar(); + doReturn(true).when(mAutoHideController).hasNavigationBar(); + mAutoHideController.mSystemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN + | View.STATUS_BAR_UNHIDE + | View.NAVIGATION_BAR_UNHIDE; + + mAutoHideController.setSystemUiVisibility( + DEFAULT_DISPLAY, View.STATUS_BAR_UNHIDE | View.NAVIGATION_BAR_UNHIDE, + 2, 3, FULL_MASK, null, new Rect()); + + int expectedStatus = View.VISIBLE; + assertEquals(expectedStatus, mAutoHideController.mSystemUiVisibility); + verify(mAutoHideController).notifySystemUiVisibilityChanged(eq(expectedStatus)); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java index 2baea1ae3b19..5786796d980f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java @@ -7,7 +7,7 @@ import static org.mockito.Mockito.when; import android.net.NetworkCapabilities; import android.os.Looper; -import android.telephony.NetworkRegistrationState; +import android.telephony.NetworkRegistrationInfo; import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; @@ -151,7 +151,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { updateDataConnectionState(TelephonyManager.DATA_CONNECTED, TelephonyManager.NETWORK_TYPE_LTE); ServiceState ss = Mockito.mock(ServiceState.class); - doReturn(NetworkRegistrationState.NR_STATUS_CONNECTED).when(ss).getNrStatus(); + doReturn(NetworkRegistrationInfo.NR_STATUS_CONNECTED).when(ss).getNrStatus(); doReturn(ServiceState.FREQUENCY_RANGE_HIGH).when(ss).getNrFrequencyRange(); mPhoneStateListener.onServiceStateChanged(ss); @@ -165,7 +165,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { updateDataConnectionState(TelephonyManager.DATA_CONNECTED, TelephonyManager.NETWORK_TYPE_LTE); ServiceState ss = Mockito.mock(ServiceState.class); - doReturn(NetworkRegistrationState.NR_STATUS_CONNECTED).when(ss).getNrStatus(); + doReturn(NetworkRegistrationInfo.NR_STATUS_CONNECTED).when(ss).getNrStatus(); doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(ss).getNrFrequencyRange(); mPhoneStateListener.onServiceStateChanged(ss); @@ -179,7 +179,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { updateDataConnectionState(TelephonyManager.DATA_CONNECTED, TelephonyManager.NETWORK_TYPE_LTE); ServiceState ss = Mockito.mock(ServiceState.class); - doReturn(NetworkRegistrationState.NR_STATUS_RESTRICTED).when(ss).getNrStatus(); + doReturn(NetworkRegistrationInfo.NR_STATUS_RESTRICTED).when(ss).getNrStatus(); mPhoneStateListener.onServiceStateChanged(mServiceState); verifyDataIndicators(TelephonyIcons.ICON_LTE); diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index bc4286356ead..f106228fc6f0 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -7117,6 +7117,9 @@ message MetricsEvent { // overridden by the system. FIELD_NOTIFICATION_IMPORTANCE_ASST = 1691; + // Open: Settings > Special App Access > Do not disturb control for app + ZEN_ACCESS_DETAIL = 1692; + // ---- End Q Constants, all Q constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/services/Android.bp b/services/Android.bp index 31385edd015f..567efac5753c 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -26,7 +26,6 @@ java_library { "services.contentsuggestions", "services.coverage", "services.devicepolicy", - "services.ipmemorystore", "services.midi", "services.net", "services.print", diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java index 65e31f3acf14..1e3f20ecd01a 100644 --- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java @@ -809,13 +809,9 @@ class TouchExplorer extends BaseEventStreamTransformation // Announce the end of the gesture recognition. sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); + // Don't announce the end of a the touch interaction if users didn't lift their fingers. if (interactionEnd) { - // Announce the end of a the touch interaction. sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); - } else { - // If gesture detection is end, but user doesn't release the finger, announce the - // transition to exploration state. - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); } mExitGestureDetectionModeDelayed.cancel(); @@ -1151,10 +1147,7 @@ class TouchExplorer extends BaseEventStreamTransformation public void run() { // Announce the end of gesture recognition. sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); - // Clearing puts is in touch exploration state with a finger already - // down, so announce the transition to exploration state. clear(); - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); } } diff --git a/services/backup/java/com/android/server/backup/params/RestoreParams.java b/services/backup/java/com/android/server/backup/params/RestoreParams.java index 5125b0d5234e..c9a6b6038a93 100644 --- a/services/backup/java/com/android/server/backup/params/RestoreParams.java +++ b/services/backup/java/com/android/server/backup/params/RestoreParams.java @@ -105,7 +105,7 @@ public class RestoreParams { /** * Caller specifies whether is considered a system-level restore. */ - public static RestoreParams createForRestoreSome( + public static RestoreParams createForRestorePackages( TransportClient transportClient, IRestoreObserver observer, IBackupManagerMonitor monitor, diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java index 0fa0f89e329e..10304c39f021 100644 --- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java +++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java @@ -22,6 +22,7 @@ import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSI import static com.android.server.backup.internal.BackupHandler.MSG_RUN_GET_RESTORE_SETS; import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.backup.IBackupManagerMonitor; import android.app.backup.IRestoreObserver; @@ -192,18 +193,22 @@ public class ActiveRestoreSession extends IRestoreSession.Stub { } // Restores of more than a single package are treated as 'system' restores - public synchronized int restoreSome(long token, IRestoreObserver observer, - IBackupManagerMonitor monitor, String[] packages) { + public synchronized int restorePackages(long token, @Nullable IRestoreObserver observer, + @NonNull String[] packages, @Nullable IBackupManagerMonitor monitor) { mBackupManagerService.getContext().enforceCallingOrSelfPermission( android.Manifest.permission.BACKUP, "performRestore"); if (DEBUG) { StringBuilder b = new StringBuilder(128); - b.append("restoreSome token="); + b.append("restorePackages token="); b.append(Long.toHexString(token)); b.append(" observer="); - b.append(observer.toString()); + if (observer == null) { + b.append("null"); + } else { + b.append(observer.toString()); + } b.append(" monitor="); if (monitor == null) { b.append("null"); @@ -260,7 +265,7 @@ public class ActiveRestoreSession extends IRestoreSession.Stub { try { return sendRestoreToHandlerLocked( (transportClient, listener) -> - RestoreParams.createForRestoreSome( + RestoreParams.createForRestorePackages( transportClient, observer, monitor, @@ -268,7 +273,7 @@ public class ActiveRestoreSession extends IRestoreSession.Stub { packages, /* isSystemRestore */ packages.length > 1, listener), - "RestoreSession.restoreSome(" + packages.length + " packages)"); + "RestoreSession.restorePackages(" + packages.length + " packages)"); } finally { Binder.restoreCallingIdentity(oldId); } diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index c2a06117cf14..7fab2b96ddbe 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -501,6 +501,8 @@ public class LocationManagerService extends ILocationManager.Stub { } if (broadcast) { + // needs to be sent to everyone because we don't know which user may have changed + // LOCATION_MODE state. mContext.sendBroadcastAsUser( new Intent(LocationManager.MODE_CHANGED_ACTION), UserHandle.ALL); @@ -1212,6 +1214,13 @@ public class LocationManagerService extends ILocationManager.Stub { "-" + mName, mCurrentUserId); } + + // needs to be sent to all users because whether or not a provider is enabled for + // a given user is complicated... we broadcast to everyone and let them figure it + // out via isProviderEnabled() + Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION); + intent.putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); } if (useable == mUseable) { @@ -1232,10 +1241,6 @@ public class LocationManagerService extends ILocationManager.Stub { } updateProviderUseableLocked(this); - - mContext.sendBroadcastAsUser( - new Intent(LocationManager.PROVIDERS_CHANGED_ACTION), - UserHandle.ALL); } @GuardedBy("mLock") diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 5582ad77c731..d3298b9238fc 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -62,6 +62,7 @@ import android.net.NetworkUtils; import android.net.RouteInfo; import android.net.TetherStatsParcel; import android.net.UidRange; +import android.net.UidRangeParcel; import android.net.util.NetdService; import android.os.BatteryStats; import android.os.Binder; @@ -80,6 +81,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; import android.telephony.DataConnectionRealTimeInfo; +import android.text.TextUtils; import android.util.Log; import android.util.Slog; import android.util.SparseBooleanArray; @@ -1028,6 +1030,46 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } + /** + * Convert InterfaceConfiguration to InterfaceConfigurationParcel with given ifname. + */ + private static InterfaceConfigurationParcel toStableParcel(InterfaceConfiguration cfg, + String iface) { + InterfaceConfigurationParcel cfgParcel = new InterfaceConfigurationParcel(); + cfgParcel.ifName = iface; + String hwAddr = cfg.getHardwareAddress(); + if (!TextUtils.isEmpty(hwAddr)) { + cfgParcel.hwAddr = hwAddr; + } else { + cfgParcel.hwAddr = ""; + } + cfgParcel.ipv4Addr = cfg.getLinkAddress().getAddress().getHostAddress(); + cfgParcel.prefixLength = cfg.getLinkAddress().getPrefixLength(); + ArrayList<String> flags = new ArrayList<>(); + for (String flag : cfg.getFlags()) { + flags.add(flag); + } + cfgParcel.flags = flags.toArray(new String[0]); + + return cfgParcel; + } + + /** + * Construct InterfaceConfiguration from InterfaceConfigurationParcel. + */ + public static InterfaceConfiguration fromStableParcel(InterfaceConfigurationParcel p) { + InterfaceConfiguration cfg = new InterfaceConfiguration(); + cfg.setHardwareAddress(p.hwAddr); + + final InetAddress addr = NetworkUtils.numericToInetAddress(p.ipv4Addr); + cfg.setLinkAddress(new LinkAddress(addr, p.prefixLength)); + for (String flag : p.flags) { + cfg.setFlag(flag); + } + + return cfg; + } + @Override public InterfaceConfiguration getInterfaceConfig(String iface) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); @@ -1039,7 +1081,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub } try { - final InterfaceConfiguration cfg = InterfaceConfiguration.fromParcel(result); + final InterfaceConfiguration cfg = fromStableParcel(result); return cfg; } catch (IllegalArgumentException iae) { throw new IllegalStateException("Invalid InterfaceConfigurationParcel", iae); @@ -1054,7 +1096,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub throw new IllegalStateException("Null LinkAddress given"); } - final InterfaceConfigurationParcel cfgParcel = cfg.toParcel(iface); + final InterfaceConfigurationParcel cfgParcel = toStableParcel(cfg, iface); try { mNetdService.interfaceSetCfg(cfgParcel); @@ -1718,12 +1760,27 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } + private static UidRangeParcel makeUidRangeParcel(int start, int stop) { + UidRangeParcel range = new UidRangeParcel(); + range.start = start; + range.stop = stop; + return range; + } + + private static UidRangeParcel[] toStableParcels(UidRange[] ranges) { + UidRangeParcel[] stableRanges = new UidRangeParcel[ranges.length]; + for (int i = 0; i < ranges.length; i++) { + stableRanges[i] = makeUidRangeParcel(ranges[i].start, ranges[i].stop); + } + return stableRanges; + } + @Override public void setAllowOnlyVpnForUids(boolean add, UidRange[] uidRanges) throws ServiceSpecificException { mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG); try { - mNetdService.networkRejectNonSecureVpn(add, uidRanges); + mNetdService.networkRejectNonSecureVpn(add, toStableParcels(uidRanges)); } catch (ServiceSpecificException e) { Log.w(TAG, "setAllowOnlyVpnForUids(" + add + ", " + Arrays.toString(uidRanges) + ")" + ": netd command failed", e); @@ -1892,7 +1949,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - mNetdService.networkAddUidRanges(netId, ranges); + mNetdService.networkAddUidRanges(netId, toStableParcels(ranges)); } catch (RemoteException | ServiceSpecificException e) { throw new IllegalStateException(e); } @@ -1902,7 +1959,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub public void removeVpnUidRanges(int netId, UidRange[] ranges) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - mNetdService.networkRemoveUidRanges(netId, ranges); + mNetdService.networkRemoveUidRanges(netId, toStableParcels(ranges)); } catch (RemoteException | ServiceSpecificException e) { throw new IllegalStateException(e); } @@ -1940,7 +1997,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub private void closeSocketsForFirewallChainLocked(int chain, String chainName) { // UID ranges to close sockets on. - UidRange[] ranges; + UidRangeParcel[] ranges; // UID ranges whose sockets we won't touch. int[] exemptUids; @@ -1948,10 +2005,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub if (DBG) Slog.d(TAG, "Closing sockets after enabling chain " + chainName); if (getFirewallType(chain) == FIREWALL_WHITELIST) { // Close all sockets on all non-system UIDs... - ranges = new UidRange[] { + ranges = new UidRangeParcel[] { // TODO: is there a better way of finding all existing users? If so, we could // specify their ranges here. - new UidRange(Process.FIRST_APPLICATION_UID, Integer.MAX_VALUE), + makeUidRangeParcel(Process.FIRST_APPLICATION_UID, Integer.MAX_VALUE), }; // ... except for the UIDs that have allow rules. synchronized (mRulesLock) { @@ -1978,11 +2035,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub // Close sockets for every UID that has a deny rule... synchronized (mRulesLock) { final SparseIntArray rules = getUidFirewallRulesLR(chain); - ranges = new UidRange[rules.size()]; + ranges = new UidRangeParcel[rules.size()]; for (int i = 0; i < ranges.length; i++) { if (rules.valueAt(i) == FIREWALL_RULE_DENY) { int uid = rules.keyAt(i); - ranges[numUids] = new UidRange(uid, uid); + ranges[numUids] = makeUidRangeParcel(uid, uid); numUids++; } } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index f4161103b699..19b0bf795393 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -3741,6 +3741,7 @@ class StorageManagerService extends IStorageManager.Stub case "com.facebook.katana": // b/123996076 case "jp.naver.line.android": // b/124767356 case "com.mxtech.videoplayer.ad": // b/124531483 + case "com.whatsapp": // b/124766614 return Zygote.MOUNT_EXTERNAL_LEGACY; } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 627010606b31..e357ce8ce929 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1600,6 +1600,11 @@ public final class ActiveServices { "BIND_TREAT_LIKE_ACTIVITY"); } + if ((flags & Context.BIND_SCHEDULE_LIKE_TOP_APP) != 0 && !isCallerSystem) { + throw new SecurityException("Non-system caller (pid=" + Binder.getCallingPid() + + ") set BIND_SCHEDULE_LIKE_TOP_APP when binding service " + service); + } + if ((flags & Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0 && !isCallerSystem) { throw new SecurityException( "Non-system caller " + caller + " (pid=" + Binder.getCallingPid() diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7c6049cd0c90..82254761817e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -5492,6 +5492,12 @@ public class ActivityManagerService extends IActivityManager.Stub } } + private boolean isAppBad(ApplicationInfo info) { + synchronized (this) { + return mAppErrors.isBadProcessLocked(info); + } + } + // NOTE: this is an internal method used by the OnShellCommand implementation only and should // be guarded by permission checking. int getUidState(int uid) { @@ -18078,6 +18084,11 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public boolean isAppBad(ApplicationInfo info) { + return ActivityManagerService.this.isAppBad(info); + } + + @Override public void clearPendingBackup(int userId) { ActivityManagerService.this.clearPendingBackup(userId); } diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java index af1031e5b887..a1c941e085a6 100644 --- a/services/core/java/com/android/server/am/ConnectionRecord.java +++ b/services/core/java/com/android/server/am/ConnectionRecord.java @@ -192,6 +192,9 @@ final class ConnectionRecord { if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) { sb.append("LACT "); } + if ((flags & Context.BIND_SCHEDULE_LIKE_TOP_APP) != 0) { + sb.append("SLTA "); + } if ((flags&Context.BIND_VISIBLE) != 0) { sb.append("VIS "); } diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 4e03b72e6ce6..9056f76c4086 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1294,6 +1294,11 @@ public final class OomAdjuster { ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND; } } + if (schedGroup < ProcessList.SCHED_GROUP_TOP_APP + && (cr.flags & Context.BIND_SCHEDULE_LIKE_TOP_APP) != 0) { + schedGroup = ProcessList.SCHED_GROUP_TOP_APP; + } + if (!trackedProcState) { cr.trackProcState(clientProcState, mAdjSeq, now); } diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java index 7da848c12e83..4e0380d37820 100644 --- a/services/core/java/com/android/server/attention/AttentionManagerService.java +++ b/services/core/java/com/android/server/attention/AttentionManagerService.java @@ -59,6 +59,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.server.SystemService; +import java.io.FileDescriptor; import java.io.PrintWriter; /** @@ -103,6 +104,7 @@ public class AttentionManagerService extends SystemService { @Override public void onStart() { + publishBinderService(Context.ATTENTION_SERVICE, new BinderService()); publishLocalService(AttentionManagerInternal.class, new LocalService()); } @@ -329,17 +331,15 @@ public class AttentionManagerService extends SystemService { return null; } - private void dumpInternal(PrintWriter pw) { - if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return; - IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); - ipw.println("Attention Manager Service (dumpsys attention)\n"); + private void dumpInternal(IndentingPrintWriter ipw) { + ipw.println("Attention Manager Service (dumpsys attention) state:\n"); ipw.printPair("context", mContext); - pw.println(); + ipw.println(); synchronized (mLock) { int size = mUserStates.size(); ipw.print("Number user states: "); - pw.println(size); + ipw.println(size); if (size > 0) { ipw.increaseIndent(); for (int i = 0; i < size; i++) { @@ -591,4 +591,15 @@ public class AttentionManagerService extends SystemService { } } } + + private final class BinderService extends Binder { + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) { + return; + } + + dumpInternal(new IndentingPrintWriter(pw, " ")); + } + } } diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 69a9e7e842eb..b7746477f0f8 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -170,8 +170,15 @@ import java.util.ArrayList; } } - /*package*/ void setSpeakerphoneOn(boolean on, String eventSource) { + /** + * Turns speakerphone on/off + * @param on + * @param eventSource for logging purposes + * @return true if speakerphone state changed + */ + /*package*/ boolean setSpeakerphoneOn(boolean on, String eventSource) { synchronized (mDeviceStateLock) { + final boolean wasOn = isSpeakerphoneOn(); if (on) { if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) { setForceUse_Async(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource); @@ -183,6 +190,7 @@ import java.util.ArrayList; mForcedUseForCommExt = mForcedUseForComm; setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource); + return (wasOn != isSpeakerphoneOn()); } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 68f76abe829a..a14a638395db 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -3331,7 +3331,11 @@ public class AudioService extends IAudioService.Stub final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on) .append(") from u/pid:").append(Binder.getCallingUid()).append("/") .append(Binder.getCallingPid()).toString(); - mDeviceBroker.setSpeakerphoneOn(on, eventSource); + final boolean stateChanged = mDeviceBroker.setSpeakerphoneOn(on, eventSource); + if (stateChanged) { + mContext.sendBroadcast(new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED) + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)); + } } /** @see AudioManager#isSpeakerphoneOn() */ diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index ddd416e14164..f313e1d48c53 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -57,6 +57,7 @@ import android.os.DeadObjectException; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; @@ -68,6 +69,7 @@ import android.util.Slog; import android.util.StatsLog; import com.android.internal.R; +import com.android.internal.os.SomeArgs; import com.android.internal.statusbar.IStatusBarService; import com.android.server.SystemService; @@ -85,22 +87,137 @@ public class BiometricService extends SystemService { private static final String TAG = "BiometricService"; + private static final int MSG_ON_TASK_STACK_CHANGED = 1; + private static final int MSG_ON_AUTHENTICATION_SUCCEEDED = 2; + private static final int MSG_ON_AUTHENTICATION_FAILED = 3; + private static final int MSG_ON_ERROR = 4; + private static final int MSG_ON_ACQUIRED = 5; + private static final int MSG_ON_DISMISSED = 6; + private static final int MSG_ON_TRY_AGAIN_PRESSED = 7; + private static final int MSG_ON_READY_FOR_AUTHENTICATION = 8; + private static final int MSG_AUTHENTICATE = 9; + private static final int MSG_CANCEL_AUTHENTICATION = 10; + private static final int[] FEATURE_ID = { TYPE_FINGERPRINT, TYPE_IRIS, TYPE_FACE }; + /** + * Authentication either just called and we have not transitioned to the CALLED state, or + * authentication terminated (success or error). + */ + private static final int STATE_AUTH_IDLE = 0; + /** + * Authentication was called and we are waiting for the <Biometric>Services to return their + * cookies before starting the hardware and showing the BiometricPrompt. + */ + private static final int STATE_AUTH_CALLED = 1; + /** + * Authentication started, BiometricPrompt is showing and the hardware is authenticating. + */ + private static final int STATE_AUTH_STARTED = 2; + /** + * Authentication is paused, waiting for the user to press "try again" button. Only + * passive modalities such as Face or Iris should have this state. Note that for passive + * modalities, the HAL enters the idle state after onAuthenticated(false) which differs from + * fingerprint. + */ + private static final int STATE_AUTH_PAUSED = 3; + /** + * Authentication is successful, but we're waiting for the user to press "confirm" button. + */ + private static final int STATE_AUTH_PENDING_CONFIRM = 5; + + private final class AuthSession { + // Map of Authenticator/Cookie pairs. We expect to receive the cookies back from + // <Biometric>Services before we can start authenticating. Pairs that have been returned + // are moved to mModalitiesMatched. + final HashMap<Integer, Integer> mModalitiesWaiting; + // Pairs that have been matched. + final HashMap<Integer, Integer> mModalitiesMatched = new HashMap<>(); + + // The following variables are passed to authenticateInternal, which initiates the + // appropriate <Biometric>Services. + final IBinder mToken; + final long mSessionId; + final int mUserId; + // Original receiver from BiometricPrompt. + final IBiometricServiceReceiver mClientReceiver; + final String mOpPackageName; + // Info to be shown on BiometricDialog when all cookies are returned. + final Bundle mBundle; + final int mCallingUid; + final int mCallingPid; + final int mCallingUserId; + // Continue authentication with the same modality/modalities after "try again" is + // pressed + final int mModality; + final boolean mRequireConfirmation; + + // The current state, which can be either idle, called, or started + private int mState = STATE_AUTH_IDLE; + // For explicit confirmation, do not send to keystore until the user has confirmed + // the authentication. + byte[] mTokenEscrow; + + // Timestamp when hardware authentication occurred + private long mAuthenticatedTimeMs; + + AuthSession(HashMap<Integer, Integer> modalities, IBinder token, long sessionId, + int userId, IBiometricServiceReceiver receiver, String opPackageName, + Bundle bundle, int callingUid, int callingPid, int callingUserId, + int modality, boolean requireConfirmation) { + mModalitiesWaiting = modalities; + mToken = token; + mSessionId = sessionId; + mUserId = userId; + mClientReceiver = receiver; + mOpPackageName = opPackageName; + mBundle = bundle; + mCallingUid = callingUid; + mCallingPid = callingPid; + mCallingUserId = callingUserId; + mModality = modality; + mRequireConfirmation = requireConfirmation; + } + + boolean isCrypto() { + return mSessionId != 0; + } + + boolean containsCookie(int cookie) { + if (mModalitiesWaiting != null && mModalitiesWaiting.containsValue(cookie)) { + return true; + } + if (mModalitiesMatched != null && mModalitiesMatched.containsValue(cookie)) { + return true; + } + return false; + } + } + + private final class BiometricTaskStackListener extends TaskStackListener { + @Override + public void onTaskStackChanged() { + mHandler.sendEmptyMessage(MSG_ON_TASK_STACK_CHANGED); + } + } + private final AppOpsManager mAppOps; - private final Handler mHandler; private final boolean mHasFeatureFingerprint; private final boolean mHasFeatureIris; private final boolean mHasFeatureFace; private final SettingObserver mSettingObserver; private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks; + private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener(); + private final Random mRandom = new Random(); private IFingerprintService mFingerprintService; private IFaceService mFaceService; + private IActivityTaskManager mActivityTaskManager; + private IStatusBarService mStatusBarService; // Get and cache the available authenticator (manager) classes. Used since aidl doesn't support // polymorphism :/ @@ -113,6 +230,108 @@ public class BiometricService extends SystemService { // should be safe. private int mCurrentModality; + // 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; + private AuthSession mPendingAuthSession; + + private final Handler mHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_ON_TASK_STACK_CHANGED: { + handleTaskStackChanged(); + break; + } + + case MSG_ON_AUTHENTICATION_SUCCEEDED: { + SomeArgs args = (SomeArgs) msg.obj; + handleAuthenticationSucceeded( + (boolean) args.arg1 /* requireConfirmation */, + (byte[]) args.arg2 /* token */); + args.recycle(); + break; + } + + case MSG_ON_AUTHENTICATION_FAILED: { + SomeArgs args = (SomeArgs) msg.obj; + handleAuthenticationFailed( + args.argi1 /* cookie */, + (boolean) args.arg1 /* requireConfirmation */); + args.recycle(); + break; + } + + case MSG_ON_ERROR: { + SomeArgs args = (SomeArgs) msg.obj; + handleOnError( + args.argi1 /* cookie */, + args.argi2 /* error */, + (String) args.arg1 /* message */); + args.recycle(); + break; + } + + case MSG_ON_ACQUIRED: { + SomeArgs args = (SomeArgs) msg.obj; + handleOnAcquired( + args.argi1 /* acquiredInfo */, + (String) args.arg1 /* message */); + args.recycle(); + break; + } + + case MSG_ON_DISMISSED: { + handleOnDismissed(msg.arg1); + break; + } + + case MSG_ON_TRY_AGAIN_PRESSED: { + handleOnTryAgainPressed(); + break; + } + + case MSG_ON_READY_FOR_AUTHENTICATION: { + SomeArgs args = (SomeArgs) msg.obj; + handleOnReadyForAuthentication( + args.argi1 /* cookie */, + (boolean) args.arg1 /* requireConfirmation */, + args.argi2 /* userId */); + args.recycle(); + break; + } + + case MSG_AUTHENTICATE: { + SomeArgs args = (SomeArgs) msg.obj; + handleAuthenticate( + (IBinder) args.arg1 /* token */, + (long) args.arg2 /* sessionId */, + args.argi1 /* userid */, + (IBiometricServiceReceiver) args.arg3 /* receiver */, + (String) args.arg4 /* opPackageName */, + (Bundle) args.arg5 /* bundle */, + args.argi2 /* callingUid */, + args.argi3 /* callingPid */, + args.argi4 /* callingUserId */); + args.recycle(); + break; + } + + case MSG_CANCEL_AUTHENTICATION: { + SomeArgs args = (SomeArgs) msg.obj; + handleCancelAuthentication( + (IBinder) args.arg1 /* token */, + (String) args.arg2 /* opPackageName */); + args.recycle(); + break; + } + + default: + break; + } + } + }; + private final class Authenticator { int mType; BiometricAuthenticator mAuthenticator; @@ -251,142 +470,62 @@ public class BiometricService extends SystemService { } } - /** - * This is just a pass-through service that wraps Fingerprint, Iris, Face services. This service - * should not carry any state. The reality is we need to keep a tiny amount of state so that - * cancelAuthentication() can go to the right place. - */ - private final class BiometricServiceWrapper extends IBiometricService.Stub { + // Wrap the client's receiver so we can do things with the BiometricDialog first + private final IBiometricServiceReceiverInternal mInternalReceiver = + new IBiometricServiceReceiverInternal.Stub() { + @Override + public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token) + throws RemoteException { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = requireConfirmation; + args.arg2 = token; + mHandler.obtainMessage(MSG_ON_AUTHENTICATION_SUCCEEDED, args).sendToTarget(); + } - /** - * Authentication either just called and we have not transitioned to the CALLED state, or - * authentication terminated (success or error). - */ - private static final int STATE_AUTH_IDLE = 0; - /** - * Authentication was called and we are waiting for the <Biometric>Services to return their - * cookies before starting the hardware and showing the BiometricPrompt. - */ - private static final int STATE_AUTH_CALLED = 1; - /** - * Authentication started, BiometricPrompt is showing and the hardware is authenticating. - */ - private static final int STATE_AUTH_STARTED = 2; - /** - * Authentication is paused, waiting for the user to press "try again" button. Only - * passive modalities such as Face or Iris should have this state. Note that for passive - * modalities, the HAL enters the idle state after onAuthenticated(false) which differs from - * fingerprint. - */ - private static final int STATE_AUTH_PAUSED = 3; - /** - * Authentication is successful, but we're waiting for the user to press "confirm" button. - */ - private static final int STATE_AUTH_PENDING_CONFIRM = 5; - - final class AuthSession { - // Map of Authenticator/Cookie pairs. We expect to receive the cookies back from - // <Biometric>Services before we can start authenticating. Pairs that have been returned - // are moved to mModalitiesMatched. - final HashMap<Integer, Integer> mModalitiesWaiting; - // Pairs that have been matched. - final HashMap<Integer, Integer> mModalitiesMatched = new HashMap<>(); - - // The following variables are passed to authenticateInternal, which initiates the - // appropriate <Biometric>Services. - final IBinder mToken; - final long mSessionId; - final int mUserId; - // Original receiver from BiometricPrompt. - final IBiometricServiceReceiver mClientReceiver; - final String mOpPackageName; - // Info to be shown on BiometricDialog when all cookies are returned. - final Bundle mBundle; - final int mCallingUid; - final int mCallingPid; - final int mCallingUserId; - // Continue authentication with the same modality/modalities after "try again" is - // pressed - final int mModality; - final boolean mRequireConfirmation; - - // The current state, which can be either idle, called, or started - private int mState = STATE_AUTH_IDLE; - // For explicit confirmation, do not send to keystore until the user has confirmed - // the authentication. - byte[] mTokenEscrow; - - // Timestamp when hardware authentication occurred - private long mAuthenticatedTimeMs; - - AuthSession(HashMap<Integer, Integer> modalities, IBinder token, long sessionId, - int userId, IBiometricServiceReceiver receiver, String opPackageName, - Bundle bundle, int callingUid, int callingPid, int callingUserId, - int modality, boolean requireConfirmation) { - mModalitiesWaiting = modalities; - mToken = token; - mSessionId = sessionId; - mUserId = userId; - mClientReceiver = receiver; - mOpPackageName = opPackageName; - mBundle = bundle; - mCallingUid = callingUid; - mCallingPid = callingPid; - mCallingUserId = callingUserId; - mModality = modality; - mRequireConfirmation = requireConfirmation; - } + @Override + public void onAuthenticationFailed(int cookie, boolean requireConfirmation) + throws RemoteException { + SomeArgs args = SomeArgs.obtain(); + args.argi1 = cookie; + args.arg1 = requireConfirmation; + mHandler.obtainMessage(MSG_ON_AUTHENTICATION_FAILED, args).sendToTarget(); + } - boolean isCrypto() { - return mSessionId != 0; - } + @Override + public void onError(int cookie, int error, String message) throws RemoteException { + SomeArgs args = SomeArgs.obtain(); + args.argi1 = cookie; + args.argi2 = error; + args.arg1 = message; + mHandler.obtainMessage(MSG_ON_ERROR, args).sendToTarget(); + } - boolean containsCookie(int cookie) { - if (mModalitiesWaiting != null && mModalitiesWaiting.containsValue(cookie)) { - return true; - } - if (mModalitiesMatched != null && mModalitiesMatched.containsValue(cookie)) { - return true; - } - return false; - } + @Override + public void onAcquired(int acquiredInfo, String message) throws RemoteException { + SomeArgs args = SomeArgs.obtain(); + args.argi1 = acquiredInfo; + args.arg1 = message; + mHandler.obtainMessage(MSG_ON_ACQUIRED, args).sendToTarget(); } - final class BiometricTaskStackListener extends TaskStackListener { - @Override - public void onTaskStackChanged() { - try { - final List<ActivityManager.RunningTaskInfo> runningTasks = - mActivityTaskManager.getTasks(1); - if (!runningTasks.isEmpty()) { - final String topPackage = runningTasks.get(0).topActivity.getPackageName(); - if (mCurrentAuthSession != null - && !topPackage.contentEquals(mCurrentAuthSession.mOpPackageName)) { - mStatusBarService.hideBiometricDialog(); - mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); - mCurrentAuthSession.mClientReceiver.onError( - BiometricConstants.BIOMETRIC_ERROR_CANCELED, - getContext().getString( - com.android.internal.R.string.biometric_error_canceled) - ); - mCurrentAuthSession.mState = STATE_AUTH_IDLE; - mCurrentAuthSession = null; - } - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to get running tasks", e); - } - } + @Override + public void onDialogDismissed(int reason) throws RemoteException { + mHandler.obtainMessage(MSG_ON_DISMISSED, reason, 0 /* arg2 */).sendToTarget(); } - private final IActivityTaskManager mActivityTaskManager = getContext().getSystemService( - ActivityTaskManager.class).getService(); - private final IStatusBarService mStatusBarService = IStatusBarService.Stub.asInterface( - ServiceManager.getService(Context.STATUS_BAR_SERVICE)); - private final BiometricTaskStackListener mTaskStackListener = - new BiometricTaskStackListener(); - private final Random mRandom = new Random(); + @Override + public void onTryAgainPressed() { + mHandler.sendEmptyMessage(MSG_ON_TRY_AGAIN_PRESSED); + } + }; + + /** + * This is just a pass-through service that wraps Fingerprint, Iris, Face services. This service + * should not carry any state. The reality is we need to keep a tiny amount of state so that + * cancelAuthentication() can go to the right place. + */ + private final class BiometricServiceWrapper extends IBiometricService.Stub { // TODO(b/123378871): Remove when moved. // When BiometricPrompt#setAllowDeviceCredentials is set to true, we need to store the // client (app) receiver. BiometricService internally launches CDCA which invokes @@ -395,331 +534,15 @@ public class BiometricService extends SystemService { // 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; - private AuthSession mPendingAuthSession; - - // Wrap the client's receiver so we can do things with the BiometricDialog first - private final IBiometricServiceReceiverInternal mInternalReceiver = - new IBiometricServiceReceiverInternal.Stub() { - @Override - public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token) - throws RemoteException { - try { - // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded - // after user dismissed/canceled dialog). - if (mCurrentAuthSession == null) { - Slog.e(TAG, "onAuthenticationSucceeded(): Auth session is null"); - return; - } - - if (!requireConfirmation) { - mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); - KeyStore.getInstance().addAuthToken(token); - mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded(); - mCurrentAuthSession.mState = STATE_AUTH_IDLE; - mCurrentAuthSession = null; - } else { - mCurrentAuthSession.mAuthenticatedTimeMs = System.currentTimeMillis(); - // Store the auth token and submit it to keystore after the confirmation - // button has been pressed. - mCurrentAuthSession.mTokenEscrow = token; - mCurrentAuthSession.mState = STATE_AUTH_PENDING_CONFIRM; - } - - // Notify SysUI that the biometric has been authenticated. SysUI already knows - // the implicit/explicit state and will react accordingly. - mStatusBarService.onBiometricAuthenticated(true); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception", e); - } - } - - @Override - public void onAuthenticationFailed(int cookie, boolean requireConfirmation) - throws RemoteException { - try { - // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded - // after user dismissed/canceled dialog). - if (mCurrentAuthSession == null) { - Slog.e(TAG, "onAuthenticationFailed(): Auth session is null"); - return; - } - - mStatusBarService.onBiometricAuthenticated(false); - - // TODO: This logic will need to be updated if BP is multi-modal - if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) { - // Pause authentication. onBiometricAuthenticated(false) causes the - // dialog to show a "try again" button for passive modalities. - mCurrentAuthSession.mState = STATE_AUTH_PAUSED; - } - - mCurrentAuthSession.mClientReceiver.onAuthenticationFailed(); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception", e); - } - } - - @Override - public void onError(int cookie, int error, String message) throws RemoteException { - Slog.d(TAG, "Error: " + error + " cookie: " + cookie); - // Errors can either be from the current auth session or the pending auth session. - // The pending auth session may receive errors such as ERROR_LOCKOUT before - // it becomes the current auth session. Similarly, the current auth session may - // receive errors such as ERROR_CANCELED while the pending auth session is preparing - // to be started. Thus we must match error messages with their cookies to be sure - // of their intended receivers. - try { - if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) { - if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) { - mStatusBarService.onBiometricError(message); - if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) { - mActivityTaskManager.unregisterTaskStackListener( - mTaskStackListener); - mCurrentAuthSession.mClientReceiver.onError(error, message); - mCurrentAuthSession.mState = STATE_AUTH_IDLE; - mCurrentAuthSession = null; - mStatusBarService.hideBiometricDialog(); - } else { - // Send errors after the dialog is dismissed. - mHandler.postDelayed(() -> { - try { - if (mCurrentAuthSession != null) { - mActivityTaskManager.unregisterTaskStackListener( - mTaskStackListener); - mCurrentAuthSession.mClientReceiver.onError(error, - message); - mCurrentAuthSession.mState = STATE_AUTH_IDLE; - mCurrentAuthSession = null; - } - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception", e); - } - }, BiometricPrompt.HIDE_DIALOG_DELAY); - } - } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) { - // In the "try again" state, we should forward canceled errors to - // the client and and clean up. - mCurrentAuthSession.mClientReceiver.onError(error, message); - mStatusBarService.onBiometricError(message); - mActivityTaskManager.unregisterTaskStackListener( - mTaskStackListener); - mCurrentAuthSession.mState = STATE_AUTH_IDLE; - mCurrentAuthSession = null; - } else { - Slog.e(TAG, "Impossible session error state: " - + mCurrentAuthSession.mState); - } - } else if (mPendingAuthSession != null - && mPendingAuthSession.containsCookie(cookie)) { - if (mPendingAuthSession.mState == STATE_AUTH_CALLED) { - mPendingAuthSession.mClientReceiver.onError(error, message); - mPendingAuthSession.mState = STATE_AUTH_IDLE; - mPendingAuthSession = null; - } else { - Slog.e(TAG, "Impossible pending session error state: " - + mPendingAuthSession.mState); - } - } - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception", e); - } - } - - @Override - public void onAcquired(int acquiredInfo, String message) throws RemoteException { - // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded - // after user dismissed/canceled dialog). - if (mCurrentAuthSession == null) { - Slog.e(TAG, "onAcquired(): Auth session is null"); - return; - } - - if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) { - try { - mStatusBarService.onBiometricHelp(message); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception", e); - } - } - } - - @Override - public void onDialogDismissed(int reason) throws RemoteException { - if (mCurrentAuthSession == null) { - Slog.e(TAG, "onDialogDismissed: " + reason + ", auth session null"); - return; - } - - logDialogDismissed(reason); - - if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) { - // Positive button is used by passive modalities as a "confirm" button, - // do not send to client - mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason); - // Cancel authentication. Skip the token/package check since we are cancelling - // from system server. The interface is permission protected so this is fine. - cancelInternal(null /* token */, null /* package */, false /* fromClient */); - } - if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) { - mCurrentAuthSession.mClientReceiver.onError( - BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED, - getContext().getString( - com.android.internal.R.string.biometric_error_user_canceled)); - } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) { - // Have the service send the token to KeyStore, and send onAuthenticated - // to the application - KeyStore.getInstance().addAuthToken(mCurrentAuthSession.mTokenEscrow); - mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded(); - } - mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); - mCurrentAuthSession.mState = STATE_AUTH_IDLE; - mCurrentAuthSession = null; - } - - @Override - public void onTryAgainPressed() { - Slog.d(TAG, "onTryAgainPressed"); - // No need to check permission, since it can only be invoked by SystemUI - // (or system server itself). - mHandler.post(() -> { - authenticateInternal(mCurrentAuthSession.mToken, - mCurrentAuthSession.mSessionId, - mCurrentAuthSession.mUserId, - mCurrentAuthSession.mClientReceiver, - mCurrentAuthSession.mOpPackageName, - mCurrentAuthSession.mBundle, - mCurrentAuthSession.mCallingUid, - mCurrentAuthSession.mCallingPid, - mCurrentAuthSession.mCallingUserId, - mCurrentAuthSession.mModality); - }); - } - - private void logDialogDismissed(int reason) { - if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) { - // Explicit auth, authentication confirmed. - // Latency in this case is authenticated -> confirmed. <Biometric>Service - // should have the first half (first acquired -> authenticated). - final long latency = System.currentTimeMillis() - - mCurrentAuthSession.mAuthenticatedTimeMs; - - if (LoggableMonitor.DEBUG) { - Slog.v(LoggableMonitor.TAG, "Confirmed! Modality: " + statsModality() - + ", User: " + mCurrentAuthSession.mUserId - + ", IsCrypto: " + mCurrentAuthSession.isCrypto() - + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT - + ", RequireConfirmation: " - + mCurrentAuthSession.mRequireConfirmation - + ", State: " + StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED - + ", Latency: " + latency); - } - - StatsLog.write(StatsLog.BIOMETRIC_AUTHENTICATED, - statsModality(), - mCurrentAuthSession.mUserId, - mCurrentAuthSession.isCrypto(), - BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, - mCurrentAuthSession.mRequireConfirmation, - StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED, - latency); - } else { - int error = reason == BiometricPrompt.DISMISSED_REASON_NEGATIVE - ? BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON - : reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL - ? BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED - : 0; - if (LoggableMonitor.DEBUG) { - Slog.v(LoggableMonitor.TAG, "Dismissed! Modality: " + statsModality() - + ", User: " + mCurrentAuthSession.mUserId - + ", IsCrypto: " + mCurrentAuthSession.isCrypto() - + ", Action: " + BiometricsProtoEnums.ACTION_AUTHENTICATE - + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT - + ", Error: " + error); - } - // Auth canceled - StatsLog.write(StatsLog.BIOMETRIC_ERROR_OCCURRED, - statsModality(), - mCurrentAuthSession.mUserId, - mCurrentAuthSession.isCrypto(), - BiometricsProtoEnums.ACTION_AUTHENTICATE, - BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, - error, - 0 /* vendorCode */); - } - } - - private int statsModality() { - int modality = 0; - if (mCurrentAuthSession == null) { - return BiometricsProtoEnums.MODALITY_UNKNOWN; - } - if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FINGERPRINT) - != 0) { - modality |= BiometricsProtoEnums.MODALITY_FINGERPRINT; - } - if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_IRIS) != 0) { - modality |= BiometricsProtoEnums.MODALITY_IRIS; - } - if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FACE) != 0) { - modality |= BiometricsProtoEnums.MODALITY_FACE; - } - return modality; - } - }; - @Override // Binder call public void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId) { checkInternalPermission(); - Iterator it = mPendingAuthSession.mModalitiesWaiting.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry<Integer, Integer> pair = (Map.Entry) it.next(); - if (pair.getValue() == cookie) { - mPendingAuthSession.mModalitiesMatched.put(pair.getKey(), pair.getValue()); - mPendingAuthSession.mModalitiesWaiting.remove(pair.getKey()); - Slog.d(TAG, "Matched cookie: " + cookie + ", " - + mPendingAuthSession.mModalitiesWaiting.size() + " remaining"); - break; - } - } - - if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) { - final boolean continuing = mCurrentAuthSession != null && - (mCurrentAuthSession.mState == STATE_AUTH_PAUSED); - - mCurrentAuthSession = mPendingAuthSession; - mPendingAuthSession = null; - - mCurrentAuthSession.mState = STATE_AUTH_STARTED; - try { - int modality = TYPE_NONE; - it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry<Integer, Integer> pair = (Map.Entry) it.next(); - if (pair.getKey() == TYPE_FINGERPRINT) { - mFingerprintService.startPreparedClient(pair.getValue()); - } else if (pair.getKey() == TYPE_IRIS) { - Slog.e(TAG, "Iris unsupported"); - } else if (pair.getKey() == TYPE_FACE) { - mFaceService.startPreparedClient(pair.getValue()); - } else { - Slog.e(TAG, "Unknown modality: " + pair.getKey()); - } - modality |= pair.getKey(); - } - - if (!continuing) { - mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle, - mInternalReceiver, modality, requireConfirmation, userId); - mActivityTaskManager.registerTaskStackListener(mTaskStackListener); - } - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception", e); - } - } + SomeArgs args = SomeArgs.obtain(); + args.argi1 = cookie; + args.arg1 = requireConfirmation; + args.argi2 = userId; + mHandler.obtainMessage(MSG_ON_READY_FOR_AUTHENTICATION, args).sendToTarget(); } @Override // Binder call @@ -754,25 +577,23 @@ public class BiometricService extends SystemService { checkInternalPermission(); // Set the default title if necessary try { - if (useDefaultTitle) { - final List<ActivityManager.RunningAppProcessInfo> procs = - ActivityManager.getService().getRunningAppProcesses(); - for (int i = 0; i < procs.size(); i++) { - final ActivityManager.RunningAppProcessInfo info = procs.get(i); - if (info.uid == callingUid - && info.importance == IMPORTANCE_FOREGROUND) { - PackageManager pm = getContext().getPackageManager(); - final CharSequence label = pm.getApplicationLabel( - pm.getApplicationInfo(info.processName, - PackageManager.GET_META_DATA)); - final String title = getContext() - .getString(R.string.biometric_dialog_default_title, label); - if (TextUtils.isEmpty( - bundle.getCharSequence(BiometricPrompt.KEY_TITLE))) { - bundle.putCharSequence(BiometricPrompt.KEY_TITLE, title); - } - break; + final List<ActivityManager.RunningAppProcessInfo> procs = + ActivityManager.getService().getRunningAppProcesses(); + for (int i = 0; i < procs.size(); i++) { + final ActivityManager.RunningAppProcessInfo info = procs.get(i); + if (info.uid == callingUid + && info.importance == IMPORTANCE_FOREGROUND) { + PackageManager pm = getContext().getPackageManager(); + final CharSequence label = pm.getApplicationLabel( + pm.getApplicationInfo(info.processName, + PackageManager.GET_META_DATA)); + final String title = getContext() + .getString(R.string.biometric_dialog_default_title, label); + if (TextUtils.isEmpty( + bundle.getCharSequence(BiometricPrompt.KEY_TITLE))) { + bundle.putCharSequence(BiometricPrompt.KEY_TITLE, title); } + break; } } } catch (RemoteException e) { @@ -792,7 +613,8 @@ public class BiometricService extends SystemService { KeyguardManager.class); if (!kgm.isDeviceSecure()) { try { - receiver.onError(BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL, + receiver.onError( + BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL, getContext().getString( R.string.biometric_error_device_not_secured)); } catch (RemoteException e) { @@ -811,160 +633,63 @@ public class BiometricService extends SystemService { return; } - mHandler.post(() -> { - final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId); - final int modality = result.first; - final int error = result.second; - - // Check for errors, notify callback, and return - if (error != BiometricConstants.BIOMETRIC_SUCCESS) { - try { - final String hardwareUnavailable = - getContext().getString(R.string.biometric_error_hw_unavailable); - switch (error) { - case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT: - receiver.onError(error, hardwareUnavailable); - break; - case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE: - receiver.onError(error, hardwareUnavailable); - break; - case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS: - receiver.onError(error, - getErrorString(modality, error, 0 /* vendorCode */)); - break; - default: - Slog.e(TAG, "Unhandled error"); - break; - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to send error", e); - } - return; - } - - mCurrentModality = modality; - - // Start preparing for authentication. Authentication starts when - // all modalities requested have invoked onReadyForAuthentication. - authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle, - callingUid, callingPid, callingUserId, modality); - }); + SomeArgs args = SomeArgs.obtain(); + args.arg1 = token; + args.arg2 = sessionId; + args.argi1 = userId; + args.arg3 = receiver; + args.arg4 = opPackageName; + args.arg5 = bundle; + args.argi2 = callingUid; + args.argi3 = callingPid; + args.argi4 = callingUserId; + + mHandler.obtainMessage(MSG_AUTHENTICATE, args).sendToTarget(); } @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; + mHandler.post(() -> { + 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 - * used for: - * 1) Preparing <Biometric>Services for authentication when BiometricPrompt#authenticate is, - * invoked, shortly after which BiometricPrompt is shown and authentication starts - * 2) Preparing <Biometric>Services for authentication when BiometricPrompt is already shown - * and the user has pressed "try again" - */ - private void authenticateInternal(IBinder token, long sessionId, int userId, - IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle, - int callingUid, int callingPid, int callingUserId, int modality) { - try { - boolean requireConfirmation = bundle.getBoolean( - BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */); - if ((modality & TYPE_FACE) != 0) { - // Check if the user has forced confirmation to be required in Settings. - requireConfirmation = requireConfirmation - || mSettingObserver.getFaceAlwaysRequireConfirmation(); - } - // Generate random cookies to pass to the services that should prepare to start - // authenticating. Store the cookie here and wait for all services to "ack" - // with the cookie. Once all cookies are received, we can show the prompt - // and let the services start authenticating. The cookie should be non-zero. - final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1; - Slog.d(TAG, "Creating auth session. Modality: " + modality - + ", cookie: " + cookie); - final HashMap<Integer, Integer> authenticators = new HashMap<>(); - authenticators.put(modality, cookie); - mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId, - receiver, opPackageName, bundle, callingUid, callingPid, callingUserId, - modality, requireConfirmation); - mPendingAuthSession.mState = STATE_AUTH_CALLED; - // No polymorphism :( - if ((modality & TYPE_FINGERPRINT) != 0) { - mFingerprintService.prepareForAuthentication(token, sessionId, userId, - mInternalReceiver, opPackageName, cookie, - callingUid, callingPid, callingUserId); - } - if ((modality & TYPE_IRIS) != 0) { - Slog.w(TAG, "Iris unsupported"); + mHandler.post(() -> { + if (mConfirmDeviceCredentialReceiver == null) { + Slog.w(TAG, "onCDCAError null! Error: " + error + " " + message); + return; } - if ((modality & TYPE_FACE) != 0) { - mFaceService.prepareForAuthentication(requireConfirmation, - token, sessionId, userId, mInternalReceiver, opPackageName, - cookie, callingUid, callingPid, callingUserId); + try { + mConfirmDeviceCredentialReceiver.onError(error, message); + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException", e); } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to start authentication", e); - } + mConfirmDeviceCredentialReceiver = null; + }); } @Override // Binder call public void cancelAuthentication(IBinder token, String opPackageName) throws RemoteException { checkPermission(); - if (token == null || opPackageName == null) { - Slog.e(TAG, "Unable to cancel, one or more null arguments"); - return; - } - - // We need to check the current authenticators state. If we're pending confirm - // or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client, - // since we won't be getting an onError from the driver. - if (mCurrentAuthSession != null && mCurrentAuthSession.mState != STATE_AUTH_STARTED) { - mHandler.post(() -> { - try { - // Send error to client - mCurrentAuthSession.mClientReceiver.onError( - BiometricConstants.BIOMETRIC_ERROR_CANCELED, - getContext().getString( - com.android.internal.R.string.biometric_error_user_canceled) - ); - mCurrentAuthSession.mState = STATE_AUTH_IDLE; - mCurrentAuthSession = null; - mStatusBarService.hideBiometricDialog(); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception", e); - } - }); - } else { - cancelInternal(token, opPackageName, true /* fromClient */); - } + SomeArgs args = SomeArgs.obtain(); + args.arg1 = token; + args.arg2 = opPackageName; + mHandler.obtainMessage(MSG_CANCEL_AUTHENTICATION, args).sendToTarget(); } @Override // Binder call @@ -1027,31 +752,6 @@ public class BiometricService extends SystemService { Binder.restoreCallingIdentity(ident); } } - - void cancelInternal(IBinder token, String opPackageName, boolean fromClient) { - final int callingUid = Binder.getCallingUid(); - final int callingPid = Binder.getCallingPid(); - final int callingUserId = UserHandle.getCallingUserId(); - mHandler.post(() -> { - try { - // TODO: For multiple modalities, send a single ERROR_CANCELED only when all - // drivers have canceled authentication. - if ((mCurrentModality & TYPE_FINGERPRINT) != 0) { - mFingerprintService.cancelAuthenticationFromService(token, opPackageName, - callingUid, callingPid, callingUserId, fromClient); - } - if ((mCurrentModality & TYPE_IRIS) != 0) { - Slog.w(TAG, "Iris unsupported"); - } - if ((mCurrentModality & TYPE_FACE) != 0) { - mFaceService.cancelAuthenticationFromService(token, opPackageName, - callingUid, callingPid, callingUserId, fromClient); - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to cancel authentication"); - } - }); - } } private void checkAppOp(String opPackageName, int callingUid) { @@ -1088,7 +788,6 @@ public class BiometricService extends SystemService { super(context); mAppOps = context.getSystemService(AppOpsManager.class); - mHandler = new Handler(Looper.getMainLooper()); mEnabledOnKeyguardCallbacks = new ArrayList<>(); mSettingObserver = new SettingObserver(mHandler); @@ -1123,6 +822,10 @@ public class BiometricService extends SystemService { ServiceManager.getService(Context.FACE_SERVICE)); } + mActivityTaskManager = ActivityTaskManager.getService(); + mStatusBarService = IStatusBarService.Stub.asInterface( + ServiceManager.getService(Context.STATUS_BAR_SERVICE)); + // Cache the authenticators for (int i = 0; i < FEATURE_ID.length; i++) { if (hasFeature(FEATURE_ID[i])) { @@ -1259,4 +962,491 @@ public class BiometricService extends SystemService { return false; } } + + private void logDialogDismissed(int reason) { + if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) { + // Explicit auth, authentication confirmed. + // Latency in this case is authenticated -> confirmed. <Biometric>Service + // should have the first half (first acquired -> authenticated). + final long latency = System.currentTimeMillis() + - mCurrentAuthSession.mAuthenticatedTimeMs; + + if (LoggableMonitor.DEBUG) { + Slog.v(LoggableMonitor.TAG, "Confirmed! Modality: " + statsModality() + + ", User: " + mCurrentAuthSession.mUserId + + ", IsCrypto: " + mCurrentAuthSession.isCrypto() + + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT + + ", RequireConfirmation: " + + mCurrentAuthSession.mRequireConfirmation + + ", State: " + StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED + + ", Latency: " + latency); + } + + StatsLog.write(StatsLog.BIOMETRIC_AUTHENTICATED, + statsModality(), + mCurrentAuthSession.mUserId, + mCurrentAuthSession.isCrypto(), + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, + mCurrentAuthSession.mRequireConfirmation, + StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED, + latency); + } else { + int error = reason == BiometricPrompt.DISMISSED_REASON_NEGATIVE + ? BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON + : reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL + ? BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED + : 0; + if (LoggableMonitor.DEBUG) { + Slog.v(LoggableMonitor.TAG, "Dismissed! Modality: " + statsModality() + + ", User: " + mCurrentAuthSession.mUserId + + ", IsCrypto: " + mCurrentAuthSession.isCrypto() + + ", Action: " + BiometricsProtoEnums.ACTION_AUTHENTICATE + + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT + + ", Error: " + error); + } + // Auth canceled + StatsLog.write(StatsLog.BIOMETRIC_ERROR_OCCURRED, + statsModality(), + mCurrentAuthSession.mUserId, + mCurrentAuthSession.isCrypto(), + BiometricsProtoEnums.ACTION_AUTHENTICATE, + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, + error, + 0 /* vendorCode */); + } + } + + private int statsModality() { + int modality = 0; + if (mCurrentAuthSession == null) { + return BiometricsProtoEnums.MODALITY_UNKNOWN; + } + if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FINGERPRINT) + != 0) { + modality |= BiometricsProtoEnums.MODALITY_FINGERPRINT; + } + if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_IRIS) != 0) { + modality |= BiometricsProtoEnums.MODALITY_IRIS; + } + if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FACE) != 0) { + modality |= BiometricsProtoEnums.MODALITY_FACE; + } + return modality; + } + + private void handleTaskStackChanged() { + try { + final List<ActivityManager.RunningTaskInfo> runningTasks = + mActivityTaskManager.getTasks(1); + if (!runningTasks.isEmpty()) { + final String topPackage = runningTasks.get(0).topActivity.getPackageName(); + if (mCurrentAuthSession != null + && !topPackage.contentEquals(mCurrentAuthSession.mOpPackageName)) { + mStatusBarService.hideBiometricDialog(); + mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); + mCurrentAuthSession.mClientReceiver.onError( + BiometricConstants.BIOMETRIC_ERROR_CANCELED, + getContext().getString( + com.android.internal.R.string.biometric_error_canceled) + ); + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + } + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to get running tasks", e); + } + } + + private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] token) { + + try { + // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded + // after user dismissed/canceled dialog). + if (mCurrentAuthSession == null) { + Slog.e(TAG, "onAuthenticationSucceeded(): Auth session is null"); + return; + } + + if (!requireConfirmation) { + mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); + KeyStore.getInstance().addAuthToken(token); + mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded(); + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + } else { + mCurrentAuthSession.mAuthenticatedTimeMs = System.currentTimeMillis(); + // Store the auth token and submit it to keystore after the confirmation + // button has been pressed. + mCurrentAuthSession.mTokenEscrow = token; + mCurrentAuthSession.mState = STATE_AUTH_PENDING_CONFIRM; + } + + // Notify SysUI that the biometric has been authenticated. SysUI already knows + // the implicit/explicit state and will react accordingly. + mStatusBarService.onBiometricAuthenticated(true); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + + private void handleAuthenticationFailed(int cookie, boolean requireConfirmation) { + try { + // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded + // after user dismissed/canceled dialog). + if (mCurrentAuthSession == null) { + Slog.e(TAG, "onAuthenticationFailed(): Auth session is null"); + return; + } + + mStatusBarService.onBiometricAuthenticated(false); + + // TODO: This logic will need to be updated if BP is multi-modal + if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) { + // Pause authentication. onBiometricAuthenticated(false) causes the + // dialog to show a "try again" button for passive modalities. + mCurrentAuthSession.mState = STATE_AUTH_PAUSED; + } + + mCurrentAuthSession.mClientReceiver.onAuthenticationFailed(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + + private void handleOnError(int cookie, int error, String message) { + Slog.d(TAG, "Error: " + error + " cookie: " + cookie); + // Errors can either be from the current auth session or the pending auth session. + // The pending auth session may receive errors such as ERROR_LOCKOUT before + // it becomes the current auth session. Similarly, the current auth session may + // receive errors such as ERROR_CANCELED while the pending auth session is preparing + // to be started. Thus we must match error messages with their cookies to be sure + // of their intended receivers. + try { + if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) { + if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) { + mStatusBarService.onBiometricError(message); + if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) { + mActivityTaskManager.unregisterTaskStackListener( + mTaskStackListener); + mCurrentAuthSession.mClientReceiver.onError(error, message); + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + mStatusBarService.hideBiometricDialog(); + } else { + // Send errors after the dialog is dismissed. + mHandler.postDelayed(() -> { + try { + if (mCurrentAuthSession != null) { + mActivityTaskManager.unregisterTaskStackListener( + mTaskStackListener); + mCurrentAuthSession.mClientReceiver.onError(error, + message); + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + }, BiometricPrompt.HIDE_DIALOG_DELAY); + } + } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) { + // In the "try again" state, we should forward canceled errors to + // the client and and clean up. + mCurrentAuthSession.mClientReceiver.onError(error, message); + mStatusBarService.onBiometricError(message); + mActivityTaskManager.unregisterTaskStackListener( + mTaskStackListener); + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + } else { + Slog.e(TAG, "Impossible session error state: " + + mCurrentAuthSession.mState); + } + } else if (mPendingAuthSession != null + && mPendingAuthSession.containsCookie(cookie)) { + if (mPendingAuthSession.mState == STATE_AUTH_CALLED) { + mPendingAuthSession.mClientReceiver.onError(error, message); + mPendingAuthSession.mState = STATE_AUTH_IDLE; + mPendingAuthSession = null; + } else { + Slog.e(TAG, "Impossible pending session error state: " + + mPendingAuthSession.mState); + } + } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + + private void handleOnAcquired(int acquiredInfo, String message) { + // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded + // after user dismissed/canceled dialog). + if (mCurrentAuthSession == null) { + Slog.e(TAG, "onAcquired(): Auth session is null"); + return; + } + + if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) { + try { + mStatusBarService.onBiometricHelp(message); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + } + + private void handleOnDismissed(int reason) { + if (mCurrentAuthSession == null) { + Slog.e(TAG, "onDialogDismissed: " + reason + ", auth session null"); + return; + } + + logDialogDismissed(reason); + + try { + if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) { + // Positive button is used by passive modalities as a "confirm" button, + // do not send to client + mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason); + // Cancel authentication. Skip the token/package check since we are cancelling + // from system server. The interface is permission protected so this is fine. + cancelInternal(null /* token */, null /* package */, false /* fromClient */); + } + if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) { + mCurrentAuthSession.mClientReceiver.onError( + BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED, + getContext().getString( + com.android.internal.R.string.biometric_error_user_canceled)); + } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) { + // Have the service send the token to KeyStore, and send onAuthenticated + // to the application + KeyStore.getInstance().addAuthToken(mCurrentAuthSession.mTokenEscrow); + mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded(); + } + mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + + private void handleOnTryAgainPressed() { + Slog.d(TAG, "onTryAgainPressed"); + // No need to check permission, since it can only be invoked by SystemUI + // (or system server itself). + authenticateInternal(mCurrentAuthSession.mToken, + mCurrentAuthSession.mSessionId, + mCurrentAuthSession.mUserId, + mCurrentAuthSession.mClientReceiver, + mCurrentAuthSession.mOpPackageName, + mCurrentAuthSession.mBundle, + mCurrentAuthSession.mCallingUid, + mCurrentAuthSession.mCallingPid, + mCurrentAuthSession.mCallingUserId, + mCurrentAuthSession.mModality); + } + + private void handleOnReadyForAuthentication(int cookie, boolean requireConfirmation, + int userId) { + Iterator it = mPendingAuthSession.mModalitiesWaiting.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry<Integer, Integer> pair = (Map.Entry) it.next(); + if (pair.getValue() == cookie) { + mPendingAuthSession.mModalitiesMatched.put(pair.getKey(), pair.getValue()); + mPendingAuthSession.mModalitiesWaiting.remove(pair.getKey()); + Slog.d(TAG, "Matched cookie: " + cookie + ", " + + mPendingAuthSession.mModalitiesWaiting.size() + " remaining"); + break; + } + } + + if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) { + final boolean continuing = mCurrentAuthSession != null + && mCurrentAuthSession.mState == STATE_AUTH_PAUSED; + + mCurrentAuthSession = mPendingAuthSession; + mPendingAuthSession = null; + + mCurrentAuthSession.mState = STATE_AUTH_STARTED; + try { + int modality = TYPE_NONE; + it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry<Integer, Integer> pair = (Map.Entry) it.next(); + if (pair.getKey() == TYPE_FINGERPRINT) { + mFingerprintService.startPreparedClient(pair.getValue()); + } else if (pair.getKey() == TYPE_IRIS) { + Slog.e(TAG, "Iris unsupported"); + } else if (pair.getKey() == TYPE_FACE) { + mFaceService.startPreparedClient(pair.getValue()); + } else { + Slog.e(TAG, "Unknown modality: " + pair.getKey()); + } + modality |= pair.getKey(); + } + + if (!continuing) { + mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle, + mInternalReceiver, modality, requireConfirmation, userId); + mActivityTaskManager.registerTaskStackListener(mTaskStackListener); + } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + } + + private void handleAuthenticate(IBinder token, long sessionId, int userId, + IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle, + int callingUid, int callingPid, int callingUserId) { + + mHandler.post(() -> { + final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId); + final int modality = result.first; + final int error = result.second; + + // Check for errors, notify callback, and return + if (error != BiometricConstants.BIOMETRIC_SUCCESS) { + try { + final String hardwareUnavailable = + getContext().getString(R.string.biometric_error_hw_unavailable); + switch (error) { + case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT: + receiver.onError(error, hardwareUnavailable); + break; + case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE: + receiver.onError(error, hardwareUnavailable); + break; + case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS: + receiver.onError(error, + getErrorString(modality, error, 0 /* vendorCode */)); + break; + default: + Slog.e(TAG, "Unhandled error"); + break; + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to send error", e); + } + return; + } + + mCurrentModality = modality; + + // Start preparing for authentication. Authentication starts when + // all modalities requested have invoked onReadyForAuthentication. + authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle, + callingUid, callingPid, callingUserId, modality); + }); + } + + /** + * authenticate() (above) which is called from BiometricPrompt determines which + * modality/modalities to start authenticating with. authenticateInternal() should only be + * used for: + * 1) Preparing <Biometric>Services for authentication when BiometricPrompt#authenticate is, + * invoked, shortly after which BiometricPrompt is shown and authentication starts + * 2) Preparing <Biometric>Services for authentication when BiometricPrompt is already shown + * and the user has pressed "try again" + */ + private void authenticateInternal(IBinder token, long sessionId, int userId, + IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle, + int callingUid, int callingPid, int callingUserId, int modality) { + try { + boolean requireConfirmation = bundle.getBoolean( + BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */); + if ((modality & TYPE_FACE) != 0) { + // Check if the user has forced confirmation to be required in Settings. + requireConfirmation = requireConfirmation + || mSettingObserver.getFaceAlwaysRequireConfirmation(); + } + // Generate random cookies to pass to the services that should prepare to start + // authenticating. Store the cookie here and wait for all services to "ack" + // with the cookie. Once all cookies are received, we can show the prompt + // and let the services start authenticating. The cookie should be non-zero. + final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1; + Slog.d(TAG, "Creating auth session. Modality: " + modality + + ", cookie: " + cookie); + final HashMap<Integer, Integer> authenticators = new HashMap<>(); + authenticators.put(modality, cookie); + mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId, + receiver, opPackageName, bundle, callingUid, callingPid, callingUserId, + modality, requireConfirmation); + mPendingAuthSession.mState = STATE_AUTH_CALLED; + // No polymorphism :( + if ((modality & TYPE_FINGERPRINT) != 0) { + mFingerprintService.prepareForAuthentication(token, sessionId, userId, + mInternalReceiver, opPackageName, cookie, + callingUid, callingPid, callingUserId); + } + if ((modality & TYPE_IRIS) != 0) { + Slog.w(TAG, "Iris unsupported"); + } + if ((modality & TYPE_FACE) != 0) { + mFaceService.prepareForAuthentication(requireConfirmation, + token, sessionId, userId, mInternalReceiver, opPackageName, + cookie, callingUid, callingPid, callingUserId); + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to start authentication", e); + } + } + + private void handleCancelAuthentication(IBinder token, String opPackageName) { + if (token == null || opPackageName == null) { + Slog.e(TAG, "Unable to cancel, one or more null arguments"); + return; + } + + // We need to check the current authenticators state. If we're pending confirm + // or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client, + // since we won't be getting an onError from the driver. + if (mCurrentAuthSession != null && mCurrentAuthSession.mState != STATE_AUTH_STARTED) { + try { + // Send error to client + mCurrentAuthSession.mClientReceiver.onError( + BiometricConstants.BIOMETRIC_ERROR_CANCELED, + getContext().getString( + com.android.internal.R.string.biometric_error_user_canceled) + ); + + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + mStatusBarService.hideBiometricDialog(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } else { + cancelInternal(token, opPackageName, true /* fromClient */); + } + } + + + void cancelInternal(IBinder token, String opPackageName, boolean fromClient) { + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); + final int callingUserId = UserHandle.getCallingUserId(); + mHandler.post(() -> { + try { + // TODO: For multiple modalities, send a single ERROR_CANCELED only when all + // drivers have canceled authentication. + if ((mCurrentModality & TYPE_FINGERPRINT) != 0) { + mFingerprintService.cancelAuthenticationFromService(token, opPackageName, + callingUid, callingPid, callingUserId, fromClient); + } + if ((mCurrentModality & TYPE_IRIS) != 0) { + Slog.w(TAG, "Iris unsupported"); + } + if ((mCurrentModality & TYPE_FACE) != 0) { + mFaceService.cancelAuthenticationFromService(token, opPackageName, + callingUid, callingPid, callingUserId, fromClient); + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to cancel authentication"); + } + }); + } + } diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java index ebb9e060317b..3e48445dfb36 100644 --- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java @@ -1223,6 +1223,9 @@ public abstract class BiometricServiceBase extends SystemService BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL); } else { clearEnumerateState(); + if (mPendingClient != null) { + startClient(mPendingClient, false /* initiatedByClient */); + } } } diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java index 23117760f45f..45567e5a34cb 100644 --- a/services/core/java/com/android/server/display/color/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java @@ -402,6 +402,10 @@ public final class ColorDisplayService extends SystemService { cr.registerContentObserver(Secure.getUriFor(Secure.DISPLAY_WHITE_BALANCE_ENABLED), false /* notifyForDescendants */, mContentObserver, mCurrentUser); + // Apply the accessibility settings first, since they override most other settings. + onAccessibilityInversionChanged(); + onAccessibilityDaltonizerChanged(); + // Set the color mode, if valid, and immediately apply the updated tint matrix based on the // existing activated state. This ensures consistency of tint across the color mode change. onDisplayColorModeChanged(getColorModeInternal()); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 150303aa6a46..647e95284534 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -231,7 +231,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Context.BIND_AUTO_CREATE | Context.BIND_TREAT_LIKE_ACTIVITY | Context.BIND_FOREGROUND_SERVICE - | Context.BIND_SHOWING_UI; + | Context.BIND_SHOWING_UI + | Context.BIND_SCHEDULE_LIKE_TOP_APP; @Retention(SOURCE) @IntDef({HardKeyboardBehavior.WIRELESS_AFFORDANCE, HardKeyboardBehavior.WIRED_AFFORDANCE}) diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index cefe583fc5d1..14b73012d126 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -2494,24 +2494,33 @@ public class JobSchedulerService extends com.android.server.SystemService } } - // The expensive check last: validate that the defined package+service is + // The expensive check: validate that the defined package+service is // still present & viable. - final boolean componentPresent; + final ServiceInfo service; try { - componentPresent = (AppGlobals.getPackageManager().getServiceInfo( + service = AppGlobals.getPackageManager().getServiceInfo( job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING, - job.getUserId()) != null); + job.getUserId()); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } - if (DEBUG) { - Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() - + " componentPresent=" + componentPresent); + if (service == null) { + if (DEBUG) { + Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() + + " component not present"); + } + return false; } // Everything else checked out so far, so this is the final yes/no check - return componentPresent; + final boolean appIsBad = mActivityManagerInternal.isAppBad(service.applicationInfo); + if (DEBUG) { + if (appIsBad) { + Slog.i(TAG, "App is bad for " + job.toShortString() + " so not runnable"); + } + } + return !appIsBad; } private void evaluateControllerStatesLocked(final JobStatus job) { diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index a53ab84f733e..293813add594 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -2699,6 +2699,14 @@ public class LockSettingsService extends ILockSettings.Stub { } } + @Override + public boolean hasPendingEscrowToken(int userId) { + checkPasswordReadPermission(userId); + synchronized (mSpManager) { + return !mSpManager.getPendingTokensForUser(userId).isEmpty(); + } + } + private boolean removeEscrowToken(long handle, int userId) { synchronized (mSpManager) { if (handle == getSyntheticPasswordHandleLocked(userId)) { diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java index 142ad53136fc..1ba0e8ce7839 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java @@ -751,7 +751,7 @@ public class SyntheticPasswordManager { /** * Create a token based Synthetic password for the given user. - * @return + * @return the handle of the token */ public long createTokenBasedSyntheticPassword(byte[] token, int userId, @Nullable EscrowTokenStateChangeCallback changeCallback) { diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 192ad6d57d52..7b691b411a99 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -28,6 +28,7 @@ import android.media.MediaParceledListSlice; import android.media.Rating; import android.media.VolumeProvider; import android.media.session.ControllerCallbackLink; +import android.media.session.ISession; import android.media.session.ISessionController; import android.media.session.MediaController; import android.media.session.MediaController.PlaybackInfo; @@ -35,7 +36,6 @@ import android.media.session.MediaSession; import android.media.session.MediaSession.QueueItem; import android.media.session.PlaybackState; import android.media.session.SessionCallbackLink; -import android.media.session.SessionLink; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -45,6 +45,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Process; +import android.os.RemoteException; import android.os.ResultReceiver; import android.os.SystemClock; import android.util.Log; @@ -81,7 +82,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private final Bundle mSessionInfo; private final ControllerStub mController; private final MediaSession.Token mSessionToken; - private final SessionLink mSession; + private final SessionStub mSession; private final SessionCb mSessionCb; private final MediaSessionService.ServiceImpl mService; private final Context mContext; @@ -133,7 +134,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { mSessionInfo = sessionInfo; mController = new ControllerStub(); mSessionToken = new MediaSession.Token(mController); - mSession = new SessionLink(new SessionStub()); + mSession = new SessionStub(); mSessionCb = new SessionCb(cb); mService = service; mContext = mService.getContext(); @@ -144,11 +145,11 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } /** - * Get the session link for the {@link MediaSession}. + * Get the session binder for the {@link MediaSession}. * - * @return The session link apps talk to. + * @return The session binder apps talk to. */ - public SessionLink getSessionBinder() { + public ISession getSessionBinder() { return mSession; } @@ -818,9 +819,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } }; - private final class SessionStub extends SessionLink.SessionStub { + private final class SessionStub extends ISession.Stub { @Override - public void destroySession() { + public void destroySession() throws RemoteException { final long token = Binder.clearCallingIdentity(); try { mService.destroySession(MediaSessionRecord.this); @@ -830,18 +831,18 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override - public void sendEvent(String event, Bundle data) { + public void sendEvent(String event, Bundle data) throws RemoteException { mHandler.post(MessageHandler.MSG_SEND_EVENT, event, data == null ? null : new Bundle(data)); } @Override - public ISessionController getController() { + public ISessionController getController() throws RemoteException { return mController; } @Override - public void setActive(boolean active) { + public void setActive(boolean active) throws RemoteException { mIsActive = active; final long token = Binder.clearCallingIdentity(); try { @@ -853,7 +854,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override - public void setFlags(int flags) { + public void setFlags(int flags) throws RemoteException { if ((flags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) { int pid = Binder.getCallingPid(); int uid = Binder.getCallingUid(); @@ -872,7 +873,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override - public void setMediaButtonReceiver(PendingIntent pi) { + public void setMediaButtonReceiver(PendingIntent pi) throws RemoteException { mMediaButtonReceiver = pi; final long token = Binder.clearCallingIdentity(); try { @@ -883,12 +884,13 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override - public void setLaunchPendingIntent(PendingIntent pi) { + public void setLaunchPendingIntent(PendingIntent pi) throws RemoteException { mLaunchIntent = pi; } @Override - public void setMetadata(MediaMetadata metadata, long duration, String metadataDescription) { + public void setMetadata(MediaMetadata metadata, long duration, String metadataDescription) + throws RemoteException { synchronized (mLock) { MediaMetadata temp = metadata == null ? null : new MediaMetadata.Builder(metadata) .build(); @@ -906,7 +908,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override - public void setPlaybackState(PlaybackState state) { + public void setPlaybackState(PlaybackState state) throws RemoteException { int oldState = mPlaybackState == null ? PlaybackState.STATE_NONE : mPlaybackState.getState(); int newState = state == null @@ -924,21 +926,21 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override - public void setQueue(List<QueueItem> queue) { + public void setQueue(MediaParceledListSlice queue) throws RemoteException { synchronized (mLock) { - mQueue = queue; + mQueue = queue == null ? null : (List<QueueItem>) queue.getList(); } mHandler.post(MessageHandler.MSG_UPDATE_QUEUE); } @Override - public void setQueueTitle(CharSequence title) { + public void setQueueTitle(CharSequence title) throws RemoteException { mQueueTitle = title; mHandler.post(MessageHandler.MSG_UPDATE_QUEUE_TITLE); } @Override - public void setExtras(Bundle extras) { + public void setExtras(Bundle extras) throws RemoteException { synchronized (mLock) { mExtras = extras == null ? null : new Bundle(extras); } @@ -946,18 +948,18 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override - public void setRatingType(int type) { + public void setRatingType(int type) throws RemoteException { mRatingType = type; } @Override - public void setCurrentVolume(int volume) { + public void setCurrentVolume(int volume) throws RemoteException { mCurrentVolume = volume; mHandler.post(MessageHandler.MSG_UPDATE_VOLUME); } @Override - public void setPlaybackToLocal(AudioAttributes attributes) { + public void setPlaybackToLocal(AudioAttributes attributes) throws RemoteException { boolean typeChanged; synchronized (mLock) { typeChanged = mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE; @@ -980,7 +982,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override - public void setPlaybackToRemote(int control, int max) { + public void setPlaybackToRemote(int control, int max) throws RemoteException { boolean typeChanged; synchronized (mLock) { typeChanged = mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL; diff --git a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java index 94f289f2e382..7c8dc745f6f7 100644 --- a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java @@ -49,12 +49,12 @@ import android.media.session.IActiveSessionsListener; import android.media.session.ICallback; import android.media.session.IOnMediaKeyListener; import android.media.session.IOnVolumeKeyLongPressListener; +import android.media.session.ISession; import android.media.session.ISession2TokensListener; import android.media.session.ISessionManager; import android.media.session.MediaSession; import android.media.session.MediaSessionManager; import android.media.session.SessionCallbackLink; -import android.media.session.SessionLink; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -1001,7 +1001,7 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { private boolean mVoiceButtonHandled = false; @Override - public SessionLink createSession(String packageName, SessionCallbackLink cb, String tag, + public ISession createSession(String packageName, SessionCallbackLink cb, String tag, Bundle sessionInfo, int userId) throws RemoteException { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 88e697c15a72..4a215cf7febb 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2504,15 +2504,20 @@ public class NotificationManagerService extends SystemService { } @Override - public boolean canNotifyAsPackage(String callingPkg, String targetPkg) { + public boolean canNotifyAsPackage(String callingPkg, String targetPkg, int userId) { checkCallerIsSameApp(callingPkg); final int callingUid = Binder.getCallingUid(); UserHandle user = UserHandle.getUserHandleForUid(callingUid); + if (user.getIdentifier() != userId) { + getContext().enforceCallingPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS, + "canNotifyAsPackage for user " + userId); + } try { ApplicationInfo info = mPackageManager.getApplicationInfo(targetPkg, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, - user.getIdentifier()); + userId); if (info != null) { return mPreferencesHelper.isDelegateAllowed( targetPkg, info.uid, callingPkg, callingUid); diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 7f057f096cae..18cfa4a479f4 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -45,8 +45,6 @@ import android.content.pm.ResolveInfo; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutServiceInternal; import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener; -import android.content.pm.Signature; -import android.content.pm.SigningInfo; import android.content.pm.UserInfo; import android.graphics.Rect; import android.net.Uri; @@ -62,31 +60,21 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.provider.Settings; -import android.util.ByteStringUtils; import android.util.Log; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; -import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; -import com.android.internal.util.StatLogger; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.wm.ActivityTaskManagerInternal; -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; /** * Service that manages requests and callbacks for launchers that support @@ -125,16 +113,6 @@ public class LauncherAppsService extends SystemService { private static final boolean DEBUG = false; private static final String TAG = "LauncherAppsService"; - // Stats - @VisibleForTesting - interface Stats { - int INIT_VOUCHED_SIGNATURES = 0; - int COUNT = INIT_VOUCHED_SIGNATURES + 1; - } - private final StatLogger mStatLogger = new StatLogger(new String[] { - "initVouchedSignatures" - }); - private final Context mContext; private final UserManager mUm; private final UserManagerInternal mUserManagerInternal; @@ -145,16 +123,11 @@ public class LauncherAppsService extends SystemService { private final PackageCallbackList<IOnAppsChangedListener> mListeners = new PackageCallbackList<IOnAppsChangedListener>(); private final DevicePolicyManager mDpm; - private final ConcurrentHashMap<UserHandle, Set<String>> mVouchedSignaturesByUser; - private final Set<String> mVouchProviders; private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor(); - private final VouchesChangedMonitor mVouchesChangedMonitor = new VouchesChangedMonitor(); private final Handler mCallbackHandler; - private final Object mVouchedSignaturesLocked = new Object(); - private PackageInstallerService mPackageInstallerService; public LauncherAppsImpl(Context context) { @@ -173,9 +146,6 @@ public class LauncherAppsService extends SystemService { mShortcutServiceInternal.addListener(mPackageMonitor); mCallbackHandler = BackgroundThread.getHandler(); mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); - mVouchedSignaturesByUser = new ConcurrentHashMap<>(); - mVouchProviders = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>()); - mVouchesChangedMonitor.register(mContext, UserHandle.ALL, true, mCallbackHandler); } @VisibleForTesting @@ -468,32 +438,9 @@ public class LauncherAppsService extends SystemService { if (appInfo == null || appInfo.isSystemApp() || appInfo.isUpdatedSystemApp()) { return false; } - if (!mVouchedSignaturesByUser.containsKey(user)) { - initVouchedSignatures(user); - } if (isManagedProfileAdmin(user, appInfo.packageName)) { return false; } - if (mVouchProviders.contains(appInfo.packageName)) { - // If it's a vouching packages then we must show hidden app - return true; - } - // If app's signature is in vouch list, do not show hidden app - final Set<String> vouches = mVouchedSignaturesByUser.get(user); - try { - final PackageInfo pkgInfo = mContext.getPackageManager().getPackageInfo( - appInfo.packageName, PackageManager.GET_SIGNING_CERTIFICATES); - final Signature[] signatures = getLatestSignatures(pkgInfo.signingInfo); - // If any of the signatures appears in vouches, then we don't show hidden app - for (Signature signature : signatures) { - final String certDigest = computePackageCertDigest(signature); - if (vouches.contains(certDigest)) { - return false; - } - } - } catch (PackageManager.NameNotFoundException e) { - // Should not happen - } return true; } @@ -515,100 +462,6 @@ public class LauncherAppsService extends SystemService { return false; } - @VisibleForTesting - static String computePackageCertDigest(Signature signature) { - MessageDigest messageDigest; - try { - messageDigest = MessageDigest.getInstance("SHA1"); - } catch (NoSuchAlgorithmException e) { - // Should not happen - return null; - } - messageDigest.update(signature.toByteArray()); - final byte[] digest = messageDigest.digest(); - return ByteStringUtils.toHexString(digest); - } - - @VisibleForTesting - static Signature[] getLatestSignatures(SigningInfo signingInfo) { - if (signingInfo.hasMultipleSigners()) { - return signingInfo.getApkContentsSigners(); - } else { - final Signature[] signatures = signingInfo.getSigningCertificateHistory(); - return new Signature[]{signatures[0]}; - } - } - - private void updateVouches(String packageName, UserHandle user) { - final PackageManagerInternal pmInt = - LocalServices.getService(PackageManagerInternal.class); - ApplicationInfo appInfo = pmInt.getApplicationInfo(packageName, - PackageManager.GET_META_DATA, Binder.getCallingUid(), user.getIdentifier()); - if (appInfo == null) { - Log.w(TAG, "appInfo " + packageName + " is null"); - return; - } - updateVouches(appInfo, user); - } - - private void updateVouches(ApplicationInfo appInfo, UserHandle user) { - if (appInfo == null || appInfo.metaData == null) { - // No meta-data - return; - } - int tokenResourceId = appInfo.metaData.getInt(LauncherApps.VOUCHED_CERTS_KEY); - if (tokenResourceId == 0) { - // No xml file - return; - } - mVouchProviders.add(appInfo.packageName); - Set<String> vouches = mVouchedSignaturesByUser.get(user); - try { - List<String> signatures = Arrays.asList( - mContext.getPackageManager().getResourcesForApplication( - appInfo.packageName).getStringArray(tokenResourceId)); - for (String signature : signatures) { - vouches.add(signature.toUpperCase()); - } - } catch (PackageManager.NameNotFoundException e) { - // Should not happen - } - } - - private void initVouchedSignatures(UserHandle user) { - synchronized (mVouchedSignaturesLocked) { - if (mVouchedSignaturesByUser.contains(user)) { - return; - } - final long startTime = mStatLogger.getTime(); - - Set<String> vouches = Collections.newSetFromMap( - new ConcurrentHashMap<String, Boolean>()); - - final int callingUid = injectBinderCallingUid(); - long ident = Binder.clearCallingIdentity(); - try { - final PackageManagerInternal pmInt = - LocalServices.getService(PackageManagerInternal.class); - List<ApplicationInfo> installedPackages = pmInt.getInstalledApplications( - PackageManager.GET_META_DATA, user.getIdentifier(), callingUid); - for (ApplicationInfo appInfo : installedPackages) { - updateVouches(appInfo, user); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - mVouchedSignaturesByUser.putIfAbsent(user, vouches); - mStatLogger.logDurationStat(Stats.INIT_VOUCHED_SIGNATURES, startTime); - } - } - - @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return; - mStatLogger.dump(pw, " "); - } - @Override public ActivityInfo resolveActivity( String callingPackage, ComponentName component, UserHandle user) @@ -1022,18 +875,6 @@ public class LauncherAppsService extends SystemService { mCallbackHandler.post(r); } - private class VouchesChangedMonitor extends PackageMonitor { - @Override - public void onPackageAdded(String packageName, int uid) { - updateVouches(packageName, new UserHandle(getChangingUserId())); - } - - @Override - public void onPackageModified(String packageName) { - updateVouches(packageName, new UserHandle(getChangingUserId())); - } - } - private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener { // TODO Simplify with lambdas. diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index e5b6397e863d..4e8ef7100466 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -160,9 +160,9 @@ import android.content.pm.InstantAppInfo; import android.content.pm.InstantAppRequest; import android.content.pm.InstrumentationInfo; import android.content.pm.IntentFilterVerificationInfo; -import android.content.pm.PackageBackwardCompatibility; import android.content.pm.KeySet; import android.content.pm.ModuleInfo; +import android.content.pm.PackageBackwardCompatibility; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageInstaller; @@ -232,6 +232,7 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; +import android.os.storage.DiskInfo; import android.os.storage.IStorageManager; import android.os.storage.StorageEventListener; import android.os.storage.StorageManager; @@ -245,6 +246,7 @@ import android.provider.Settings.Secure; import android.security.KeyStore; import android.security.SystemKeyStore; import android.service.pm.PackageServiceDumpProto; +import android.stats.storage.StorageEnums; import android.system.ErrnoException; import android.system.Os; import android.text.TextUtils; @@ -269,6 +271,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; +import android.util.StatsLog; import android.util.TimingsTraceLog; import android.util.Xml; import android.util.jar.StrictJarFile; @@ -1912,6 +1915,15 @@ public class PackageManagerService extends IPackageManager.Stub // Send broadcast package appeared if external for all users if (isExternal(res.pkg)) { + if (!update) { + int packageExternalStorageType = + getPackageExternalStorageType(res.pkg); + // If the package was installed externally, log it. + if (packageExternalStorageType != StorageEnums.UNKNOWN) { + StatsLog.write(StatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED, + packageExternalStorageType, res.pkg.packageName); + } + } if (DEBUG_INSTALL) { Slog.i(TAG, "upgrading pkg " + res.pkg + " is external"); } @@ -2000,6 +2012,32 @@ public class PackageManagerService extends IPackageManager.Stub } } + /** + * Gets the type of the external storage a package is installed on. + * @param pkg The package for which to get the external storage type. + * @return {@link StorageEnum#TYPE_UNKNOWN} if it is not stored externally or the corresponding + * {@link StorageEnum} storage type value if it is. + */ + private int getPackageExternalStorageType(PackageParser.Package pkg) { + final StorageManager storage = mContext.getSystemService(StorageManager.class); + VolumeInfo volume = storage.findVolumeByUuid(pkg.applicationInfo.storageUuid.toString()); + if (volume != null) { + DiskInfo disk = volume.getDisk(); + if (disk != null) { + if (disk.isSd()) { + return StorageEnums.SD_CARD; + } + if (disk.isUsb()) { + return StorageEnums.USB; + } + if (isExternal(pkg)) { + return StorageEnums.OTHER; + } + } + } + return StorageEnums.UNKNOWN; + } + private StorageEventListener mStorageListener = new StorageEventListener() { @Override public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 3744f68afbfe..316a9c0d73a4 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -1144,6 +1144,19 @@ public class UserManagerService extends IUserManager.Stub { } @Override + public String getUserName() { + if (!hasManageUsersOrPermission(android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED)) { + throw new SecurityException("You need MANAGE_USERS or GET_ACCOUNTS_PRIVILEGED " + + "permissions to: get user name"); + } + final int userId = UserHandle.getUserId(Binder.getCallingUid()); + synchronized (mUsersLock) { + UserInfo userInfo = userWithName(getUserInfoLU(userId)); + return userInfo == null ? "" : userInfo.name; + } + } + + @Override public long getUserStartRealtime() { final int userId = UserHandle.getUserId(Binder.getCallingUid()); synchronized (mUsersLock) { @@ -1299,7 +1312,12 @@ public class UserManagerService extends IUserManager.Stub { } } if (changed) { - sendUserInfoChangedBroadcast(userId); + long ident = Binder.clearCallingIdentity(); + try { + sendUserInfoChangedBroadcast(userId); + } finally { + Binder.restoreCallingIdentity(ident); + } } } @@ -1324,7 +1342,10 @@ public class UserManagerService extends IUserManager.Stub { @Override public ParcelFileDescriptor getUserIcon(int targetUserId) { - checkManageUsersPermission("get user icon"); + if (!hasManageUsersOrPermission(android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED)) { + throw new SecurityException("You need MANAGE_USERS or GET_ACCOUNTS_PRIVILEGED " + + "permissions to: get user icon"); + } String iconPath; synchronized (mPackagesLock) { UserInfo targetUserInfo = getUserInfoNoChecks(targetUserId); @@ -1941,15 +1962,23 @@ public class UserManagerService extends IUserManager.Stub { /** * @return whether the calling UID is system UID or root's UID or the calling app has the - * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} or - * {@link android.Manifest.permission#CREATE_USERS CREATE_USERS}. + * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} or the provided permission. */ - private static final boolean hasManageOrCreateUsersPermission() { + private static final boolean hasManageUsersOrPermission(String alternativePermission) { final int callingUid = Binder.getCallingUid(); return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) || callingUid == Process.ROOT_UID || hasPermissionGranted(android.Manifest.permission.MANAGE_USERS, callingUid) - || hasPermissionGranted(android.Manifest.permission.CREATE_USERS, callingUid); + || hasPermissionGranted(alternativePermission, callingUid); + } + + /** + * @return whether the calling UID is system UID or root's UID or the calling app has the + * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} or + * {@link android.Manifest.permission#CREATE_USERS CREATE_USERS}. + */ + private static final boolean hasManageOrCreateUsersPermission() { + return hasManageUsersOrPermission(android.Manifest.permission.CREATE_USERS); } /** diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 58106367842a..e7c9a0819896 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -104,7 +104,6 @@ import static com.android.server.wm.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; -import android.app.ActivityOptions; import android.app.ActivityTaskManager; import android.app.AppOpsManager; import android.app.IUiModeManager; @@ -5114,6 +5113,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { awakenDreams(); } + // Start dock. Intent dock = createHomeDockIntent(); if (dock != null) { try { @@ -5126,21 +5126,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - Intent intent; - - if (fromHomeKey) { - intent = new Intent(mHomeIntent); - intent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, fromHomeKey); - } else { - intent = mHomeIntent; - } - final Bundle bundle = getLaunchDisplayIdBundle(displayId); - startActivityAsUser(intent, bundle, UserHandle.CURRENT); - } - - private @Nullable Bundle getLaunchDisplayIdBundle(int displayId) { - return (displayId == INVALID_DISPLAY) ? null - : ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(); + // Start home. + mActivityTaskManagerInternal.startHomeOnDisplay(mCurrentUserId, "startDockOrHome", + displayId, fromHomeKey); } /** diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java index 5adcf352e260..f0e462503769 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java @@ -115,9 +115,41 @@ public class BatterySaverController implements BatterySaverPolicyListener { public static final int REASON_SETTING_CHANGED = 8; public static final int REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON = 9; public static final int REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_OFF = 10; - public static final int REASON_STICKY_RESTORE_OFF = 11; - public static final int REASON_ADAPTIVE_DYNAMIC_POWER_SAVINGS_CHANGED = 12; - public static final int REASON_TIMEOUT = 13; + public static final int REASON_ADAPTIVE_DYNAMIC_POWER_SAVINGS_CHANGED = 11; + public static final int REASON_TIMEOUT = 12; + + static String reasonToString(int reason) { + switch (reason) { + case BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_ON: + return "Percentage Auto ON"; + case BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_OFF: + return "Percentage Auto OFF"; + case BatterySaverController.REASON_MANUAL_ON: + return "Manual ON"; + case BatterySaverController.REASON_MANUAL_OFF: + return "Manual OFF"; + case BatterySaverController.REASON_STICKY_RESTORE: + return "Sticky restore"; + case BatterySaverController.REASON_INTERACTIVE_CHANGED: + return "Interactivity changed"; + case BatterySaverController.REASON_POLICY_CHANGED: + return "Policy changed"; + case BatterySaverController.REASON_PLUGGED_IN: + return "Plugged in"; + case BatterySaverController.REASON_SETTING_CHANGED: + return "Setting changed"; + case BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON: + return "Dynamic Warning Auto ON"; + case BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_OFF: + return "Dynamic Warning Auto OFF"; + case BatterySaverController.REASON_ADAPTIVE_DYNAMIC_POWER_SAVINGS_CHANGED: + return "Adaptive Power Savings changed"; + case BatterySaverController.REASON_TIMEOUT: + return "timeout"; + default: + return "Unknown reason: " + reason; + } + } /** * Plugin interface. All methods are guaranteed to be called on the same (handler) thread. diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java index af5d40bfbcec..8f2e997d319e 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java @@ -15,6 +15,8 @@ */ package com.android.server.power.batterysaver; +import static com.android.server.power.batterysaver.BatterySaverController.reasonToString; + import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; @@ -49,6 +51,42 @@ import java.io.PrintWriter; * Do not call out with the lock held. (Settings provider is okay.) * * Test: atest com.android.server.power.batterysaver.BatterySaverStateMachineTest + * + * Current state machine. This can be visualized using Graphviz: + <pre> + + digraph { + STATE_OFF + STATE_MANUAL_ON [label="STATE_MANUAL_ON\nTurned on manually by the user"] + STATE_AUTOMATIC_ON [label="STATE_AUTOMATIC_ON\nTurned on automatically by the system"] + STATE_OFF_AUTOMATIC_SNOOZED [ + label="STATE_OFF_AUTOMATIC_SNOOZED\nTurned off manually by the user." + + " The system should not turn it back on automatically." + ] + STATE_PENDING_STICKY_ON [ + label="STATE_PENDING_STICKY_ON\n" + + " Turned on manually by the user and then plugged in. Will turn back on after unplug." + ] + + STATE_OFF -> STATE_MANUAL_ON [label="manual"] + STATE_OFF -> STATE_AUTOMATIC_ON [label="Auto on AND charge <= auto threshold"] + + STATE_MANUAL_ON -> STATE_OFF [label="manual\nOR\nPlugged & sticky disabled"] + STATE_MANUAL_ON -> STATE_PENDING_STICKY_ON [label="Plugged & sticky enabled"] + + STATE_PENDING_STICKY_ON -> STATE_MANUAL_ON [label="Unplugged & sticky enabled"] + STATE_PENDING_STICKY_ON -> STATE_OFF [ + label="Sticky disabled\nOR\nSticky auto off enabled AND charge >= sticky auto off threshold" + ] + + STATE_AUTOMATIC_ON -> STATE_OFF [label="Plugged"] + STATE_AUTOMATIC_ON -> STATE_OFF_AUTOMATIC_SNOOZED [label="Manual"] + + STATE_OFF_AUTOMATIC_SNOOZED -> STATE_OFF [label="Plug\nOR\nCharge > auto threshold"] + STATE_OFF_AUTOMATIC_SNOOZED -> STATE_MANUAL_ON [label="manual"] + + </pre> + } */ public class BatterySaverStateMachine { private static final String TAG = "BatterySaverStateMachine"; @@ -60,6 +98,25 @@ public class BatterySaverStateMachine { private static final long ADAPTIVE_CHANGE_TIMEOUT_MS = 24 * 60 * 60 * 1000L; + /** Turn off adaptive battery saver if the device has charged above this level. */ + private static final int ADAPTIVE_AUTO_DISABLE_BATTERY_LEVEL = 80; + + private static final int STATE_OFF = BatterySaverStateMachineProto.STATE_OFF; + + /** Turned on manually by the user. */ + private static final int STATE_MANUAL_ON = BatterySaverStateMachineProto.STATE_MANUAL_ON; + + /** Turned on automatically by the system. */ + private static final int STATE_AUTOMATIC_ON = BatterySaverStateMachineProto.STATE_AUTOMATIC_ON; + + /** Turned off manually by the user. The system should not turn it back on automatically. */ + private static final int STATE_OFF_AUTOMATIC_SNOOZED = + BatterySaverStateMachineProto.STATE_OFF_AUTOMATIC_SNOOZED; + + /** Turned on manually by the user and then plugged in. Will turn back on after unplug. */ + private static final int STATE_PENDING_STICKY_ON = + BatterySaverStateMachineProto.STATE_PENDING_STICKY_ON; + private final Context mContext; private final BatterySaverController mBatterySaverController; @@ -75,6 +132,9 @@ public class BatterySaverStateMachine { @GuardedBy("mLock") private boolean mBatteryStatusSet; + @GuardedBy("mLock") + private int mState; + /** Whether the device is connected to any power source. */ @GuardedBy("mLock") private boolean mIsPowered; @@ -142,13 +202,6 @@ public class BatterySaverStateMachine { private boolean mDynamicPowerSavingsBatterySaver; /** - * Whether BS has been manually disabled while the battery level is low, in which case we - * shouldn't auto re-enable it until the battery level is not low. - */ - @GuardedBy("mLock") - private boolean mBatterySaverSnoozing; - - /** * Last reason passed to {@link #enableBatterySaverLocked}. */ @GuardedBy("mLock") @@ -181,6 +234,7 @@ public class BatterySaverStateMachine { mLock = lock; mContext = context; mBatterySaverController = batterySaverController; + mState = STATE_OFF; mBatterySaverStickyBehaviourDisabled = mContext.getResources().getBoolean( com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled); @@ -188,8 +242,36 @@ public class BatterySaverStateMachine { com.android.internal.R.integer.config_dynamicPowerSavingsDefaultDisableThreshold); } - private boolean isAutoBatterySaverConfiguredLocked() { - return mSettingBatterySaverTriggerThreshold > 0; + /** @return true if the automatic percentage based mode should be used */ + private boolean isAutomaticModeActiveLocked() { + return mSettingAutomaticBatterySaver == PowerManager.POWER_SAVER_MODE_PERCENTAGE + && mSettingBatterySaverTriggerThreshold > 0; + } + + /** + * The returned value won't necessarily make sense if {@link #isAutomaticModeActiveLocked()} + * returns {@code false}. + * + * @return true if the battery level is below automatic's threshold. + */ + private boolean isInAutomaticLowZoneLocked() { + return mIsBatteryLevelLow; + } + + /** @return true if the dynamic mode should be used */ + private boolean isDynamicModeActiveLocked() { + return mSettingAutomaticBatterySaver == PowerManager.POWER_SAVER_MODE_DYNAMIC + && mDynamicPowerSavingsBatterySaver; + } + + /** + * The returned value won't necessarily make sense if {@link #isDynamicModeActiveLocked()} + * returns {@code false}. + * + * @return true if the battery level is below dynamic's threshold. + */ + private boolean isInDynamicLowZoneLocked() { + return mBatteryLevel <= mDynamicPowerSavingsDisableThreshold; } /** @@ -233,7 +315,14 @@ public class BatterySaverStateMachine { Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL), false, mSettingsObserver, UserHandle.USER_SYSTEM); + synchronized (mLock) { + final boolean lowPowerModeEnabledSticky = getGlobalSetting( + Settings.Global.LOW_POWER_MODE_STICKY, 0) != 0; + + if (lowPowerModeEnabledSticky) { + mState = STATE_PENDING_STICKY_ON; + } mBootCompleted = true; @@ -362,6 +451,8 @@ public class BatterySaverStateMachine { ? "Global.low_power changed to 1" : "Global.low_power changed to 0"; enableBatterySaverLocked(/*enable=*/ batterySaverEnabled, /*manual=*/ true, BatterySaverController.REASON_SETTING_CHANGED, reason); + } else { + doAutoBatterySaverLocked(); } } @@ -428,17 +519,6 @@ public class BatterySaverStateMachine { } } - @GuardedBy("mLock") - private boolean isBatteryLowLocked() { - final boolean percentageLow = - mSettingAutomaticBatterySaver == PowerManager.POWER_SAVER_MODE_PERCENTAGE - && mIsBatteryLevelLow; - final boolean dynamicPowerSavingsLow = - mSettingAutomaticBatterySaver == PowerManager.POWER_SAVER_MODE_DYNAMIC - && mBatteryLevel <= mDynamicPowerSavingsDisableThreshold; - return percentageLow || dynamicPowerSavingsLow; - } - /** * Decide whether to auto-start / stop battery saver. */ @@ -449,7 +529,6 @@ public class BatterySaverStateMachine { + " mSettingsLoaded=" + mSettingsLoaded + " mBatteryStatusSet=" + mBatteryStatusSet + " mIsBatteryLevelLow=" + mIsBatteryLevelLow - + " mBatterySaverSnoozing=" + mBatterySaverSnoozing + " mIsPowered=" + mIsPowered + " mSettingAutomaticBatterySaver=" + mSettingAutomaticBatterySaver + " mSettingBatterySaverEnabledSticky=" + mSettingBatterySaverEnabledSticky @@ -460,66 +539,166 @@ public class BatterySaverStateMachine { return; // Not fully initialized yet. } - if (!isBatteryLowLocked()) { - updateSnoozingLocked(false, "Battery not low"); - } + updateStateLocked(false, false); + // Adaptive control. if (SystemClock.elapsedRealtime() - mLastAdaptiveBatterySaverChangedExternallyElapsed > ADAPTIVE_CHANGE_TIMEOUT_MS) { mBatterySaverController.setAdaptivePolicyEnabledLocked( false, BatterySaverController.REASON_TIMEOUT); mBatterySaverController.resetAdaptivePolicyLocked( BatterySaverController.REASON_TIMEOUT); + } else if (mIsPowered && mBatteryLevel >= ADAPTIVE_AUTO_DISABLE_BATTERY_LEVEL) { + mBatterySaverController.setAdaptivePolicyEnabledLocked(false, + BatterySaverController.REASON_PLUGGED_IN); + } + } + + /** + * Update the state machine based on the current settings and battery/charge status. + * + * @param manual Whether the change was made by the user. + * @param enable Whether the user wants to turn battery saver on or off. Is only used if {@param + * manual} is true. + */ + @GuardedBy("mLock") + private void updateStateLocked(boolean manual, boolean enable) { + if (!manual && !(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) { + return; // Not fully initialized yet. } - if (mIsPowered) { - updateSnoozingLocked(false, "Plugged in"); - enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false, - BatterySaverController.REASON_PLUGGED_IN, - "Plugged in"); + switch (mState) { + case STATE_OFF: { + if (!mIsPowered) { + if (manual) { + if (!enable) { + Slog.e(TAG, "Tried to disable BS when it's already OFF"); + return; + } + enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, + BatterySaverController.REASON_MANUAL_ON); + mState = STATE_MANUAL_ON; + } else if (isAutomaticModeActiveLocked() && isInAutomaticLowZoneLocked()) { + enableBatterySaverLocked(/*enable*/ true, /*manual*/ false, + BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_ON); + mState = STATE_AUTOMATIC_ON; + } else if (isDynamicModeActiveLocked() && isInDynamicLowZoneLocked()) { + enableBatterySaverLocked(/*enable*/ true, /*manual*/ false, + BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON); + mState = STATE_AUTOMATIC_ON; + } + } + break; + } - if (mBatteryLevel >= 80 /* Arbitrary level */) { - mBatterySaverController.setAdaptivePolicyEnabledLocked( - false, BatterySaverController.REASON_PLUGGED_IN); + case STATE_MANUAL_ON: { + if (manual) { + if (enable) { + Slog.e(TAG, "Tried to enable BS when it's already MANUAL_ON"); + return; + } + enableBatterySaverLocked(/*enable*/ false, /*manual*/ true, + BatterySaverController.REASON_MANUAL_OFF); + mState = STATE_OFF; + } else if (mIsPowered) { + enableBatterySaverLocked(/*enable*/ false, /*manual*/ false, + BatterySaverController.REASON_PLUGGED_IN); + if (mSettingBatterySaverEnabledSticky + && !mBatterySaverStickyBehaviourDisabled) { + mState = STATE_PENDING_STICKY_ON; + } else { + mState = STATE_OFF; + } + } + break; } - } else if (mSettingBatterySaverEnabledSticky && !mBatterySaverStickyBehaviourDisabled) { - if (mSettingBatterySaverStickyAutoDisableEnabled - && mBatteryLevel >= mSettingBatterySaverStickyAutoDisableThreshold) { - setStickyActive(false); - } else { - // Re-enable BS. - enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ true, - BatterySaverController.REASON_STICKY_RESTORE, - "Sticky restore"); + case STATE_AUTOMATIC_ON: { + if (mIsPowered) { + enableBatterySaverLocked(/*enable*/ false, /*manual*/ false, + BatterySaverController.REASON_PLUGGED_IN); + mState = STATE_OFF; + } else if (manual) { + if (enable) { + Slog.e(TAG, "Tried to enable BS when it's already AUTO_ON"); + return; + } + enableBatterySaverLocked(/*enable*/ false, /*manual*/ true, + BatterySaverController.REASON_MANUAL_OFF); + // When battery saver is disabled manually (while battery saver is enabled) + // when the battery level is low, we "snooze" BS -- i.e. disable auto battery + // saver. + // We resume auto-BS once the battery level is not low, or the device is + // plugged in. + mState = STATE_OFF_AUTOMATIC_SNOOZED; + } else if (isAutomaticModeActiveLocked() && !isInAutomaticLowZoneLocked()) { + enableBatterySaverLocked(/*enable*/ false, /*manual*/ false, + BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_OFF); + mState = STATE_OFF; + } else if (isDynamicModeActiveLocked() && !isInDynamicLowZoneLocked()) { + enableBatterySaverLocked(/*enable*/ false, /*manual*/ false, + BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_OFF); + mState = STATE_OFF; + } else if (!isAutomaticModeActiveLocked() && !isDynamicModeActiveLocked()) { + enableBatterySaverLocked(/*enable*/ false, /*manual*/ false, + BatterySaverController.REASON_SETTING_CHANGED); + mState = STATE_OFF; + } + break; } - } else if (mSettingAutomaticBatterySaver - == PowerManager.POWER_SAVER_MODE_PERCENTAGE - && isAutoBatterySaverConfiguredLocked()) { - if (mIsBatteryLevelLow && !mBatterySaverSnoozing) { - enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ false, - BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_ON, - "Percentage Auto ON"); - } else { - // Battery not low - enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false, - BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_OFF, - "Percentage Auto OFF"); + case STATE_OFF_AUTOMATIC_SNOOZED: { + if (manual) { + if (!enable) { + Slog.e(TAG, "Tried to disable BS when it's already AUTO_SNOOZED"); + return; + } + enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, + BatterySaverController.REASON_MANUAL_ON); + mState = STATE_MANUAL_ON; + } else if (mIsPowered // Plugging in resets snooze. + || (isAutomaticModeActiveLocked() && !isInAutomaticLowZoneLocked()) + || (isDynamicModeActiveLocked() && !isInDynamicLowZoneLocked()) + || (!isAutomaticModeActiveLocked() && !isDynamicModeActiveLocked())) { + mState = STATE_OFF; + } + break; } - } else if (mSettingAutomaticBatterySaver - == PowerManager.POWER_SAVER_MODE_DYNAMIC) { - if (mBatteryLevel >= mDynamicPowerSavingsDisableThreshold) { - enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false, - BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_OFF, - "Dynamic Warning Auto OFF"); - } else if (mDynamicPowerSavingsBatterySaver && !mBatterySaverSnoozing) { - enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ false, - BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON, - "Dynamic Warning Auto ON"); + + case STATE_PENDING_STICKY_ON: { + if (manual) { + // This shouldn't be possible. We'll only be in this state when the device is + // plugged in, so the user shouldn't be able to manually change state. + Slog.e(TAG, "Tried to manually change BS state from PENDING_STICKY_ON"); + return; + } + final boolean shouldTurnOffSticky = mSettingBatterySaverStickyAutoDisableEnabled + && mBatteryLevel >= mSettingBatterySaverStickyAutoDisableThreshold; + final boolean isStickyDisabled = + mBatterySaverStickyBehaviourDisabled || !mSettingBatterySaverEnabledSticky; + if (isStickyDisabled || shouldTurnOffSticky) { + setStickyActive(false); + mState = STATE_OFF; + } else if (!mIsPowered) { + // Re-enable BS. + enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, + BatterySaverController.REASON_STICKY_RESTORE); + mState = STATE_MANUAL_ON; + } + break; } + + default: + Slog.wtf(TAG, "Unknown state: " + mState); + break; + } + } + + @VisibleForTesting + int getState() { + synchronized (mLock) { + return mState; } - // do nothing if automatic battery saver mode = PERCENTAGE and low warning threshold = 0% } /** @@ -533,13 +712,17 @@ public class BatterySaverStateMachine { Slog.d(TAG, "setBatterySaverEnabledManually: enabled=" + enabled); } synchronized (mLock) { - enableBatterySaverLocked(/*enable=*/ enabled, /*manual=*/ true, - (enabled ? BatterySaverController.REASON_MANUAL_ON - : BatterySaverController.REASON_MANUAL_OFF), - (enabled ? "Manual ON" : "Manual OFF")); + updateStateLocked(true, enabled); + // TODO: maybe turn off adaptive if it's on and advertiseIsEnabled is true and + // enabled is false } } + @GuardedBy("mLock") + private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason) { + enableBatterySaverLocked(enable, manual, intReason, reasonToString(intReason)); + } + /** * Actually enable / disable battery saver. Write the new state to the global settings * and propagate it to {@link #mBatterySaverController}. @@ -566,20 +749,6 @@ public class BatterySaverStateMachine { mLastChangedIntReason = intReason; mLastChangedStrReason = strReason; - if (manual) { - if (enable) { - updateSnoozingLocked(false, "Manual snooze OFF"); - } else { - // When battery saver is disabled manually (while battery saver is enabled) - // when the battery level is low, we "snooze" BS -- i.e. disable auto battery saver. - // We resume auto-BS once the battery level is not low, or the device is plugged in. - if (mBatterySaverController.isFullEnabled() && isBatteryLowLocked()) { - updateSnoozingLocked(true, "Manual snooze"); - } - // TODO: maybe turn off adaptive if it's on and advertiseIsEnabled is true - } - } - mSettingBatterySaverEnabled = enable; putGlobalSetting(Settings.Global.LOW_POWER_MODE, enable ? 1 : 0); @@ -641,15 +810,6 @@ public class BatterySaverStateMachine { manager.cancel(DYNAMIC_MODE_NOTIFICATION_ID); } - @GuardedBy("mLock") - private void updateSnoozingLocked(boolean snoozing, String reason) { - if (mBatterySaverSnoozing == snoozing) { - return; - } - if (DEBUG) Slog.d(TAG, "Snooze: " + (snoozing ? "start" : "stop") + " reason=" + reason); - mBatterySaverSnoozing = snoozing; - } - private void setStickyActive(boolean active) { mSettingBatterySaverEnabledSticky = active; putGlobalSetting(Settings.Global.LOW_POWER_MODE_STICKY, @@ -684,6 +844,8 @@ public class BatterySaverStateMachine { pw.print(")"); } pw.println(); + pw.print(" mState="); + pw.println(mState); pw.print(" mLastChangedIntReason="); pw.println(mLastChangedIntReason); @@ -697,9 +859,6 @@ public class BatterySaverStateMachine { pw.print(" mBatteryStatusSet="); pw.println(mBatteryStatusSet); - pw.print(" mBatterySaverSnoozing="); - pw.println(mBatterySaverSnoozing); - pw.print(" mIsPowered="); pw.println(mIsPowered); pw.print(" mBatteryLevel="); @@ -731,6 +890,7 @@ public class BatterySaverStateMachine { proto.write(BatterySaverStateMachineProto.ENABLED, mBatterySaverController.isEnabled()); + proto.write(BatterySaverStateMachineProto.STATE, mState); proto.write(BatterySaverStateMachineProto.IS_FULL_ENABLED, mBatterySaverController.isFullEnabled()); proto.write(BatterySaverStateMachineProto.IS_ADAPTIVE_ENABLED, @@ -742,8 +902,6 @@ public class BatterySaverStateMachine { proto.write(BatterySaverStateMachineProto.SETTINGS_LOADED, mSettingsLoaded); proto.write(BatterySaverStateMachineProto.BATTERY_STATUS_SET, mBatteryStatusSet); - proto.write(BatterySaverStateMachineProto.BATTERY_SAVER_SNOOZING, - mBatterySaverSnoozing); proto.write(BatterySaverStateMachineProto.IS_POWERED, mIsPowered); proto.write(BatterySaverStateMachineProto.BATTERY_LEVEL, mBatteryLevel); diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 2b17d19e1cf7..18004336e7a2 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -19,6 +19,8 @@ import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS; import static android.os.Process.getPidsForCommands; import static android.os.Process.getUidForPid; +import static android.os.storage.VolumeInfo.TYPE_PRIVATE; +import static android.os.storage.VolumeInfo.TYPE_PUBLIC; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.server.am.MemoryStatUtil.readCmdlineFromProcfs; @@ -89,6 +91,7 @@ import android.os.UserManager; import android.os.storage.DiskInfo; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; +import android.stats.storage.StorageEnums; import android.telephony.ModemActivityInfo; import android.telephony.TelephonyManager; import android.util.ArrayMap; @@ -1968,7 +1971,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pulledData.add(e); } - private void pullSDCardInfo(int tagId, long elapsedNanos, long wallClockNanos, + private void pullExternalStorageInfo(int tagId, long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) { StorageManager storageManager = mContext.getSystemService(StorageManager.class); if (storageManager != null) { @@ -1976,11 +1979,29 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { for (VolumeInfo vol : volumes) { final String envState = VolumeInfo.getEnvironmentForState(vol.getState()); final DiskInfo diskInfo = vol.getDisk(); - if (diskInfo != null && diskInfo.isSd()) { + if (diskInfo != null) { if (envState.equals(Environment.MEDIA_MOUNTED)) { + // Get the type of the volume, if it is adoptable or portable. + int volumeType = StatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__OTHER; + if (vol.getType() == TYPE_PUBLIC) { + volumeType = StatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__PUBLIC; + } else if (vol.getType() == TYPE_PRIVATE) { + volumeType = StatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__PRIVATE; + } + // Get the type of external storage inserted in the device (sd cards, + // usb, etc) + int externalStorageType; + if (diskInfo.isSd()) { + externalStorageType = StorageEnums.SD_CARD; + } else if (diskInfo.isUsb()) { + externalStorageType = StorageEnums.USB; + } else { + externalStorageType = StorageEnums.OTHER; + } StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(vol.getType() + 1); + e.writeInt(externalStorageType); + e.writeInt(volumeType); e.writeLong(diskInfo.size); pulledData.add(e); } @@ -2185,8 +2206,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pullTimeZoneDataInfo(tagId, elapsedNanos, wallClockNanos, ret); break; } - case StatsLog.SDCARD_INFO: { - pullSDCardInfo(tagId, elapsedNanos, wallClockNanos, ret); + case StatsLog.EXTERNAL_STORAGE_INFO: { + pullExternalStorageInfo(tagId, elapsedNanos, wallClockNanos, ret); break; } default: diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 5a20959dcdbf..b262a006a545 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -352,6 +352,19 @@ public abstract class ActivityTaskManagerInternal { /** @return The intent used to launch the home activity. */ public abstract Intent getHomeIntent(); public abstract boolean startHomeActivity(int userId, String reason); + /** + * This starts home activity on displays that can have system decorations based on displayId - + * Default display always use primary home component. + * For Secondary displays, the home activity must have category SECONDARY_HOME and then resolves + * according to the priorities listed below. + * - If default home is not set, always use the secondary home defined in the config. + * - Use currently selected primary home activity. + * - Use the activity in the same package as currently selected primary home activity. + * If there are multiple activities matched, use first one. + * - Use the secondary home defined in the config. + */ + public abstract boolean startHomeOnDisplay(int userId, String reason, int displayId, + boolean fromHomeKey); /** Start home activities on all displays that support system decorations. */ public abstract boolean startHomeOnAllDisplays(int userId, String reason); /** @return true if the given process is the factory test process. */ diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index a1dbbab2036a..118eb5bea602 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -6491,6 +6491,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override + public boolean startHomeOnDisplay(int userId, String reason, int displayId, + boolean fromHomeKey) { + return mRootActivityContainer.startHomeOnDisplay(userId, reason, displayId, + fromHomeKey); + } + + @Override public boolean startHomeOnAllDisplays(int userId, String reason) { synchronized (mGlobalLock) { return mRootActivityContainer.startHomeOnAllDisplays(userId, reason); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index bcfbefe95479..b00cafdccf11 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3284,7 +3284,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } private void updateImeParent() { - final SurfaceControl newParent = computeImeParent(); + // Force attaching IME to the display when magnifying, or it would be magnified with + // target app together. + final boolean shouldAttachToDisplay = (mMagnificationSpec != null); + final SurfaceControl newParent = + shouldAttachToDisplay ? mWindowingLayer : computeImeParent(); if (newParent != null) { mPendingTransaction.reparent(mImeWindowsContainers.mSurfaceControl, newParent); scheduleAnimation(); @@ -4699,6 +4703,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } else { mMagnificationSpec = null; } + // Re-parent IME's SurfaceControl when MagnificationSpec changed. + updateImeParent(); applyMagnificationSpec(getPendingTransaction(), spec); getPendingTransaction().apply(); diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java index f964b5753488..24cf7f127e18 100644 --- a/services/core/java/com/android/server/wm/RootActivityContainer.java +++ b/services/core/java/com/android/server/wm/RootActivityContainer.java @@ -114,6 +114,7 @@ import com.android.server.LocalServices; import com.android.server.am.ActivityManagerService; import com.android.server.am.AppTimeTracker; import com.android.server.am.UserState; +import com.android.server.policy.WindowManagerPolicy; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -345,6 +346,10 @@ class RootActivityContainer extends ConfigurationContainer } } + boolean startHomeOnDisplay(int userId, String reason, int displayId) { + return startHomeOnDisplay(userId, reason, displayId, false /*fromHomeKey*/); + } + /** * This starts home activity on displays that can have system decorations based on displayId - * Default display always use primary home component. @@ -356,7 +361,12 @@ class RootActivityContainer extends ConfigurationContainer * If there are multiple activities matched, use first one. * - Use the secondary home defined in the config. */ - boolean startHomeOnDisplay(int userId, String reason, int displayId) { + boolean startHomeOnDisplay(int userId, String reason, int displayId, boolean fromHomeKey) { + // Fallback to top focused display if the displayId is invalid. + if (displayId == INVALID_DISPLAY) { + displayId = getTopDisplayFocusedStack().mDisplayId; + } + Intent homeIntent; ActivityInfo aInfo; if (displayId == DEFAULT_DISPLAY) { @@ -380,6 +390,10 @@ class RootActivityContainer extends ConfigurationContainer // Updates the home component of the intent. homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name)); homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK); + // Updates the extra information of the intent. + if (fromHomeKey) { + homeIntent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, true); + } // Update the reason for ANR debugging to verify if the user activity is the one that // actually launched. final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId( diff --git a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java index bdb76c27c668..bd4e542033c5 100644 --- a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java +++ b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java @@ -83,7 +83,12 @@ class SystemGesturesPointerEventListener implements PointerEventListener { } public void systemReady() { - mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), mHandler); + // GestureDetector records statistics about gesture classification events to inform gesture + // usage trends. SystemGesturesPointerEventListener creates a lot of noise in these + // statistics because it passes every touch event though a GestureDetector. By creating an + // anonymous subclass of GestureDetector, these statistics will be recorded with a unique + // source name that can be filtered. + mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), mHandler) {}; } @Override diff --git a/services/ipmemorystore/Android.bp b/services/ipmemorystore/Android.bp deleted file mode 100644 index 013cf5616904..000000000000 --- a/services/ipmemorystore/Android.bp +++ /dev/null @@ -1,4 +0,0 @@ -java_library_static { - name: "services.ipmemorystore", - srcs: ["java/**/*.java"], -} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 3f323d98a303..39af565d156f 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -108,7 +108,6 @@ import com.android.server.media.MediaSessionService; import com.android.server.media.projection.MediaProjectionManagerService; import com.android.server.net.NetworkPolicyManagerService; import com.android.server.net.NetworkStatsService; -import com.android.server.net.ipmemorystore.IpMemoryStoreService; import com.android.server.net.watchlist.NetworkWatchlistService; import com.android.server.notification.NotificationManagerService; import com.android.server.oemlock.OemLockService; @@ -1264,14 +1263,6 @@ public final class SystemServer { } traceEnd(); - traceBeginAndSlog("StartIpMemoryStoreService"); - try { - ServiceManager.addService(Context.IP_MEMORY_STORE_SERVICE, - new IpMemoryStoreService(context)); - } catch (Throwable e) { - reportWtf("starting IP Memory Store Service", e); - } - traceEnd(); traceBeginAndSlog("StartIpSecService"); try { diff --git a/services/net/Android.bp b/services/net/Android.bp index 8ad4d7679107..486d15d918bf 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -7,6 +7,19 @@ java_library_static { ] } +java_library_static { + name: "ipmemorystore-client", + sdk_version: "system_current", + srcs: [ + ":framework-annotations", + "java/android/net/IpMemoryStoreClient.java", + "java/android/net/ipmemorystore/**.java", + ], + static_libs: [ + "ipmemorystore-aidl-interfaces-java", + ] +} + filegroup { name: "services-networkstack-shared-srcs", srcs: [ diff --git a/services/net/java/android/net/IpMemoryStore.java b/services/net/java/android/net/IpMemoryStore.java new file mode 100644 index 000000000000..9248299e178d --- /dev/null +++ b/services/net/java/android/net/IpMemoryStore.java @@ -0,0 +1,62 @@ +/* + * 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; + +import android.annotation.NonNull; +import android.content.Context; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +/** + * Manager class used to communicate with the ip memory store service in the network stack, + * which is running in a separate module. + * @hide +*/ +public class IpMemoryStore extends IpMemoryStoreClient { + private final CompletableFuture<IIpMemoryStore> mService; + + public IpMemoryStore(@NonNull final Context context) { + super(context); + mService = new CompletableFuture<>(); + getNetworkStackClient().fetchIpMemoryStore( + new IIpMemoryStoreCallbacks.Stub() { + @Override + public void onIpMemoryStoreFetched(final IIpMemoryStore memoryStore) { + mService.complete(memoryStore); + } + }); + } + + @Override + protected IIpMemoryStore getService() throws InterruptedException, ExecutionException { + return mService.get(); + } + + @VisibleForTesting + protected NetworkStackClient getNetworkStackClient() { + return NetworkStackClient.getInstance(); + } + + /** Gets an instance of the memory store */ + @NonNull + public static IpMemoryStore getMemoryStore(final Context context) { + return new IpMemoryStore(context); + } +} diff --git a/core/java/android/net/IpMemoryStore.java b/services/net/java/android/net/IpMemoryStoreClient.java index 2f4d9bc6a89a..2f4fdbd8a4a7 100644 --- a/core/java/android/net/IpMemoryStore.java +++ b/services/net/java/android/net/IpMemoryStoreClient.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ package android.net; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SystemService; import android.content.Context; import android.net.ipmemorystore.Blob; import android.net.ipmemorystore.IOnBlobRetrievedListener; @@ -27,23 +26,34 @@ import android.net.ipmemorystore.IOnNetworkAttributesRetrieved; import android.net.ipmemorystore.IOnSameNetworkResponseListener; import android.net.ipmemorystore.IOnStatusListener; import android.net.ipmemorystore.NetworkAttributes; +import android.net.ipmemorystore.Status; +import android.net.ipmemorystore.StatusParcelable; import android.os.RemoteException; +import android.util.Log; -import com.android.internal.util.Preconditions; +import java.util.concurrent.ExecutionException; /** - * The interface for system components to access the IP memory store. - * @see com.android.server.net.ipmemorystore.IpMemoryStoreService + * service used to communicate with the ip memory store service in network stack, + * which is running in a separate module. * @hide */ -@SystemService(Context.IP_MEMORY_STORE_SERVICE) -public class IpMemoryStore { - @NonNull final Context mContext; - @NonNull final IIpMemoryStore mService; +public abstract class IpMemoryStoreClient { + private static final String TAG = IpMemoryStoreClient.class.getSimpleName(); + private final Context mContext; - public IpMemoryStore(@NonNull final Context context, @NonNull final IIpMemoryStore service) { - mContext = Preconditions.checkNotNull(context, "missing context"); - mService = Preconditions.checkNotNull(service, "missing IIpMemoryStore"); + public IpMemoryStoreClient(@NonNull final Context context) { + if (context == null) throw new IllegalArgumentException("missing context"); + mContext = context; + } + + @NonNull + protected abstract IIpMemoryStore getService() throws InterruptedException, ExecutionException; + + protected StatusParcelable internalErrorStatus() { + final StatusParcelable error = new StatusParcelable(); + error.resultCode = Status.ERROR_UNKNOWN; + return error; } /** @@ -66,9 +76,13 @@ public class IpMemoryStore { @NonNull final NetworkAttributes attributes, @Nullable final IOnStatusListener listener) { try { - mService.storeNetworkAttributes(l2Key, attributes.toParcelable(), listener); + try { + getService().storeNetworkAttributes(l2Key, attributes.toParcelable(), listener); + } catch (InterruptedException | ExecutionException m) { + listener.onComplete(internalErrorStatus()); + } } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + Log.e(TAG, "Error storing network attributes", e); } } @@ -87,9 +101,13 @@ public class IpMemoryStore { @NonNull final String name, @NonNull final Blob data, @Nullable final IOnStatusListener listener) { try { - mService.storeBlob(l2Key, clientId, name, data, listener); + try { + getService().storeBlob(l2Key, clientId, name, data, listener); + } catch (InterruptedException | ExecutionException m) { + listener.onComplete(internalErrorStatus()); + } } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + Log.e(TAG, "Error storing blob", e); } } @@ -110,9 +128,13 @@ public class IpMemoryStore { public void findL2Key(@NonNull final NetworkAttributes attributes, @NonNull final IOnL2KeyResponseListener listener) { try { - mService.findL2Key(attributes.toParcelable(), listener); + try { + getService().findL2Key(attributes.toParcelable(), listener); + } catch (InterruptedException | ExecutionException m) { + listener.onL2KeyResponse(internalErrorStatus(), null); + } } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + Log.e(TAG, "Error finding L2 Key", e); } } @@ -128,9 +150,13 @@ public class IpMemoryStore { public void isSameNetwork(@NonNull final String l2Key1, @NonNull final String l2Key2, @NonNull final IOnSameNetworkResponseListener listener) { try { - mService.isSameNetwork(l2Key1, l2Key2, listener); + try { + getService().isSameNetwork(l2Key1, l2Key2, listener); + } catch (InterruptedException | ExecutionException m) { + listener.onSameNetworkResponse(internalErrorStatus(), null); + } } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + Log.e(TAG, "Error checking for network sameness", e); } } @@ -146,9 +172,13 @@ public class IpMemoryStore { public void retrieveNetworkAttributes(@NonNull final String l2Key, @NonNull final IOnNetworkAttributesRetrieved listener) { try { - mService.retrieveNetworkAttributes(l2Key, listener); + try { + getService().retrieveNetworkAttributes(l2Key, listener); + } catch (InterruptedException | ExecutionException m) { + listener.onNetworkAttributesRetrieved(internalErrorStatus(), null, null); + } } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + Log.e(TAG, "Error retrieving network attributes", e); } } @@ -166,14 +196,13 @@ public class IpMemoryStore { public void retrieveBlob(@NonNull final String l2Key, @NonNull final String clientId, @NonNull final String name, @NonNull final IOnBlobRetrievedListener listener) { try { - mService.retrieveBlob(l2Key, clientId, name, listener); + try { + getService().retrieveBlob(l2Key, clientId, name, listener); + } catch (InterruptedException | ExecutionException m) { + listener.onBlobRetrieved(internalErrorStatus(), null, null, null); + } } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + Log.e(TAG, "Error retrieving blob", e); } } - - /** Gets an instance of the memory store */ - public static IpMemoryStore getMemoryStore(final Context context) { - return (IpMemoryStore) context.getSystemService(Context.IP_MEMORY_STORE_SERVICE); - } } diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java index cc09fe305070..7befd0870c00 100644 --- a/services/net/java/android/net/NetworkStackClient.java +++ b/services/net/java/android/net/NetworkStackClient.java @@ -130,6 +130,21 @@ public class NetworkStackClient { }); } + /** + * Get an instance of the IpMemoryStore. + * + * <p>The IpMemoryStore will be returned asynchronously through the provided callbacks. + */ + public void fetchIpMemoryStore(IIpMemoryStoreCallbacks cb) { + requestConnector(connector -> { + try { + connector.fetchIpMemoryStore(cb); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + }); + } + private class NetworkStackConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { diff --git a/core/java/android/net/TcpKeepalivePacketData.java b/services/net/java/android/net/TcpKeepalivePacketData.java index 99d36c504e5c..398a6b31cbce 100644 --- a/core/java/android/net/TcpKeepalivePacketData.java +++ b/services/net/java/android/net/TcpKeepalivePacketData.java @@ -167,8 +167,9 @@ public class TcpKeepalivePacketData extends KeepalivePacketData implements Parce tcpWndScale); } - /* Parcelable Implementation. */ - /* Note that this object implements parcelable (and needs to keep doing this as it inherits + /** + * Parcelable Implementation. + * Note that this object implements parcelable (and needs to keep doing this as it inherits * from a class that does), but should usually be parceled as a stable parcelable using * the toStableParcelable() and fromStableParcelable() methods. */ @@ -194,7 +195,7 @@ public class TcpKeepalivePacketData extends KeepalivePacketData implements Parce } /** Parcelable Creator. */ - public static final @android.annotation.NonNull Parcelable.Creator<TcpKeepalivePacketData> CREATOR = + public static final @NonNull Parcelable.Creator<TcpKeepalivePacketData> CREATOR = new Parcelable.Creator<TcpKeepalivePacketData>() { public TcpKeepalivePacketData createFromParcel(Parcel in) { return new TcpKeepalivePacketData(in); diff --git a/core/java/android/net/ipmemorystore/NetworkAttributes.java b/services/net/java/android/net/ipmemorystore/NetworkAttributes.java index 6a9eae00e3ff..6a9eae00e3ff 100644 --- a/core/java/android/net/ipmemorystore/NetworkAttributes.java +++ b/services/net/java/android/net/ipmemorystore/NetworkAttributes.java diff --git a/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java b/services/net/java/android/net/ipmemorystore/SameL3NetworkResponse.java index 291aca8fc611..291aca8fc611 100644 --- a/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java +++ b/services/net/java/android/net/ipmemorystore/SameL3NetworkResponse.java diff --git a/core/java/android/net/ipmemorystore/Status.java b/services/net/java/android/net/ipmemorystore/Status.java index cacd42d713c2..13242c03ce01 100644 --- a/core/java/android/net/ipmemorystore/Status.java +++ b/services/net/java/android/net/ipmemorystore/Status.java @@ -32,6 +32,7 @@ public class Status { public static final int ERROR_ILLEGAL_ARGUMENT = -2; public static final int ERROR_DATABASE_CANNOT_BE_OPENED = -3; public static final int ERROR_STORAGE = -4; + public static final int ERROR_UNKNOWN = -5; public final int resultCode; diff --git a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java index f17a9fe48c61..f4cea7ae86a6 100644 --- a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java +++ b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java @@ -326,15 +326,14 @@ public class ActiveRestoreSessionTest { } @Test - public void testRestoreSome_for2Packages() throws Exception { + public void testRestorePackages_for2Packages() throws Exception { mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP); TransportMock transportMock = setUpTransport(mTransport); IRestoreSession restoreSession = createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1); - int result = - restoreSession.restoreSome( - TOKEN_1, mObserver, mMonitor, new String[] {PACKAGE_1, PACKAGE_2}); + int result = restoreSession.restorePackages(TOKEN_1, mObserver, + new String[] {PACKAGE_1, PACKAGE_2}, mMonitor); mShadowBackupLooper.runToEndOfTasks(); assertThat(result).isEqualTo(0); @@ -349,27 +348,27 @@ public class ActiveRestoreSessionTest { } @Test - public void testRestoreSome_for2Packages_createsSystemRestoreTask() throws Exception { + public void testRestorePackages_for2Packages_createsSystemRestoreTask() throws Exception { mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP); setUpTransport(mTransport); IRestoreSession restoreSession = createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1); - restoreSession.restoreSome( - TOKEN_1, mObserver, mMonitor, new String[] {PACKAGE_1, PACKAGE_2}); + restoreSession.restorePackages(TOKEN_1, mObserver, new String[] {PACKAGE_1, PACKAGE_2}, + mMonitor); mShadowBackupLooper.runToEndOfTasks(); assertThat(ShadowPerformUnifiedRestoreTask.getLastCreated().isFullSystemRestore()).isTrue(); } @Test - public void testRestoreSome_for1Package() throws Exception { + public void testRestorePackages_for1Package() throws Exception { mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP); setUpTransport(mTransport); IRestoreSession restoreSession = createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1); - restoreSession.restoreSome(TOKEN_1, mObserver, mMonitor, new String[] {PACKAGE_1}); + restoreSession.restorePackages(TOKEN_1, mObserver, new String[] {PACKAGE_1}, mMonitor); mShadowBackupLooper.runToEndOfTasks(); ShadowPerformUnifiedRestoreTask shadowTask = @@ -379,13 +378,13 @@ public class ActiveRestoreSessionTest { } @Test - public void testRestoreSome_for1Package_createsNonSystemRestoreTask() throws Exception { + public void testRestorePackages_for1Package_createsNonSystemRestoreTask() throws Exception { mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP); setUpTransport(mTransport); IRestoreSession restoreSession = createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1); - restoreSession.restoreSome(TOKEN_1, mObserver, mMonitor, new String[] {PACKAGE_1}); + restoreSession.restorePackages(TOKEN_1, mObserver, new String[] {PACKAGE_1}, mMonitor); mShadowBackupLooper.runToEndOfTasks(); assertThat(ShadowPerformUnifiedRestoreTask.getLastCreated().isFullSystemRestore()) @@ -393,32 +392,32 @@ public class ActiveRestoreSessionTest { } @Test - public void testRestoreSome_whenNoRestoreSets() throws Exception { + public void testRestorePackages_whenNoRestoreSets() throws Exception { mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP); setUpTransport(mTransport); IRestoreSession restoreSession = createActiveRestoreSession(null, mTransport); - int result = - restoreSession.restoreSome(TOKEN_1, mObserver, mMonitor, new String[] {PACKAGE_1}); + int result = restoreSession.restorePackages(TOKEN_1, mObserver, new String[] {PACKAGE_1}, + mMonitor); assertThat(result).isEqualTo(-1); } @Test - public void testRestoreSome_whenSinglePackageSession() throws Exception { + public void testRestorePackages_whenSinglePackageSession() throws Exception { mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP); setUpTransport(mTransport); IRestoreSession restoreSession = createActiveRestoreSessionWithRestoreSets(PACKAGE_1, mTransport, mRestoreSet1); - int result = - restoreSession.restoreSome(TOKEN_1, mObserver, mMonitor, new String[] {PACKAGE_2}); + int result = restoreSession.restorePackages(TOKEN_1, mObserver, new String[] {PACKAGE_2}, + mMonitor); assertThat(result).isEqualTo(-1); } @Test - public void testRestoreSome_whenSessionEnded() throws Exception { + public void testRestorePackages_whenSessionEnded() throws Exception { mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP); setUpTransport(mTransport); IRestoreSession restoreSession = @@ -429,19 +428,19 @@ public class ActiveRestoreSessionTest { expectThrows( IllegalStateException.class, () -> - restoreSession.restoreSome( - TOKEN_1, mObserver, mMonitor, new String[] {PACKAGE_1})); + restoreSession.restorePackages(TOKEN_1, mObserver, new String[] {PACKAGE_1}, + mMonitor)); } @Test - public void testRestoreSome_whenTransportNotRegistered() throws Exception { + public void testRestorePackages_whenTransportNotRegistered() throws Exception { mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP); setUpTransport(mTransport.unregistered()); IRestoreSession restoreSession = createActiveRestoreSessionWithRestoreSets(null, mTransport, mRestoreSet1); - int result = - restoreSession.restoreSome(TOKEN_1, mObserver, mMonitor, new String[] {PACKAGE_1}); + int result = restoreSession.restorePackages(TOKEN_1, mObserver, new String[] {PACKAGE_1}, + mMonitor); assertThat(result).isEqualTo(-1); } diff --git a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java index 86e859809ffd..2e5efbd1deef 100644 --- a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java @@ -28,6 +28,7 @@ import android.app.NotificationManager; import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; +import android.os.PowerManager; import android.provider.Settings.Global; import androidx.test.filters.SmallTest; @@ -452,6 +453,21 @@ public class BatterySaverStateMachineTest { assertEquals(true, mDevice.batterySaverEnabled); assertEquals(30, mPersistedState.batteryLevel); assertEquals(true, mPersistedState.batteryLow); + + // Disable auto battery saver. + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); + mDevice.setBatteryLevel(25); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(25, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + // PowerManager sets batteryLow to true at 15% if battery saver trigger level is lower. + mDevice.setBatteryLevel(15); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(15, mPersistedState.batteryLevel); + assertEquals(true, mPersistedState.batteryLow); } @Test @@ -542,6 +558,12 @@ public class BatterySaverStateMachineTest { assertEquals(100, mPersistedState.batteryLevel); assertEquals(false, mPersistedState.batteryLow); + mDevice.setBatteryLevel(97); + + assertEquals(true, mDevice.batterySaverEnabled); // Stays on. + assertEquals(97, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + mDevice.setBatteryLevel(95); assertEquals(true, mDevice.batterySaverEnabled); // Stays on. @@ -719,6 +741,48 @@ public class BatterySaverStateMachineTest { } @Test + public void testAutoBatterySaver_withSticky_withAutoOffToggled() { + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50); + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 1); + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, 90); + + // Scenario 1: User turns BS on manually above the threshold, it shouldn't turn off even + // with battery level change above threshold. + mDevice.setBatteryLevel(100); + mTarget.setBatterySaverEnabledManually(true); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(100, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + mDevice.setBatteryLevel(95); + + assertEquals(true, mDevice.batterySaverEnabled); // Stays on. + assertEquals(95, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + // Disable auto disable while in the pending sticky state. BS should reactivate after + // unplug. + mDevice.setPowered(true); + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 0); + mDevice.setPowered(false); + + assertEquals(true, mDevice.batterySaverEnabled); // Sticky BS should activate. + assertEquals(95, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + // Enable auto disable while in the pending sticky state. Sticky should turn off after + // unplug. + mDevice.setPowered(true); + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 1); + mDevice.setPowered(false); + + assertEquals(false, mDevice.batterySaverEnabled); // Sticky BS no longer enabled. + assertEquals(95, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + } + + @Test public void testAutoBatterySaver_withStickyDisabled() { when(mMockResources.getBoolean( com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled)) @@ -739,7 +803,9 @@ public class BatterySaverStateMachineTest { assertEquals(30, mPersistedState.batteryLevel); assertEquals(true, mPersistedState.batteryLow); + mDevice.setPowered(true); mDevice.setBatteryLevel(80); + mDevice.setPowered(false); assertEquals(false, mDevice.batterySaverEnabled); // Not sticky. assertEquals(80, mPersistedState.batteryLevel); @@ -830,10 +896,9 @@ public class BatterySaverStateMachineTest { assertEquals(90, mPersistedState.batteryLevel); assertEquals(false, mPersistedState.batteryLow); - // Reboot -- setting BS from adb is also sticky. + // Reboot -- LOW_POWER_MODE shouldn't be persisted. initDevice(); - - assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(false, mDevice.batterySaverEnabled); assertEquals(90, mPersistedState.batteryLevel); assertEquals(false, mPersistedState.batteryLow); } @@ -841,7 +906,8 @@ public class BatterySaverStateMachineTest { @Test public void testAutoBatterySaver_smartBatterySaverEnabled() { mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 50); - mDevice.putGlobalSetting(Global.AUTOMATIC_POWER_SAVER_MODE, 1); + mDevice.putGlobalSetting(Global.AUTOMATIC_POWER_SAVER_MODE, + PowerManager.POWER_SAVER_MODE_DYNAMIC); mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_ENABLED, 0); assertEquals(false, mDevice.batterySaverEnabled); @@ -922,8 +988,8 @@ public class BatterySaverStateMachineTest { mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 71); mDevice.setBatteryLevel(mPersistedState.batteryLevel); - // changes are only registered if some battery level changed - assertEquals(false, mDevice.batterySaverEnabled); + // Changes should register immediately. + assertEquals(true, mDevice.batterySaverEnabled); assertEquals(70, mPersistedState.batteryLevel); mDevice.setBatteryLevel(69); @@ -935,8 +1001,8 @@ public class BatterySaverStateMachineTest { mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 60); mDevice.setBatteryLevel(mPersistedState.batteryLevel); - // changes are only registered if battery level changed - assertEquals(true, mDevice.batterySaverEnabled); + // Changes should register immediately. + assertEquals(false, mDevice.batterySaverEnabled); assertEquals(69, mPersistedState.batteryLevel); mDevice.setBatteryLevel(68); @@ -956,4 +1022,220 @@ public class BatterySaverStateMachineTest { assertEquals(true, mDevice.batterySaverEnabled); assertEquals(30, mPersistedState.batteryLevel); } + + @Test + public void testAutoBatterySaver_snoozed_autoEnabled() { + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 30); + // Test dynamic threshold higher than automatic to make sure it doesn't interfere when it's + // not enabled. + mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 50); + mDevice.putGlobalSetting(Global.AUTOMATIC_POWER_SAVER_MODE, + PowerManager.POWER_SAVER_MODE_PERCENTAGE); + mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_ENABLED, 0); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(100, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(90); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(90, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(51); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(51, mPersistedState.batteryLevel); + + // Hit dynamic threshold. BS should be disabled since dynamic is off + mDevice.setBatteryLevel(50); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(50, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(30); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(30, mPersistedState.batteryLevel); + + mDevice.setPowered(true); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(30, mPersistedState.batteryLevel); + + mDevice.setPowered(false); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(30, mPersistedState.batteryLevel); + + mTarget.setBatterySaverEnabledManually(false); // Manually disable -> snooze. + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(30, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(20); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(20, mPersistedState.batteryLevel); + + // Lower threshold. Level is still below, so should still be snoozed. + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 25); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(20, mPersistedState.batteryLevel); + + // Lower threshold even more. Battery no longer considered "low" so snoozing should be + // disabled. + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 10); + // "batteryLow" is set in setBatteryLevel. + mDevice.setBatteryLevel(19); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(19, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(10); + + assertEquals(true, mDevice.batterySaverEnabled); // No longer snoozing. + assertEquals(10, mPersistedState.batteryLevel); + + mTarget.setBatterySaverEnabledManually(false); // Manually disable -> snooze. + + // Plug in and out, snooze will reset. + mDevice.setPowered(true); + mDevice.setPowered(false); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(10, mPersistedState.batteryLevel); + + mDevice.setPowered(true); + mDevice.setBatteryLevel(60); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(60, mPersistedState.batteryLevel); + + // Test toggling resets snooze. + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50); + mDevice.setPowered(false); + mDevice.setBatteryLevel(45); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(45, mPersistedState.batteryLevel); + + mTarget.setBatterySaverEnabledManually(false); // Manually disable -> snooze. + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(45, mPersistedState.batteryLevel); + + // Disable and re-enable. + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50); + + assertEquals(true, mDevice.batterySaverEnabled); // Snooze reset + assertEquals(45, mPersistedState.batteryLevel); + } + + @Test + public void testAutoBatterySaver_snoozed_dynamicEnabled() { + // Test auto threshold higher than dynamic to make sure it doesn't interfere when it's + // not enabled. + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50); + mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 30); + mDevice.putGlobalSetting(Global.AUTOMATIC_POWER_SAVER_MODE, + PowerManager.POWER_SAVER_MODE_DYNAMIC); + mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_ENABLED, 1); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(100, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(90); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(90, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(51); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(51, mPersistedState.batteryLevel); + + // Hit automatic threshold. BS should be disabled since automatic is off + mDevice.setBatteryLevel(50); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(50, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(30); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(30, mPersistedState.batteryLevel); + + mDevice.setPowered(true); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(30, mPersistedState.batteryLevel); + + mDevice.setPowered(false); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(30, mPersistedState.batteryLevel); + + mTarget.setBatterySaverEnabledManually(false); // Manually disable -> snooze. + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(30, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(20); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(20, mPersistedState.batteryLevel); + + // Lower threshold. Level is still below, so should still be snoozed. + mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 25); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(20, mPersistedState.batteryLevel); + + // Lower threshold even more. Battery no longer considered "low" so snoozing should be + // disabled. + mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 10); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(20, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(10); + + assertEquals(true, mDevice.batterySaverEnabled); // No longer snoozing. + assertEquals(10, mPersistedState.batteryLevel); + + mTarget.setBatterySaverEnabledManually(false); // Manually disable -> snooze. + + // Plug in and out, snooze will reset. + mDevice.setPowered(true); + mDevice.setPowered(false); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(10, mPersistedState.batteryLevel); + + mDevice.setPowered(true); + mDevice.setBatteryLevel(60); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(60, mPersistedState.batteryLevel); + + // Test toggling resets snooze. + mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 50); + mDevice.setPowered(false); + mDevice.setBatteryLevel(45); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(45, mPersistedState.batteryLevel); + + mTarget.setBatterySaverEnabledManually(false); // Manually disable -> snooze. + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(45, mPersistedState.batteryLevel); + + // Disable and re-enable. + mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_ENABLED, 0); + mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_ENABLED, 1); + + assertEquals(true, mDevice.batterySaverEnabled); // Snooze reset + assertEquals(45, mPersistedState.batteryLevel); + } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java index 775152587166..6a07a45ed360 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java @@ -338,12 +338,15 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { initializeCredentialUnderSP(password, PRIMARY_USER_ID); final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); + assertFalse(mService.hasPendingEscrowToken(PRIMARY_USER_ID)); long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null); assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); + assertTrue(mService.hasPendingEscrowToken(PRIMARY_USER_ID)); mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode(); assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); + assertFalse(mService.hasPendingEscrowToken(PRIMARY_USER_ID)); mLocalService.setLockCredentialWithToken(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, handle, token, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID); diff --git a/services/tests/servicestests/src/com/android/server/pm/LauncherAppsServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/LauncherAppsServiceTest.java deleted file mode 100644 index d7dc58dbc213..000000000000 --- a/services/tests/servicestests/src/com/android/server/pm/LauncherAppsServiceTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.pm; - -import static org.junit.Assert.assertEquals; - -import android.content.pm.PackageParser; -import android.content.pm.Signature; -import android.content.pm.SigningInfo; -import android.platform.test.annotations.Presubmit; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; - -@Presubmit -@RunWith(MockitoJUnitRunner.class) -public class LauncherAppsServiceTest { - - private static final Signature SIGNATURE_1 = new Signature(new byte[]{0x00, 0x01, 0x02, 0x03}); - private static final Signature SIGNATURE_2 = new Signature(new byte[]{0x04, 0x05, 0x06, 0x07}); - private static final Signature SIGNATURE_3 = new Signature(new byte[]{0x08, 0x09, 0x10, 0x11}); - - @Test - public void testComputePackageCertDigest() { - String digest = LauncherAppsService.LauncherAppsImpl.computePackageCertDigest(SIGNATURE_1); - assertEquals("A02A05B025B928C039CF1AE7E8EE04E7C190C0DB", digest); - } - - @Test - public void testGetLatestSignaturesWithSingleCert() { - SigningInfo signingInfo = new SigningInfo( - new PackageParser.SigningDetails( - new Signature[]{SIGNATURE_1}, - PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, - null, - null)); - Signature[] signatures = LauncherAppsService.LauncherAppsImpl.getLatestSignatures( - signingInfo); - assertEquals(1, signatures.length); - assertEquals(SIGNATURE_1, signatures[0]); - } - - @Test - public void testGetLatestSignaturesWithMultiCert() { - SigningInfo signingInfo = new SigningInfo( - new PackageParser.SigningDetails( - new Signature[]{SIGNATURE_1, SIGNATURE_2}, - PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, - null, - null)); - Signature[] signatures = LauncherAppsService.LauncherAppsImpl.getLatestSignatures( - signingInfo); - assertEquals(2, signatures.length); - assertEquals(SIGNATURE_1, signatures[0]); - assertEquals(SIGNATURE_2, signatures[1]); - } - - @Test - public void testGetLatestSignaturesWithCertHistory() { - SigningInfo signingInfo = new SigningInfo( - new PackageParser.SigningDetails( - new Signature[]{SIGNATURE_1}, - PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, - null, - new Signature[]{SIGNATURE_2, SIGNATURE_3})); - Signature[] signatures = LauncherAppsService.LauncherAppsImpl.getLatestSignatures( - signingInfo); - assertEquals(1, signatures.length); - assertEquals(SIGNATURE_2, signatures[0]); - } - -} diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java index 95043810128a..cd095a5fa6ff 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java @@ -250,7 +250,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setRank(123) .setPerson(makePerson("person", "personKey", "personUri")) - .setLongLived() + .setLongLived(true) .setExtras(pb) .build(); si.addFlags(ShortcutInfo.FLAG_PINNED); @@ -352,7 +352,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(123) .setPerson(makePerson("person", "personKey", "personUri")) - .setLongLived() + .setLongLived(true) .setExtras(pb) .build(); sorig.addFlags(ShortcutInfo.FLAG_PINNED); 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 4626840e6263..6f967593e6f1 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -37,6 +37,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; import static android.content.pm.PackageManager.FEATURE_WATCH; import static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Build.VERSION_CODES.O_MR1; import static android.os.Build.VERSION_CODES.P; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; @@ -111,6 +112,7 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableContext; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; +import android.testing.TestablePermissions; import android.text.Html; import android.util.ArrayMap; import android.util.ArraySet; @@ -4171,6 +4173,25 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testCanNotifyAsUser_crossUser() throws Exception { + // same user no problem + mBinderService.canNotifyAsPackage("src", "target", mContext.getUserId()); + + // cross user, no permission, problem + try { + mBinderService.canNotifyAsPackage("src", "target", mContext.getUserId() + 1); + fail("Should not be callable cross user without cross user permission"); + } catch (SecurityException e) { + // good + } + + // cross user, with permission, no problem + TestablePermissions perms = mContext.getTestablePermissions(); + perms.setPermission(android.Manifest.permission.INTERACT_ACROSS_USERS, PERMISSION_GRANTED); + mBinderService.canNotifyAsPackage("src", "target", mContext.getUserId() + 1); + } + + @Test public void setDefaultAssistantForUser_fromConfigXml() { clearDeviceConfig(); ComponentName xmlConfig = new ComponentName("config", "xml"); diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index dcaa49996d0b..c05a3461e26e 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -144,6 +144,16 @@ public final class Call { public static final String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS = "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS"; + + /** + * Extra key used to indicate whether a {@link CallScreeningService} has requested to silence + * the ringtone for a call. If the {@link InCallService} declares + * {@link TelecomManager#METADATA_IN_CALL_SERVICE_RINGING} in its manifest, it should not + * play a ringtone for an incoming call with this extra key set. + */ + public static final String EXTRA_SILENT_RINGING_REQUESTED = + "android.telecom.extra.SILENT_RINGING_REQUESTED"; + /** * Call event sent from a {@link Call} via {@link #sendCallEvent(String, Bundle)} to inform * Telecom that the user has requested that the current {@link Call} should be handed over diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java index 2fa388fa037e..b1aece7fbe81 100644 --- a/telecomm/java/android/telecom/CallScreeningService.java +++ b/telecomm/java/android/telecom/CallScreeningService.java @@ -259,12 +259,14 @@ public abstract class CallScreeningService extends Service { public static class CallResponse { private final boolean mShouldDisallowCall; private final boolean mShouldRejectCall; + private final boolean mShouldSilenceCall; private final boolean mShouldSkipCallLog; private final boolean mShouldSkipNotification; private CallResponse( boolean shouldDisallowCall, boolean shouldRejectCall, + boolean shouldSilenceCall, boolean shouldSkipCallLog, boolean shouldSkipNotification) { if (!shouldDisallowCall @@ -276,6 +278,7 @@ public abstract class CallScreeningService extends Service { mShouldRejectCall = shouldRejectCall; mShouldSkipCallLog = shouldSkipCallLog; mShouldSkipNotification = shouldSkipNotification; + mShouldSilenceCall = shouldSilenceCall; } /* @@ -294,6 +297,13 @@ public abstract class CallScreeningService extends Service { } /* + * @return Whether the ringtone should be silenced for the incoming call. + */ + public boolean getSilenceCall() { + return mShouldSilenceCall; + } + + /* * @return Whether the incoming call should not be displayed in the call log. */ public boolean getSkipCallLog() { @@ -310,6 +320,7 @@ public abstract class CallScreeningService extends Service { public static class Builder { private boolean mShouldDisallowCall; private boolean mShouldRejectCall; + private boolean mShouldSilenceCall; private boolean mShouldSkipCallLog; private boolean mShouldSkipNotification; @@ -331,6 +342,21 @@ public abstract class CallScreeningService extends Service { } /** + * Sets whether ringing should be silenced for the incoming call. When set + * to {@code true}, the Telecom framework will not play a ringtone for the call. + * The call will, however, still be sent to the default dialer app if it is not blocked. + * A {@link CallScreeningService} can use this to ensure a potential nuisance call is + * still surfaced to the user, but in a less intrusive manner. + * + * Setting this to true only makes sense when the call has not been disallowed + * using {@link #setDisallowCall(boolean)}. + */ + public @NonNull Builder setSilenceCall(boolean shouldSilenceCall) { + mShouldSilenceCall = shouldSilenceCall; + return this; + } + + /** * Sets whether the incoming call should not be displayed in the call log. This property * should only be set to true if the call is disallowed. * <p> @@ -356,6 +382,7 @@ public abstract class CallScreeningService extends Service { return new CallResponse( mShouldDisallowCall, mShouldRejectCall, + mShouldSilenceCall, mShouldSkipCallLog, mShouldSkipNotification); } @@ -411,10 +438,11 @@ public abstract class CallScreeningService extends Service { public abstract void onScreenCall(@NonNull Call.Details callDetails); /** - * Responds to the given incoming call, either allowing it or disallowing it. + * Responds to the given incoming call, either allowing it, silencing it or disallowing it. * <p> * The {@link CallScreeningService} calls this method to inform the system whether the call - * should be silently blocked or not. + * should be silently blocked or not. In the event that it should not be blocked, it may + * also be requested to ring silently. * <p> * Calls to this method are ignored unless the {@link Call.Details#getCallDirection()} is * {@link Call.Details#DIRECTION_INCOMING}. @@ -436,6 +464,8 @@ public abstract class CallScreeningService extends Service { !response.getSkipCallLog(), !response.getSkipNotification(), new ComponentName(getPackageName(), getClass().getName())); + } else if (response.getSilenceCall()) { + mCallScreeningAdapter.silenceCall(callDetails.getTelecomCallId()); } else { mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId()); } diff --git a/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl b/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl index a86c83068c6c..1160d27e8f7d 100644 --- a/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl +++ b/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl @@ -29,6 +29,8 @@ import android.telecom.CallIdentification; oneway interface ICallScreeningAdapter { void allowCall(String callId); + void silenceCall(String callId); + void disallowCall( String callId, boolean shouldReject, diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 524d080c2ea5..5f9ef3c0c601 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1520,6 +1520,14 @@ public class CarrierConfigManager { "carrier_app_no_wake_signal_config"; /** + * Determines whether the carrier app needed to be involved when users try to finish setting up + * the SIM card to get network service. + * @hide + */ + public static final String KEY_CARRIER_APP_REQUIRED_DURING_SIM_SETUP_BOOL = + "carrier_app_required_during_setup_bool"; + + /** * Default value for {@link Settings.Global#DATA_ROAMING} * @hide */ @@ -2307,9 +2315,9 @@ public class CarrierConfigManager { "undelivered_sms_message_expiration_time"; /** - * Specifies a carrier-defined {@link CallRedirectionService} which Telecom will bind - * to for outgoing calls. An empty string indicates that no carrier-defined - * {@link CallRedirectionService} is specified. + * Specifies a carrier-defined {@link android.telecom.CallRedirectionService} which Telecom + * will bind to for outgoing calls. An empty string indicates that no carrier-defined + * {@link android.telecom.CallRedirectionService} is specified. * @hide */ public static final String KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING = @@ -2867,6 +2875,7 @@ public class CarrierConfigManager { sDefaults.putString(KEY_CARRIER_NAME_STRING, ""); sDefaults.putString(KEY_SIM_COUNTRY_ISO_OVERRIDE_STRING, ""); sDefaults.putString(KEY_CARRIER_CALL_SCREENING_APP_STRING, ""); + sDefaults.putString(KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING, null); sDefaults.putBoolean(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL, false); sDefaults.putString(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING, ""); sDefaults.putBoolean(KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL, false); @@ -2922,6 +2931,7 @@ public class CarrierConfigManager { + "com.android.internal.telephony.CARRIER_SIGNAL_RESET" }); sDefaults.putStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY, null); + sDefaults.putBoolean(KEY_CARRIER_APP_REQUIRED_DURING_SIM_SETUP_BOOL, false); // Default carrier app configurations diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java index f0a26f50a0b8..ca264f738e1d 100644 --- a/telephony/java/android/telephony/DataFailCause.java +++ b/telephony/java/android/telephony/DataFailCause.java @@ -318,8 +318,8 @@ public final class DataFailCause { public static final int LOW_POWER_MODE_OR_POWERING_DOWN = 0x7FC; /** APN has been disabled. */ public static final int APN_DISABLED = 0x7FD; - /** PPP inactivity timer expired. */ - public static final int PPP_INACTIVITY_TIMER_EXPIRED = 0x7FE; + /** Maximum PPP inactivity timer expired. */ + public static final int MAX_PPP_INACTIVITY_TIMER_EXPIRED = 0x7FE; /** IPv6 address transfer failed. */ public static final int IPV6_ADDRESS_TRANSFER_FAILED = 0x7FF; /** Target RAT swap failed. */ @@ -339,12 +339,12 @@ public final class DataFailCause { * IPv4 data call bring up is rejected because the UE already maintains the allotted maximum * number of IPv4 data connections. */ - public static final int IPV4_CONNECTIONS_LIMIT_REACHED = 0x804; + public static final int MAX_IPV4_CONNECTIONS = 0x804; /** * IPv6 data call bring up is rejected because the UE already maintains the allotted maximum * number of IPv6 data connections. */ - public static final int IPV6_CONNECTIONS_LIMIT_REACHED = 0x805; + public static final int MAX_IPV6_CONNECTIONS = 0x805; /** * New PDN bring up is rejected during interface selection because the UE has already allotted * the available interfaces for other PDNs. @@ -416,7 +416,7 @@ public final class DataFailCause { */ public static final int CHANNEL_ACQUISITION_FAILURE = 0x81E; /** Maximum access probes transmitted. */ - public static final int ACCESS_PROBE_LIMIT_REACHED = 0x81F; + public static final int MAX_ACCESS_PROBE = 0x81F; /** Concurrent service is not supported by base station. */ public static final int CONCURRENT_SERVICE_NOT_SUPPORTED_BY_BASE_STATION = 0x820; /** There was no response received from the base station. */ @@ -1079,14 +1079,14 @@ public final class DataFailCause { SIM_CARD_CHANGED, LOW_POWER_MODE_OR_POWERING_DOWN, APN_DISABLED, - PPP_INACTIVITY_TIMER_EXPIRED, + MAX_PPP_INACTIVITY_TIMER_EXPIRED, IPV6_ADDRESS_TRANSFER_FAILED, TRAT_SWAP_FAILED, EHRPD_TO_HRPD_FALLBACK, MIP_CONFIG_FAILURE, PDN_INACTIVITY_TIMER_EXPIRED, - IPV4_CONNECTIONS_LIMIT_REACHED, - IPV6_CONNECTIONS_LIMIT_REACHED, + MAX_IPV4_CONNECTIONS, + MAX_IPV6_CONNECTIONS, APN_MISMATCH, IP_VERSION_MISMATCH, DUN_CALL_DISALLOWED, @@ -1112,7 +1112,7 @@ public final class DataFailCause { CDMA_INCOMING_CALL, CDMA_ALERT_STOP, CHANNEL_ACQUISITION_FAILURE, - ACCESS_PROBE_LIMIT_REACHED, + MAX_ACCESS_PROBE, CONCURRENT_SERVICE_NOT_SUPPORTED_BY_BASE_STATION, NO_RESPONSE_FROM_BASE_STATION, REJECTED_BY_BASE_STATION, @@ -1447,14 +1447,14 @@ public final class DataFailCause { sFailCauseMap.put(SIM_CARD_CHANGED, "SIM_CARD_CHANGED"); sFailCauseMap.put(LOW_POWER_MODE_OR_POWERING_DOWN, "LOW_POWER_MODE_OR_POWERING_DOWN"); sFailCauseMap.put(APN_DISABLED, "APN_DISABLED"); - sFailCauseMap.put(PPP_INACTIVITY_TIMER_EXPIRED, "PPP_INACTIVITY_TIMER_EXPIRED"); + sFailCauseMap.put(MAX_PPP_INACTIVITY_TIMER_EXPIRED, "MAX_PPP_INACTIVITY_TIMER_EXPIRED"); sFailCauseMap.put(IPV6_ADDRESS_TRANSFER_FAILED, "IPV6_ADDRESS_TRANSFER_FAILED"); sFailCauseMap.put(TRAT_SWAP_FAILED, "TRAT_SWAP_FAILED"); sFailCauseMap.put(EHRPD_TO_HRPD_FALLBACK, "EHRPD_TO_HRPD_FALLBACK"); sFailCauseMap.put(MIP_CONFIG_FAILURE, "MIP_CONFIG_FAILURE"); sFailCauseMap.put(PDN_INACTIVITY_TIMER_EXPIRED, "PDN_INACTIVITY_TIMER_EXPIRED"); - sFailCauseMap.put(IPV4_CONNECTIONS_LIMIT_REACHED, "IPV4_CONNECTIONS_LIMIT_REACHED"); - sFailCauseMap.put(IPV6_CONNECTIONS_LIMIT_REACHED, "IPV6_CONNECTIONS_LIMIT_REACHED"); + sFailCauseMap.put(MAX_IPV4_CONNECTIONS, "MAX_IPV4_CONNECTIONS"); + sFailCauseMap.put(MAX_IPV6_CONNECTIONS, "MAX_IPV6_CONNECTIONS"); sFailCauseMap.put(APN_MISMATCH, "APN_MISMATCH"); sFailCauseMap.put(IP_VERSION_MISMATCH, "IP_VERSION_MISMATCH"); sFailCauseMap.put(DUN_CALL_DISALLOWED, "DUN_CALL_DISALLOWED"); @@ -1480,7 +1480,7 @@ public final class DataFailCause { sFailCauseMap.put(CDMA_INCOMING_CALL, "CDMA_INCOMING_CALL"); sFailCauseMap.put(CDMA_ALERT_STOP, "CDMA_ALERT_STOP"); sFailCauseMap.put(CHANNEL_ACQUISITION_FAILURE, "CHANNEL_ACQUISITION_FAILURE"); - sFailCauseMap.put(ACCESS_PROBE_LIMIT_REACHED, "ACCESS_PROBE_LIMIT_REACHED"); + sFailCauseMap.put(MAX_ACCESS_PROBE, "MAX_ACCESS_PROBE"); sFailCauseMap.put(CONCURRENT_SERVICE_NOT_SUPPORTED_BY_BASE_STATION, "CONCURRENT_SERVICE_NOT_SUPPORTED_BY_BASE_STATION"); sFailCauseMap.put(NO_RESPONSE_FROM_BASE_STATION, "NO_RESPONSE_FROM_BASE_STATION"); diff --git a/telephony/java/android/telephony/INetworkService.aidl b/telephony/java/android/telephony/INetworkService.aidl index 9ef7186e8ae1..67e5650ab359 100644 --- a/telephony/java/android/telephony/INetworkService.aidl +++ b/telephony/java/android/telephony/INetworkService.aidl @@ -25,7 +25,7 @@ oneway interface INetworkService { void createNetworkServiceProvider(int slotId); void removeNetworkServiceProvider(int slotId); - void getNetworkRegistrationState(int slotId, int domain, INetworkServiceCallback callback); - void registerForNetworkRegistrationStateChanged(int slotId, INetworkServiceCallback callback); - void unregisterForNetworkRegistrationStateChanged(int slotId, INetworkServiceCallback callback); + void getNetworkRegistrationInfo(int slotId, int domain, INetworkServiceCallback callback); + void registerForNetworkRegistrationInfoChanged(int slotId, INetworkServiceCallback callback); + void unregisterForNetworkRegistrationInfoChanged(int slotId, INetworkServiceCallback callback); } diff --git a/telephony/java/android/telephony/INetworkServiceCallback.aidl b/telephony/java/android/telephony/INetworkServiceCallback.aidl index 520598ff9144..33b3ac0940b5 100644 --- a/telephony/java/android/telephony/INetworkServiceCallback.aidl +++ b/telephony/java/android/telephony/INetworkServiceCallback.aidl @@ -16,7 +16,7 @@ package android.telephony; -import android.telephony.NetworkRegistrationState; +import android.telephony.NetworkRegistrationInfo; /** * Network service call back interface @@ -24,6 +24,6 @@ import android.telephony.NetworkRegistrationState; */ oneway interface INetworkServiceCallback { - void onGetNetworkRegistrationStateComplete(int result, in NetworkRegistrationState state); + void onGetNetworkRegistrationInfoComplete(int result, in NetworkRegistrationInfo state); void onNetworkStateChanged(); } diff --git a/telephony/java/android/telephony/NetworkRegistrationState.aidl b/telephony/java/android/telephony/NetworkRegistrationInfo.aidl index 98cba7720603..5c803bf69f69 100644 --- a/telephony/java/android/telephony/NetworkRegistrationState.aidl +++ b/telephony/java/android/telephony/NetworkRegistrationInfo.aidl @@ -16,4 +16,4 @@ package android.telephony; -parcelable NetworkRegistrationState; +parcelable NetworkRegistrationInfo; diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java index eff3285a43dc..c31a14edff10 100644 --- a/telephony/java/android/telephony/NetworkRegistrationState.java +++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java @@ -32,11 +32,11 @@ import java.util.Objects; import java.util.stream.Collectors; /** - * Description of a mobile network registration state + * Description of a mobile network registration info * @hide */ @SystemApi -public class NetworkRegistrationState implements Parcelable { +public class NetworkRegistrationInfo implements Parcelable { /** * Network domain * @hide @@ -184,12 +184,12 @@ public class NetworkRegistrationState implements Parcelable { * @param cellIdentity The identity representing a unique cell or wifi AP. Set to null if the * information is not available. */ - public NetworkRegistrationState(@Domain int domain, @TransportType int transportType, - @RegState int regState, - @NetworkType int accessNetworkTechnology, int rejectCause, - boolean emergencyOnly, - @NonNull @ServiceType int[] availableServices, - @Nullable CellIdentity cellIdentity) { + public NetworkRegistrationInfo(@Domain int domain, @TransportType int transportType, + @RegState int regState, + @NetworkType int accessNetworkTechnology, int rejectCause, + boolean emergencyOnly, + @NonNull @ServiceType int[] availableServices, + @Nullable CellIdentity cellIdentity) { mDomain = domain; mTransportType = transportType; mRegState = regState; @@ -204,13 +204,15 @@ public class NetworkRegistrationState implements Parcelable { } /** - * Constructor for voice network registration states. + * Constructor for voice network registration info. * @hide */ - public NetworkRegistrationState(int domain, @TransportType int transportType, int regState, - int accessNetworkTechnology, int rejectCause, boolean emergencyOnly, - int[] availableServices, @Nullable CellIdentity cellIdentity, boolean cssSupported, - int roamingIndicator, int systemIsInPrl, int defaultRoamingIndicator) { + public NetworkRegistrationInfo(int domain, @TransportType int transportType, int regState, + int accessNetworkTechnology, int rejectCause, + boolean emergencyOnly, int[] availableServices, + @Nullable CellIdentity cellIdentity, boolean cssSupported, + int roamingIndicator, int systemIsInPrl, + int defaultRoamingIndicator) { this(domain, transportType, regState, accessNetworkTechnology, rejectCause, emergencyOnly, availableServices, cellIdentity); @@ -219,14 +221,16 @@ public class NetworkRegistrationState implements Parcelable { } /** - * Constructor for data network registration states. + * Constructor for data network registration info. * @hide */ - public NetworkRegistrationState(int domain, @TransportType int transportType, int regState, - int accessNetworkTechnology, int rejectCause, boolean emergencyOnly, - int[] availableServices, @Nullable CellIdentity cellIdentity, int maxDataCalls, - boolean isDcNrRestricted, boolean isNrAvailable, boolean isEndcAvailable, - LteVopsSupportInfo lteVopsSupportInfo) { + public NetworkRegistrationInfo(int domain, @TransportType int transportType, int regState, + int accessNetworkTechnology, int rejectCause, + boolean emergencyOnly, int[] availableServices, + @Nullable CellIdentity cellIdentity, int maxDataCalls, + boolean isDcNrRestricted, boolean isNrAvailable, + boolean isEndcAvailable, + LteVopsSupportInfo lteVopsSupportInfo) { this(domain, transportType, regState, accessNetworkTechnology, rejectCause, emergencyOnly, availableServices, cellIdentity); @@ -235,7 +239,7 @@ public class NetworkRegistrationState implements Parcelable { updateNrStatus(mDataSpecificStates); } - private NetworkRegistrationState(Parcel source) { + private NetworkRegistrationInfo(Parcel source) { mDomain = source.readInt(); mTransportType = source.readInt(); mRegState = source.readInt(); @@ -433,7 +437,7 @@ public class NetworkRegistrationState implements Parcelable { @Override public String toString() { - return new StringBuilder("NetworkRegistrationState{") + return new StringBuilder("NetworkRegistrationInfo{") .append(" domain=").append((mDomain == DOMAIN_CS) ? "CS" : "PS") .append(" transportType=").append( AccessNetworkConstants.transportTypeToString(mTransportType)) @@ -465,11 +469,11 @@ public class NetworkRegistrationState implements Parcelable { public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof NetworkRegistrationState)) { + if (!(o instanceof NetworkRegistrationInfo)) { return false; } - NetworkRegistrationState other = (NetworkRegistrationState) o; + NetworkRegistrationInfo other = (NetworkRegistrationInfo) o; return mDomain == other.mDomain && mTransportType == other.mTransportType && mRegState == other.mRegState @@ -528,46 +532,46 @@ public class NetworkRegistrationState implements Parcelable { } } - public static final @android.annotation.NonNull Parcelable.Creator<NetworkRegistrationState> CREATOR = - new Parcelable.Creator<NetworkRegistrationState>() { - @Override - public NetworkRegistrationState createFromParcel(Parcel source) { - return new NetworkRegistrationState(source); - } + public static final @NonNull Parcelable.Creator<NetworkRegistrationInfo> CREATOR = + new Parcelable.Creator<NetworkRegistrationInfo>() { + @Override + public NetworkRegistrationInfo createFromParcel(Parcel source) { + return new NetworkRegistrationInfo(source); + } - @Override - public NetworkRegistrationState[] newArray(int size) { - return new NetworkRegistrationState[size]; - } - }; + @Override + public NetworkRegistrationInfo[] newArray(int size) { + return new NetworkRegistrationInfo[size]; + } + }; /** * @hide */ - public NetworkRegistrationState sanitizeLocationInfo() { - NetworkRegistrationState result = copy(); + public NetworkRegistrationInfo sanitizeLocationInfo() { + NetworkRegistrationInfo result = copy(); result.mCellIdentity = null; return result; } - private NetworkRegistrationState copy() { + private NetworkRegistrationInfo copy() { Parcel p = Parcel.obtain(); this.writeToParcel(p, 0); p.setDataPosition(0); - NetworkRegistrationState result = new NetworkRegistrationState(p); + NetworkRegistrationInfo result = new NetworkRegistrationInfo(p); p.recycle(); return result; } /** - * Provides a convenient way to set the fields of a {@link NetworkRegistrationState} when + * Provides a convenient way to set the fields of a {@link NetworkRegistrationInfo} when * creating a new instance. * - * <p>The example below shows how you might create a new {@code NetworkRegistrationState}: + * <p>The example below shows how you might create a new {@code NetworkRegistrationInfo}: * * <pre><code> * - * NetworkRegistrationState nrs = new NetworkRegistrationState.Builder() + * NetworkRegistrationInfo nrs = new NetworkRegistrationInfo.Builder() * .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS) * .setApnName("apn.example.com") * .setEntryName("Example Carrier APN") @@ -736,12 +740,12 @@ public class NetworkRegistrationState implements Parcelable { } /** - * Build the NetworkRegistrationState. + * Build the NetworkRegistrationInfo. * - * @return the NetworkRegistrationState object. + * @return the NetworkRegistrationInfo object. */ - public @NonNull NetworkRegistrationState build() { - return new NetworkRegistrationState(mDomain, mTransportType, mRegState, + public @NonNull NetworkRegistrationInfo build() { + return new NetworkRegistrationInfo(mDomain, mTransportType, mRegState, mAccessNetworkTechnology, mRejectCause, mEmergencyOnly, mAvailableServices, mCellIdentity); } diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java index f1240e9fcf34..bc989dd8c5ad 100644 --- a/telephony/java/android/telephony/NetworkService.java +++ b/telephony/java/android/telephony/NetworkService.java @@ -27,7 +27,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; -import android.telephony.NetworkRegistrationState.Domain; +import android.telephony.NetworkRegistrationInfo.Domain; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; @@ -59,10 +59,10 @@ public abstract class NetworkService extends Service { private static final int NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER = 1; private static final int NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER = 2; private static final int NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS = 3; - private static final int NETWORK_SERVICE_GET_REGISTRATION_STATE = 4; - private static final int NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE = 5; - private static final int NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE = 6; - private static final int NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED = 7; + private static final int NETWORK_SERVICE_GET_REGISTRATION_INFO = 4; + private static final int NETWORK_SERVICE_REGISTER_FOR_INFO_CHANGE = 5; + private static final int NETWORK_SERVICE_UNREGISTER_FOR_INFO_CHANGE = 6; + private static final int NETWORK_SERVICE_INDICATION_NETWORK_INFO_CHANGED = 7; private final HandlerThread mHandlerThread; @@ -86,7 +86,7 @@ public abstract class NetworkService extends Service { private final int mSlotIndex; private final List<INetworkServiceCallback> - mNetworkRegistrationStateChangedCallbacks = new ArrayList<>(); + mNetworkRegistrationInfoChangedCallbacks = new ArrayList<>(); /** * Constructor @@ -104,38 +104,38 @@ public abstract class NetworkService extends Service { } /** - * API to get network registration state. The result will be passed to the callback. + * API to get network registration info. The result will be passed to the callback. * @param domain Network domain - * @param callback The callback for reporting network registration state + * @param callback The callback for reporting network registration info */ - public void getNetworkRegistrationState(@Domain int domain, - @NonNull NetworkServiceCallback callback) { - callback.onGetNetworkRegistrationStateComplete( + public void getNetworkRegistrationInfo(@Domain int domain, + @NonNull NetworkServiceCallback callback) { + callback.onGetNetworkRegistrationInfoComplete( NetworkServiceCallback.RESULT_ERROR_UNSUPPORTED, null); } /** - * Notify the system that network registration state is changed. + * Notify the system that network registration info is changed. */ - public final void notifyNetworkRegistrationStateChanged() { - mHandler.obtainMessage(NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED, + public final void notifyNetworkRegistrationInfoChanged() { + mHandler.obtainMessage(NETWORK_SERVICE_INDICATION_NETWORK_INFO_CHANGED, mSlotIndex, 0, null).sendToTarget(); } - private void registerForStateChanged(@NonNull INetworkServiceCallback callback) { - synchronized (mNetworkRegistrationStateChangedCallbacks) { - mNetworkRegistrationStateChangedCallbacks.add(callback); + private void registerForInfoChanged(@NonNull INetworkServiceCallback callback) { + synchronized (mNetworkRegistrationInfoChangedCallbacks) { + mNetworkRegistrationInfoChangedCallbacks.add(callback); } } - private void unregisterForStateChanged(@NonNull INetworkServiceCallback callback) { - synchronized (mNetworkRegistrationStateChangedCallbacks) { - mNetworkRegistrationStateChangedCallbacks.remove(callback); + private void unregisterForInfoChanged(@NonNull INetworkServiceCallback callback) { + synchronized (mNetworkRegistrationInfoChangedCallbacks) { + mNetworkRegistrationInfoChangedCallbacks.remove(callback); } } - private void notifyStateChangedToCallbacks() { - for (INetworkServiceCallback callback : mNetworkRegistrationStateChangedCallbacks) { + private void notifyInfoChangedToCallbacks() { + for (INetworkServiceCallback callback : mNetworkRegistrationInfoChangedCallbacks) { try { callback.onNetworkStateChanged(); } catch (RemoteException exception) { @@ -189,24 +189,24 @@ public abstract class NetworkService extends Service { } mServiceMap.clear(); break; - case NETWORK_SERVICE_GET_REGISTRATION_STATE: + case NETWORK_SERVICE_GET_REGISTRATION_INFO: if (serviceProvider == null) break; int domainId = message.arg2; - serviceProvider.getNetworkRegistrationState(domainId, + serviceProvider.getNetworkRegistrationInfo(domainId, new NetworkServiceCallback(callback)); break; - case NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE: + case NETWORK_SERVICE_REGISTER_FOR_INFO_CHANGE: if (serviceProvider == null) break; - serviceProvider.registerForStateChanged(callback); + serviceProvider.registerForInfoChanged(callback); break; - case NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE: + case NETWORK_SERVICE_UNREGISTER_FOR_INFO_CHANGE: if (serviceProvider == null) break; - serviceProvider.unregisterForStateChanged(callback); + serviceProvider.unregisterForInfoChanged(callback); break; - case NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED: + case NETWORK_SERVICE_INDICATION_NETWORK_INFO_CHANGED: if (serviceProvider == null) break; - serviceProvider.notifyStateChangedToCallbacks(); + serviceProvider.notifyInfoChangedToCallbacks(); break; default: break; @@ -280,23 +280,23 @@ public abstract class NetworkService extends Service { } @Override - public void getNetworkRegistrationState( + public void getNetworkRegistrationInfo( int slotIndex, int domain, INetworkServiceCallback callback) { - mHandler.obtainMessage(NETWORK_SERVICE_GET_REGISTRATION_STATE, slotIndex, + mHandler.obtainMessage(NETWORK_SERVICE_GET_REGISTRATION_INFO, slotIndex, domain, callback).sendToTarget(); } @Override - public void registerForNetworkRegistrationStateChanged( + public void registerForNetworkRegistrationInfoChanged( int slotIndex, INetworkServiceCallback callback) { - mHandler.obtainMessage(NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE, slotIndex, + mHandler.obtainMessage(NETWORK_SERVICE_REGISTER_FOR_INFO_CHANGE, slotIndex, 0, callback).sendToTarget(); } @Override - public void unregisterForNetworkRegistrationStateChanged( + public void unregisterForNetworkRegistrationInfoChanged( int slotIndex, INetworkServiceCallback callback) { - mHandler.obtainMessage(NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE, slotIndex, + mHandler.obtainMessage(NETWORK_SERVICE_UNREGISTER_FOR_INFO_CHANGE, slotIndex, 0, callback).sendToTarget(); } } diff --git a/telephony/java/android/telephony/NetworkServiceCallback.java b/telephony/java/android/telephony/NetworkServiceCallback.java index c2fcfb7a0759..cc2524000091 100644 --- a/telephony/java/android/telephony/NetworkServiceCallback.java +++ b/telephony/java/android/telephony/NetworkServiceCallback.java @@ -28,8 +28,8 @@ import java.lang.ref.WeakReference; /** * Network service callback. Object of this class is passed to NetworkServiceProvider upon - * calling getNetworkRegistrationState, to receive asynchronous feedback from NetworkServiceProvider - * upon onGetNetworkRegistrationStateComplete. It's like a wrapper of INetworkServiceCallback + * calling getNetworkRegistrationInfo, to receive asynchronous feedback from NetworkServiceProvider + * upon onGetNetworkRegistrationInfoComplete. It's like a wrapper of INetworkServiceCallback * because INetworkServiceCallback can't be a parameter type in public APIs. * * @hide @@ -70,20 +70,20 @@ public class NetworkServiceCallback { /** * Called to indicate result of - * {@link NetworkServiceProvider#getNetworkRegistrationState(int, NetworkServiceCallback)} + * {@link NetworkServiceProvider#getNetworkRegistrationInfo(int, NetworkServiceCallback)} * * @param result Result status like {@link NetworkServiceCallback#RESULT_SUCCESS} or * {@link NetworkServiceCallback#RESULT_ERROR_UNSUPPORTED} * @param state The state information to be returned to callback. */ - public void onGetNetworkRegistrationStateComplete(int result, - @Nullable NetworkRegistrationState state) { + public void onGetNetworkRegistrationInfoComplete(int result, + @Nullable NetworkRegistrationInfo state) { INetworkServiceCallback callback = mCallback.get(); if (callback != null) { try { - callback.onGetNetworkRegistrationStateComplete(result, state); + callback.onGetNetworkRegistrationInfoComplete(result, state); } catch (RemoteException e) { - Rlog.e(mTag, "Failed to onGetNetworkRegistrationStateComplete on the remote"); + Rlog.e(mTag, "Failed to onGetNetworkRegistrationInfoComplete on the remote"); } } else { Rlog.e(mTag, "Weak reference of callback is null."); diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 687c6f4ff72e..adbe295513a7 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -29,8 +29,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.telephony.AccessNetworkConstants.AccessNetworkType; import android.telephony.AccessNetworkConstants.TransportType; -import android.telephony.NetworkRegistrationState.Domain; -import android.telephony.NetworkRegistrationState.NRStatus; +import android.telephony.NetworkRegistrationInfo.Domain; +import android.telephony.NetworkRegistrationInfo.NRStatus; import android.text.TextUtils; import java.lang.annotation.Retention; @@ -349,7 +349,7 @@ public class ServiceState implements Parcelable { * Reference: 3GPP TS 36.104 5.4.3 */ private int mLteEarfcnRsrpBoost = 0; - private List<NetworkRegistrationState> mNetworkRegistrationStates = new ArrayList<>(); + private List<NetworkRegistrationInfo> mNetworkRegistrationInfos = new ArrayList<>(); /** * get String description of roaming type @@ -432,8 +432,8 @@ public class ServiceState implements Parcelable { mCellBandwidths = s.mCellBandwidths == null ? null : Arrays.copyOf(s.mCellBandwidths, s.mCellBandwidths.length); mLteEarfcnRsrpBoost = s.mLteEarfcnRsrpBoost; - mNetworkRegistrationStates = s.mNetworkRegistrationStates == null ? null : - new ArrayList<>(s.mNetworkRegistrationStates); + mNetworkRegistrationInfos = s.mNetworkRegistrationInfos == null ? null : + new ArrayList<>(s.mNetworkRegistrationInfos); mNrFrequencyRange = s.mNrFrequencyRange; } @@ -466,8 +466,8 @@ public class ServiceState implements Parcelable { mIsEmergencyOnly = in.readInt() != 0; mIsUsingCarrierAggregation = in.readInt() != 0; mLteEarfcnRsrpBoost = in.readInt(); - mNetworkRegistrationStates = new ArrayList<>(); - in.readList(mNetworkRegistrationStates, NetworkRegistrationState.class.getClassLoader()); + mNetworkRegistrationInfos = new ArrayList<>(); + in.readList(mNetworkRegistrationInfos, NetworkRegistrationInfo.class.getClassLoader()); mChannelNumber = in.readInt(); mCellBandwidths = in.createIntArray(); mNrFrequencyRange = in.readInt(); @@ -495,7 +495,7 @@ public class ServiceState implements Parcelable { out.writeInt(mIsEmergencyOnly ? 1 : 0); out.writeInt(mIsUsingCarrierAggregation ? 1 : 0); out.writeInt(mLteEarfcnRsrpBoost); - out.writeList(mNetworkRegistrationStates); + out.writeList(mNetworkRegistrationInfos); out.writeInt(mChannelNumber); out.writeIntArray(mCellBandwidths); out.writeInt(mNrFrequencyRange); @@ -620,8 +620,8 @@ public class ServiceState implements Parcelable { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public @RoamingType int getVoiceRoamingType() { - final NetworkRegistrationState regState = getNetworkRegistrationState( - NetworkRegistrationState.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + final NetworkRegistrationInfo regState = getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); if (regState != null) { return regState.getRoamingType(); } @@ -644,10 +644,10 @@ public class ServiceState implements Parcelable { * @hide */ public boolean getDataRoamingFromRegistration() { - final NetworkRegistrationState regState = getNetworkRegistrationState( - NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + final NetworkRegistrationInfo regState = getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); if (regState != null) { - return (regState.getRegState() == NetworkRegistrationState.REG_STATE_ROAMING); + return (regState.getRegState() == NetworkRegistrationInfo.REG_STATE_ROAMING); } return false; } @@ -659,8 +659,8 @@ public class ServiceState implements Parcelable { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public @RoamingType int getDataRoamingType() { - final NetworkRegistrationState regState = getNetworkRegistrationState( - NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + final NetworkRegistrationInfo regState = getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); if (regState != null) { return regState.getRoamingType(); } @@ -858,7 +858,7 @@ public class ServiceState implements Parcelable { mIsEmergencyOnly, mIsUsingCarrierAggregation, mLteEarfcnRsrpBoost, - mNetworkRegistrationStates, + mNetworkRegistrationInfos, mNrFrequencyRange); } @@ -888,9 +888,9 @@ public class ServiceState implements Parcelable { s.mCdmaDefaultRoamingIndicator) && mIsEmergencyOnly == s.mIsEmergencyOnly && mIsUsingCarrierAggregation == s.mIsUsingCarrierAggregation) - && (mNetworkRegistrationStates == null ? s.mNetworkRegistrationStates == null : - s.mNetworkRegistrationStates != null && - mNetworkRegistrationStates.containsAll(s.mNetworkRegistrationStates)) + && (mNetworkRegistrationInfos == null + ? s.mNetworkRegistrationInfos == null : s.mNetworkRegistrationInfos != null + && mNetworkRegistrationInfos.containsAll(s.mNetworkRegistrationInfos)) && mNrFrequencyRange == s.mNrFrequencyRange; } @@ -1043,7 +1043,7 @@ public class ServiceState implements Parcelable { .append(", mIsEmergencyOnly=").append(mIsEmergencyOnly) .append(", mIsUsingCarrierAggregation=").append(mIsUsingCarrierAggregation) .append(", mLteEarfcnRsrpBoost=").append(mLteEarfcnRsrpBoost) - .append(", mNetworkRegistrationStates=").append(mNetworkRegistrationStates) + .append(", mNetworkRegistrationInfos=").append(mNetworkRegistrationInfos) .append(", mNrFrequencyRange=").append(mNrFrequencyRange) .append("}").toString(); } @@ -1073,7 +1073,7 @@ public class ServiceState implements Parcelable { mIsEmergencyOnly = false; mIsUsingCarrierAggregation = false; mLteEarfcnRsrpBoost = 0; - mNetworkRegistrationStates = new ArrayList<>(); + mNetworkRegistrationInfos = new ArrayList<>(); mNrFrequencyRange = FREQUENCY_RANGE_UNKNOWN; } @@ -1130,14 +1130,14 @@ public class ServiceState implements Parcelable { /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public void setVoiceRoamingType(@RoamingType int type) { - NetworkRegistrationState regState = getNetworkRegistrationState( - NetworkRegistrationState.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + NetworkRegistrationInfo regState = getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); if (regState == null) { - regState = new NetworkRegistrationState( - NetworkRegistrationState.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN, + regState = new NetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN, ServiceState.ROAMING_TYPE_NOT_ROAMING, TelephonyManager.NETWORK_TYPE_UNKNOWN, 0, false, null, null); - addNetworkRegistrationState(regState); + addNetworkRegistrationInfo(regState); } regState.setRoamingType(type); } @@ -1151,14 +1151,14 @@ public class ServiceState implements Parcelable { /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public void setDataRoamingType(@RoamingType int type) { - NetworkRegistrationState regState = getNetworkRegistrationState( - NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + NetworkRegistrationInfo regState = getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); if (regState == null) { - regState = new NetworkRegistrationState( - NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN, + regState = new NetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN, ServiceState.ROAMING_TYPE_NOT_ROAMING, TelephonyManager.NETWORK_TYPE_UNKNOWN, 0, false, null, null); - addNetworkRegistrationState(regState); + addNetworkRegistrationInfo(regState); } regState.setRoamingType(type); } @@ -1326,14 +1326,14 @@ public class ServiceState implements Parcelable { this.mRilVoiceRadioTechnology = rt; // sync to network registration state - NetworkRegistrationState regState = getNetworkRegistrationState( - NetworkRegistrationState.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + NetworkRegistrationInfo regState = getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); if (regState == null) { - regState = new NetworkRegistrationState( - NetworkRegistrationState.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN, + regState = new NetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN, ServiceState.ROAMING_TYPE_NOT_ROAMING, TelephonyManager.NETWORK_TYPE_UNKNOWN, 0, false, null, null); - addNetworkRegistrationState(regState); + addNetworkRegistrationInfo(regState); } regState.setAccessNetworkTechnology( rilRadioTechnologyToNetworkType(mRilVoiceRadioTechnology)); @@ -1353,15 +1353,15 @@ public class ServiceState implements Parcelable { mRilDataRadioTechnology); // sync to network registration state - NetworkRegistrationState regState = getNetworkRegistrationState( - NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + NetworkRegistrationInfo regState = getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); if (regState == null) { - regState = new NetworkRegistrationState( - NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN, + regState = new NetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN, ServiceState.ROAMING_TYPE_NOT_ROAMING, TelephonyManager.NETWORK_TYPE_UNKNOWN, 0, false, null, null); - addNetworkRegistrationState(regState); + addNetworkRegistrationInfo(regState); } regState.setAccessNetworkTechnology( rilRadioTechnologyToNetworkType(mRilDataRadioTechnology)); @@ -1391,9 +1391,9 @@ public class ServiceState implements Parcelable { * @hide */ public @NRStatus int getNrStatus() { - final NetworkRegistrationState regState = getNetworkRegistrationState( - NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); - if (regState == null) return NetworkRegistrationState.NR_STATUS_NONE; + final NetworkRegistrationInfo regState = getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + if (regState == null) return NetworkRegistrationInfo.NR_STATUS_NONE; return regState.getNrStatus(); } @@ -1576,19 +1576,19 @@ public class ServiceState implements Parcelable { /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public @TelephonyManager.NetworkType int getDataNetworkType() { - final NetworkRegistrationState iwlanRegState = getNetworkRegistrationState( - NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WLAN); + final NetworkRegistrationInfo iwlanRegState = getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WLAN); if (iwlanRegState != null - && iwlanRegState.getRegState() == NetworkRegistrationState.REG_STATE_HOME) { + && iwlanRegState.getRegState() == NetworkRegistrationInfo.REG_STATE_HOME) { // If the device is on IWLAN, return IWLAN as the network type. This is to simulate the // behavior of legacy mode device. In the future caller should use - // getNetworkRegistrationState() to retrieve the actual data network type on cellular + // getNetworkRegistrationInfo() to retrieve the actual data network type on cellular // or on IWLAN. return iwlanRegState.getAccessNetworkTechnology(); } - final NetworkRegistrationState regState = getNetworkRegistrationState( - NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + final NetworkRegistrationInfo regState = getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); if (regState != null) { return regState.getAccessNetworkTechnology(); } @@ -1598,8 +1598,8 @@ public class ServiceState implements Parcelable { /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public @TelephonyManager.NetworkType int getVoiceNetworkType() { - final NetworkRegistrationState regState = getNetworkRegistrationState( - NetworkRegistrationState.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + final NetworkRegistrationInfo regState = getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); if (regState != null) { return regState.getAccessNetworkTechnology(); } @@ -1762,52 +1762,36 @@ public class ServiceState implements Parcelable { } /** - * Get all of the available network registration states. + * Get all of the available network registration info. * - * @return List of {@link NetworkRegistrationState} + * @return List of {@link NetworkRegistrationInfo} * @hide */ @NonNull @SystemApi - public List<NetworkRegistrationState> getNetworkRegistrationStates() { - synchronized (mNetworkRegistrationStates) { - return new ArrayList<>(mNetworkRegistrationStates); + public List<NetworkRegistrationInfo> getNetworkRegistrationInfoList() { + synchronized (mNetworkRegistrationInfos) { + return new ArrayList<>(mNetworkRegistrationInfos); } } /** - * Get the network registration states for the transport type. + * Get the network registration info list for the transport type. * * @param transportType The transport type - * @return List of {@link NetworkRegistrationState} - * @hide - * - * @deprecated Use {@link #getNetworkRegistrationStatesForTransportType(int)} - */ - @NonNull - @Deprecated - @SystemApi - public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) { - return getNetworkRegistrationStatesForTransportType(transportType); - } - - /** - * Get the network registration states for the transport type. - * - * @param transportType The transport type - * @return List of {@link NetworkRegistrationState} + * @return List of {@link NetworkRegistrationInfo} * @hide */ @NonNull @SystemApi - public List<NetworkRegistrationState> getNetworkRegistrationStatesForTransportType( + public List<NetworkRegistrationInfo> getNetworkRegistrationInfoListForTransportType( @TransportType int transportType) { - List<NetworkRegistrationState> list = new ArrayList<>(); + List<NetworkRegistrationInfo> list = new ArrayList<>(); - synchronized (mNetworkRegistrationStates) { - for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) { - if (networkRegistrationState.getTransportType() == transportType) { - list.add(networkRegistrationState); + synchronized (mNetworkRegistrationInfos) { + for (NetworkRegistrationInfo networkRegistrationInfo : mNetworkRegistrationInfos) { + if (networkRegistrationInfo.getTransportType() == transportType) { + list.add(networkRegistrationInfo); } } } @@ -1816,22 +1800,22 @@ public class ServiceState implements Parcelable { } /** - * Get the network registration states for the network domain. + * Get the network registration info list for the network domain. * - * @param domain The network {@link NetworkRegistrationState.Domain domain} - * @return List of {@link NetworkRegistrationState} + * @param domain The network {@link NetworkRegistrationInfo.Domain domain} + * @return List of {@link NetworkRegistrationInfo} * @hide */ @NonNull @SystemApi - public List<NetworkRegistrationState> getNetworkRegistrationStatesForDomain( + public List<NetworkRegistrationInfo> getNetworkRegistrationInfoListForDomain( @Domain int domain) { - List<NetworkRegistrationState> list = new ArrayList<>(); + List<NetworkRegistrationInfo> list = new ArrayList<>(); - synchronized (mNetworkRegistrationStates) { - for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) { - if (networkRegistrationState.getDomain() == domain) { - list.add(networkRegistrationState); + synchronized (mNetworkRegistrationInfos) { + for (NetworkRegistrationInfo networkRegistrationInfo : mNetworkRegistrationInfos) { + if (networkRegistrationInfo.getDomain() == domain) { + list.add(networkRegistrationInfo); } } } @@ -1842,39 +1826,21 @@ public class ServiceState implements Parcelable { /** * Get the network registration state for the transport type and network domain. * - * @param domain The network {@link NetworkRegistrationState.Domain domain} - * @param transportType The transport type - * @return The matching {@link NetworkRegistrationState} - * @hide - * - * @deprecated Use {@link #getNetworkRegistrationState(int, int)} - */ - @Nullable - @Deprecated - @SystemApi - public NetworkRegistrationState getNetworkRegistrationStates(@Domain int domain, - @TransportType int transportType) { - return getNetworkRegistrationState(domain, transportType); - } - - /** - * Get the network registration state for the transport type and network domain. - * - * @param domain The network {@link NetworkRegistrationState.Domain domain} + * @param domain The network {@link NetworkRegistrationInfo.Domain domain} * @param transportType The transport type - * @return The matching {@link NetworkRegistrationState} + * @return The matching {@link NetworkRegistrationInfo} * @hide * */ @Nullable @SystemApi - public NetworkRegistrationState getNetworkRegistrationState(@Domain int domain, - @TransportType int transportType) { - synchronized (mNetworkRegistrationStates) { - for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) { - if (networkRegistrationState.getTransportType() == transportType - && networkRegistrationState.getDomain() == domain) { - return networkRegistrationState; + public NetworkRegistrationInfo getNetworkRegistrationInfo(@Domain int domain, + @TransportType int transportType) { + synchronized (mNetworkRegistrationInfos) { + for (NetworkRegistrationInfo networkRegistrationInfo : mNetworkRegistrationInfos) { + if (networkRegistrationInfo.getTransportType() == transportType + && networkRegistrationInfo.getDomain() == domain) { + return networkRegistrationInfo; } } } @@ -1885,20 +1851,20 @@ public class ServiceState implements Parcelable { /** * @hide */ - public void addNetworkRegistrationState(NetworkRegistrationState regState) { + public void addNetworkRegistrationInfo(NetworkRegistrationInfo regState) { if (regState == null) return; - synchronized (mNetworkRegistrationStates) { - for (int i = 0; i < mNetworkRegistrationStates.size(); i++) { - NetworkRegistrationState curRegState = mNetworkRegistrationStates.get(i); + synchronized (mNetworkRegistrationInfos) { + for (int i = 0; i < mNetworkRegistrationInfos.size(); i++) { + NetworkRegistrationInfo curRegState = mNetworkRegistrationInfos.get(i); if (curRegState.getTransportType() == regState.getTransportType() && curRegState.getDomain() == regState.getDomain()) { - mNetworkRegistrationStates.remove(i); + mNetworkRegistrationInfos.remove(i); break; } } - mNetworkRegistrationStates.add(regState); + mNetworkRegistrationInfos.add(regState); } } @@ -1913,15 +1879,15 @@ public class ServiceState implements Parcelable { /** * Returns a copy of self with location-identifying information removed. - * Always clears the NetworkRegistrationState's CellIdentity fields, but if removeCoarseLocation + * Always clears the NetworkRegistrationInfo's CellIdentity fields, but if removeCoarseLocation * is true, clears other info as well. * @hide */ public ServiceState sanitizeLocationInfo(boolean removeCoarseLocation) { ServiceState state = new ServiceState(this); - if (state.mNetworkRegistrationStates != null) { - state.mNetworkRegistrationStates = state.mNetworkRegistrationStates.stream() - .map(NetworkRegistrationState::sanitizeLocationInfo) + if (state.mNetworkRegistrationInfos != null) { + state.mNetworkRegistrationInfos = state.mNetworkRegistrationInfos.stream() + .map(NetworkRegistrationInfo::sanitizeLocationInfo) .collect(Collectors.toList()); } if (!removeCoarseLocation) return state; diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index f7ab9216ee11..c91f16b7deba 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -58,6 +58,8 @@ import android.os.SystemProperties; import android.os.WorkSource; import android.provider.Settings.SettingNotFoundException; import android.service.carrier.CarrierIdentifier; +import android.telecom.CallScreeningService; +import android.telecom.InCallService; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; @@ -617,7 +619,13 @@ public class TelephonyManager { * <p class="note"> * Retrieve with * {@link android.content.Intent#getStringExtra(String)}. + * <p> + * + * @deprecated Companion apps for wearable devices should use the {@link InCallService} API + * to retrieve the phone number for calls instead. Apps performing call screening should use + * the {@link CallScreeningService} API instead. */ + @Deprecated public static final String EXTRA_INCOMING_NUMBER = "incoming_number"; /** @@ -8285,7 +8293,7 @@ public class TelephonyManager { * @see SubscriptionManager#getDefaultSubscriptionId() * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public boolean isVolteAvailable() { try { return getITelephony().isAvailable(getSubId(), @@ -8304,7 +8312,7 @@ public class TelephonyManager { * @return true if VT is available, or false if it is unavailable or unknown. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public boolean isVideoTelephonyAvailable() { try { return getITelephony().isVideoTelephonyAvailable(getSubId()); @@ -8319,7 +8327,7 @@ public class TelephonyManager { * @return true if VoWiFi is available, or false if it is unavailable or unknown. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public boolean isWifiCallingAvailable() { try { return getITelephony().isWifiCallingAvailable(getSubId()); @@ -9857,8 +9865,11 @@ public class TelephonyManager { * <p>Requires Permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} * + * + * @deprecated * @hide */ + @Deprecated @TestApi public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1, String gid2, String plmn, String spn) { @@ -9866,7 +9877,35 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) { telephony.setCarrierTestOverride( - getSubId(), mccmnc, imsi, iccid, gid1, gid2, plmn, spn); + getSubId(), mccmnc, imsi, iccid, gid1, gid2, plmn, spn, + null, null); + } + } catch (RemoteException ex) { + // This could happen if binder process crashes. + } + } + + /** + * A test API to override carrier information including mccmnc, imsi, iccid, gid1, gid2, + * plmn, spn, apn and carrier priviledge. This would be handy for, eg, forcing a particular + * carrier id, carrier's config (also any country or carrier overlays) to be loaded when using + * a test SIM with a call box. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * + * @hide + */ + @TestApi + public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1, + String gid2, String plmn, String spn, + String carrierPriviledgeRules, String apn) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setCarrierTestOverride( + getSubId(), mccmnc, imsi, iccid, gid1, gid2, plmn, spn, + carrierPriviledgeRules, apn); } } catch (RemoteException ex) { // This could happen if binder process crashes. diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java index e651783c44f2..875bd782d2cb 100644 --- a/telephony/java/android/telephony/euicc/EuiccManager.java +++ b/telephony/java/android/telephony/euicc/EuiccManager.java @@ -285,6 +285,8 @@ public class EuiccManager { * {@link #ACTION_DELETE_SUBSCRIPTION_PRIVILEGED}, and * {@link #ACTION_RENAME_SUBSCRIPTION_PRIVILEGED} providing the ID of the targeted subscription. * + * <p>Expected type of the extra data: int + * * @hide */ @SystemApi @@ -295,6 +297,8 @@ public class EuiccManager { * Key for an extra set on {@link #ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED} providing a boolean * value of whether to enable or disable the targeted subscription. * + * <p>Expected type of the extra data: boolean + * * @hide */ @SystemApi @@ -305,6 +309,8 @@ public class EuiccManager { * Key for an extra set on {@link #ACTION_RENAME_SUBSCRIPTION_PRIVILEGED} providing a new * nickname for the targeted subscription. * + * <p>Expected type of the extra data: String + * * @hide */ @SystemApi diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 62b92fd9a789..c2c31ccee795 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -1683,7 +1683,7 @@ interface ITelephony { * (also any country or carrier overlays) to be loaded when using a test SIM with a call box. */ void setCarrierTestOverride(int subId, String mccmnc, String imsi, String iccid, String gid1, - String gid2, String plmn, String spn); + String gid2, String plmn, String spn, String carrierPrivilegeRules, String apn); /** * A test API to return installed carrier id list version. diff --git a/tests/net/Android.bp b/tests/net/Android.bp index 2539c0f66ce1..c62d85e4187e 100644 --- a/tests/net/Android.bp +++ b/tests/net/Android.bp @@ -13,7 +13,6 @@ android_test { "mockito-target-minus-junit4", "platform-test-annotations", "services.core", - "services.ipmemorystore", "services.net", ], libs: [ diff --git a/tests/net/java/android/net/DnsPacketTest.java b/tests/net/java/android/net/DnsPacketTest.java index 9ede2b85af00..975abf416944 100644 --- a/tests/net/java/android/net/DnsPacketTest.java +++ b/tests/net/java/android/net/DnsPacketTest.java @@ -69,7 +69,7 @@ public class DnsPacketTest { try { new TestDnsPacket(null); fail("Exception not thrown for null byte array"); - } catch (DnsPacket.ParseException e) { + } catch (ParseException e) { } } diff --git a/tests/net/java/android/net/IpMemoryStoreTest.java b/tests/net/java/android/net/IpMemoryStoreTest.java index 57ecc8f38c69..18c67688940a 100644 --- a/tests/net/java/android/net/IpMemoryStoreTest.java +++ b/tests/net/java/android/net/IpMemoryStoreTest.java @@ -16,6 +16,9 @@ package android.net; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; + import android.content.Context; import androidx.test.filters.SmallTest; @@ -33,13 +36,25 @@ public class IpMemoryStoreTest { @Mock Context mMockContext; @Mock + NetworkStackClient mNetworkStackClient; + @Mock IIpMemoryStore mMockService; IpMemoryStore mStore; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mStore = new IpMemoryStore(mMockContext, mMockService); + doAnswer(invocation -> { + ((IIpMemoryStoreCallbacks) invocation.getArgument(0)) + .onIpMemoryStoreFetched(mMockService); + return null; + }).when(mNetworkStackClient).fetchIpMemoryStore(any()); + mStore = new IpMemoryStore(mMockContext) { + @Override + protected NetworkStackClient getNetworkStackClient() { + return mNetworkStackClient; + } + }; } @Test diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index 7a505a23c530..950c6f801461 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -139,6 +139,8 @@ public class WifiEnterpriseConfig implements Parcelable { private X509Certificate[] mClientCertificateChain; private int mEapMethod = Eap.NONE; private int mPhase2Method = Phase2.NONE; + private boolean mIsAppInstalledDeviceKeyAndCert = false; + private boolean mIsAppInstalledCaCert = false; private static final String TAG = "WifiEnterpriseConfig"; @@ -181,6 +183,8 @@ public class WifiEnterpriseConfig implements Parcelable { } mEapMethod = source.mEapMethod; mPhase2Method = source.mPhase2Method; + mIsAppInstalledDeviceKeyAndCert = source.mIsAppInstalledDeviceKeyAndCert; + mIsAppInstalledCaCert = source.mIsAppInstalledCaCert; } /** @@ -224,6 +228,8 @@ public class WifiEnterpriseConfig implements Parcelable { ParcelUtil.writeCertificates(dest, mCaCerts); ParcelUtil.writePrivateKey(dest, mClientPrivateKey); ParcelUtil.writeCertificates(dest, mClientCertificateChain); + dest.writeBoolean(mIsAppInstalledDeviceKeyAndCert); + dest.writeBoolean(mIsAppInstalledCaCert); } public static final @android.annotation.NonNull Creator<WifiEnterpriseConfig> CREATOR = @@ -243,6 +249,8 @@ public class WifiEnterpriseConfig implements Parcelable { enterpriseConfig.mCaCerts = ParcelUtil.readCertificates(in); enterpriseConfig.mClientPrivateKey = ParcelUtil.readPrivateKey(in); enterpriseConfig.mClientCertificateChain = ParcelUtil.readCertificates(in); + enterpriseConfig.mIsAppInstalledDeviceKeyAndCert = in.readBoolean(); + enterpriseConfig.mIsAppInstalledCaCert = in.readBoolean(); return enterpriseConfig; } @@ -652,8 +660,10 @@ public class WifiEnterpriseConfig implements Parcelable { public void setCaCertificate(@Nullable X509Certificate cert) { if (cert != null) { if (cert.getBasicConstraints() >= 0) { + mIsAppInstalledCaCert = true; mCaCerts = new X509Certificate[] {cert}; } else { + mCaCerts = null; throw new IllegalArgumentException("Not a CA certificate"); } } else { @@ -694,10 +704,12 @@ public class WifiEnterpriseConfig implements Parcelable { if (certs[i].getBasicConstraints() >= 0) { newCerts[i] = certs[i]; } else { + mCaCerts = null; throw new IllegalArgumentException("Not a CA certificate"); } } mCaCerts = newCerts; + mIsAppInstalledCaCert = true; } else { mCaCerts = null; } @@ -853,6 +865,7 @@ public class WifiEnterpriseConfig implements Parcelable { mClientPrivateKey = privateKey; mClientCertificateChain = newCerts; + mIsAppInstalledDeviceKeyAndCert = true; } /** @@ -1147,4 +1160,30 @@ public class WifiEnterpriseConfig implements Parcelable { } return true; } + + /** + * Check if certificate was installed by an app, or manually (not by an app). If true, + * certificate and keys will be removed from key storage when this network is removed. If not, + * then certificates and keys remain persistent until the user manually removes them. + * + * @return true if certificate was installed by an app, false if certificate was installed + * manually by the user. + * @hide + */ + public boolean isAppInstalledDeviceKeyAndCert() { + return mIsAppInstalledDeviceKeyAndCert; + } + + /** + * Check if CA certificate was installed by an app, or manually (not by an app). If true, + * CA certificate will be removed from key storage when this network is removed. If not, + * then certificates and keys remain persistent until the user manually removes them. + * + * @return true if CA certificate was installed by an app, false if CA certificate was installed + * manually by the user. + * @hide + */ + public boolean isAppInstalledCaCert() { + return mIsAppInstalledCaCert; + } } diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java index 32a7a477466d..a9c9939875e9 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java +++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java @@ -403,7 +403,7 @@ public final class WifiNetworkSuggestion implements Parcelable { * .setWpa3Passphrase("test6789") * .build() * final List<WifiNetworkSuggestion> suggestionsList = - * new ArrayList<WifiNetworkSuggestion> {{ + * new ArrayList<WifiNetworkSuggestion> {{ * add(suggestion1); * add(suggestion2); * add(suggestion3); diff --git a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java index 6ec64ffb0a06..beed6666f28f 100644 --- a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java +++ b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java @@ -423,4 +423,68 @@ public class WifiEnterpriseConfigTest { mEnterpriseConfig.setPassword(password); assertFalse(mEnterpriseConfig.toString().contains(password)); } + + /** Verifies that certificate ownership flag is set correctly */ + @Test + public void testIsAppInstalledDeviceKeyAndCert() { + // First make sure that app didn't install anything + assertFalse(mEnterpriseConfig.isAppInstalledDeviceKeyAndCert()); + assertFalse(mEnterpriseConfig.isAppInstalledCaCert()); + + // Then app loads keys via the enterprise config API + PrivateKey clientKey = FakeKeys.RSA_KEY1; + X509Certificate cert0 = FakeKeys.CLIENT_CERT; + X509Certificate cert1 = FakeKeys.CA_CERT1; + X509Certificate[] clientChain = new X509Certificate[] {cert0, cert1}; + mEnterpriseConfig.setClientKeyEntryWithCertificateChain(clientKey, clientChain); + X509Certificate[] result = mEnterpriseConfig.getClientCertificateChain(); + assertEquals(result.length, 2); + assertTrue(result[0] == cert0 && result[1] == cert1); + assertTrue(mEnterpriseConfig.getClientCertificate() == cert0); + + // Make sure it is the owner now + assertTrue(mEnterpriseConfig.isAppInstalledDeviceKeyAndCert()); + assertFalse(mEnterpriseConfig.isAppInstalledCaCert()); + } + + /** Verifies that certificate ownership flag is set correctly */ + @Test + public void testIsAppInstalledCaCert() { + // First make sure that app didn't install anything + assertFalse(mEnterpriseConfig.isAppInstalledDeviceKeyAndCert()); + assertFalse(mEnterpriseConfig.isAppInstalledCaCert()); + + // Then app loads CA cert via the enterprise config API + X509Certificate cert = FakeKeys.CA_CERT1; + mEnterpriseConfig.setCaCertificate(cert); + X509Certificate result = mEnterpriseConfig.getCaCertificate(); + assertTrue(result == cert); + + // Make sure it is the owner now + assertFalse(mEnterpriseConfig.isAppInstalledDeviceKeyAndCert()); + assertTrue(mEnterpriseConfig.isAppInstalledCaCert()); + } + + /** Verifies that certificate ownership flag is set correctly */ + @Test + public void testIsAppInstalledCaCerts() { + // First make sure that app didn't install anything + assertFalse(mEnterpriseConfig.isAppInstalledDeviceKeyAndCert()); + assertFalse(mEnterpriseConfig.isAppInstalledCaCert()); + + // Then app loads CA cert via the enterprise config API + X509Certificate cert0 = FakeKeys.CA_CERT0; + X509Certificate cert1 = FakeKeys.CA_CERT1; + X509Certificate[] cert = new X509Certificate[] {cert0, cert1}; + + mEnterpriseConfig.setCaCertificates(cert); + X509Certificate[] result = mEnterpriseConfig.getCaCertificates(); + assertEquals(result.length, 2); + assertTrue(result[0] == cert0 && result[1] == cert1); +// assertTrue(mEnterpriseConfig.getClientCertificate() == cert0); + + // Make sure it is the owner now + assertFalse(mEnterpriseConfig.isAppInstalledDeviceKeyAndCert()); + assertTrue(mEnterpriseConfig.isAppInstalledCaCert()); + } } |