diff options
598 files changed, 17187 insertions, 9770 deletions
diff --git a/Android.bp b/Android.bp index b26b373943ae..aa47d7bf4eac 100644 --- a/Android.bp +++ b/Android.bp @@ -307,6 +307,7 @@ java_library { "android.hardware.thermal-V1.1-java", "android.hardware.thermal-V2.0-java", "android.hardware.tv.input-V1.0-java-constants", + "android.hardware.tv.tuner-V1.0-java-constants", "android.hardware.usb-V1.0-java-constants", "android.hardware.usb-V1.1-java-constants", "android.hardware.usb-V1.2-java-constants", @@ -935,6 +936,7 @@ framework_docs_only_libs = [ ] metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " + + "--ignore-classes-on-classpath " + "--hide-package com.android.okhttp " + "--hide-package com.android.org.conscrypt --hide-package com.android.server " + "--error UnhiddenSystemApi " + @@ -1439,29 +1441,6 @@ droidstubs { " --show-annotation android.annotation.TestApi ", } -filegroup { - name: "apache-http-stubs-sources", - srcs: [ - "core/java/org/apache/http/conn/ConnectTimeoutException.java", - "core/java/org/apache/http/conn/scheme/HostNameResolver.java", - "core/java/org/apache/http/conn/scheme/LayeredSocketFactory.java", - "core/java/org/apache/http/conn/scheme/SocketFactory.java", - "core/java/org/apache/http/conn/ssl/AbstractVerifier.java", - "core/java/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java", - "core/java/org/apache/http/conn/ssl/AndroidDistinguishedNameParser.java", - "core/java/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java", - "core/java/org/apache/http/conn/ssl/SSLSocketFactory.java", - "core/java/org/apache/http/conn/ssl/StrictHostnameVerifier.java", - "core/java/org/apache/http/conn/ssl/X509HostnameVerifier.java", - "core/java/org/apache/http/params/CoreConnectionPNames.java", - "core/java/org/apache/http/params/HttpConnectionParams.java", - "core/java/org/apache/http/params/HttpParams.java", - "core/java/android/net/http/SslCertificate.java", - "core/java/android/net/http/SslError.java", - "core/java/com/android/internal/util/HexDump.java", - ], -} - droidstubs { name: "api-stubs-docs", defaults: ["metalava-api-stubs-default"], diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java index 1a10753d7bcb..5e5a04b1af3b 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java @@ -156,8 +156,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; -import java.time.Instant; -import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -2071,9 +2069,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { CompletableFuture<HistoricalOps> ops = new CompletableFuture<>(); HistoricalOpsRequest histOpsRequest = - new HistoricalOpsRequest.Builder( - Instant.now().minus(1, ChronoUnit.HOURS).toEpochMilli(), - Long.MAX_VALUE).build(); + new HistoricalOpsRequest.Builder(0, Long.MAX_VALUE).build(); appOps.getHistoricalOps(histOpsRequest, mContext.getMainExecutor(), ops::complete); HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, diff --git a/api/current.txt b/api/current.txt index fb65243b8e97..2cf1f44ebb6c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9231,14 +9231,16 @@ package android.companion { public final class WifiDeviceFilter implements android.companion.DeviceFilter<android.net.wifi.ScanResult> { method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); + method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.companion.WifiDeviceFilter> CREATOR; } public static final class WifiDeviceFilter.Builder { ctor public WifiDeviceFilter.Builder(); method @NonNull public android.companion.WifiDeviceFilter build(); - method public android.companion.WifiDeviceFilter.Builder setNamePattern(@Nullable java.util.regex.Pattern); + method @NonNull public android.companion.WifiDeviceFilter.Builder setBssid(@Nullable android.net.MacAddress); + method @NonNull public android.companion.WifiDeviceFilter.Builder setBssidMask(@NonNull android.net.MacAddress); + method @NonNull public android.companion.WifiDeviceFilter.Builder setNamePattern(@Nullable java.util.regex.Pattern); } } @@ -9465,6 +9467,7 @@ package android.content { method @NonNull public final android.content.ContentProvider.CallingIdentity clearCallingIdentity(); method public abstract int delete(@NonNull android.net.Uri, @Nullable String, @Nullable String[]); method public void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]); + method @Nullable public final String getCallingFeatureId(); method @Nullable public final String getCallingPackage(); method @Nullable public final android.content.Context getContext(); method @Nullable public final android.content.pm.PathPermission[] getPathPermissions(); @@ -30034,7 +30037,7 @@ package android.net.wifi { method public void setTdlsEnabled(java.net.InetAddress, boolean); method public void setTdlsEnabledWithMacAddress(String, boolean); method @Deprecated public boolean setWifiEnabled(boolean); - method public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, @Nullable android.os.Handler); + method @RequiresPermission(allOf={android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, @Nullable android.os.Handler); method @Deprecated public boolean startScan(); method @Deprecated public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback); method @Deprecated public int updateNetwork(android.net.wifi.WifiConfiguration); @@ -35503,7 +35506,7 @@ package android.os { method public String getUserName(); method public java.util.List<android.os.UserHandle> getUserProfiles(); method public android.os.Bundle getUserRestrictions(); - method public android.os.Bundle getUserRestrictions(android.os.UserHandle); + method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle); method public boolean hasUserRestriction(String); method public boolean isDemoUser(); method public boolean isQuietModeEnabled(android.os.UserHandle); @@ -38488,7 +38491,11 @@ package android.provider { method @NonNull public static String getVersion(@NonNull android.content.Context, @NonNull String); method @NonNull public static String getVolumeName(@NonNull android.net.Uri); method @NonNull public static android.net.Uri setIncludePending(@NonNull android.net.Uri); + method @NonNull public static android.net.Uri setIncludeTrashed(@NonNull android.net.Uri); method @NonNull public static android.net.Uri setRequireOriginal(@NonNull android.net.Uri); + method public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri); + method public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri, long); + method public static void untrash(@NonNull android.content.Context, @NonNull android.net.Uri); field public static final String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE"; field public static final String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE"; field public static final String ACTION_REVIEW = "android.provider.action.REVIEW"; @@ -38581,14 +38588,11 @@ package android.provider { } public static interface MediaStore.Audio.AudioColumns extends android.provider.MediaStore.MediaColumns { - field public static final String ALBUM = "album"; field public static final String ALBUM_ID = "album_id"; field @Deprecated public static final String ALBUM_KEY = "album_key"; - field public static final String ARTIST = "artist"; field public static final String ARTIST_ID = "artist_id"; field @Deprecated public static final String ARTIST_KEY = "artist_key"; field public static final String BOOKMARK = "bookmark"; - field public static final String COMPOSER = "composer"; field public static final String GENRE = "genre"; field public static final String GENRE_ID = "genre_id"; field @Deprecated public static final String GENRE_KEY = "genre_key"; @@ -38703,7 +38707,6 @@ package android.provider { field public static final int MEDIA_TYPE_VIDEO = 3; // 0x3 field public static final String MIME_TYPE = "mime_type"; field public static final String PARENT = "parent"; - field public static final String TITLE = "title"; } public static final class MediaStore.Images { @@ -38712,6 +38715,9 @@ package android.provider { public static interface MediaStore.Images.ImageColumns extends android.provider.MediaStore.MediaColumns { field public static final String DESCRIPTION = "description"; + field public static final String EXPOSURE_TIME = "exposure_time"; + field public static final String F_NUMBER = "f_number"; + field public static final String ISO = "iso"; field public static final String IS_PRIVATE = "isprivate"; field @Deprecated public static final String LATITUDE = "latitude"; field @Deprecated public static final String LONGITUDE = "longitude"; @@ -38760,28 +38766,45 @@ package android.provider { } public static interface MediaStore.MediaColumns extends android.provider.BaseColumns { + field public static final String ALBUM = "album"; + field public static final String ALBUM_ARTIST = "album_artist"; + field public static final String ARTIST = "artist"; + field public static final String AUTHOR = "author"; + field public static final String BITRATE = "bitrate"; field public static final String BUCKET_DISPLAY_NAME = "bucket_display_name"; field public static final String BUCKET_ID = "bucket_id"; + field public static final String CAPTURE_FRAMERATE = "capture_framerate"; + field public static final String CD_TRACK_NUMBER = "cd_track_number"; + field public static final String COMPILATION = "compilation"; + field public static final String COMPOSER = "composer"; field @Deprecated public static final String DATA = "_data"; field public static final String DATE_ADDED = "date_added"; field public static final String DATE_EXPIRES = "date_expires"; field public static final String DATE_MODIFIED = "date_modified"; field public static final String DATE_TAKEN = "datetaken"; + field public static final String DISC_NUMBER = "disc_number"; field public static final String DISPLAY_NAME = "_display_name"; field public static final String DOCUMENT_ID = "document_id"; field public static final String DURATION = "duration"; + field public static final String GENRE = "genre"; field public static final String HEIGHT = "height"; field public static final String INSTANCE_ID = "instance_id"; + field public static final String IS_FAVORITE = "is_favorite"; field public static final String IS_PENDING = "is_pending"; + field public static final String IS_TRASHED = "is_trashed"; field public static final String MIME_TYPE = "mime_type"; + field public static final String NUM_TRACKS = "num_tracks"; field public static final String ORIENTATION = "orientation"; field public static final String ORIGINAL_DOCUMENT_ID = "original_document_id"; field public static final String OWNER_PACKAGE_NAME = "owner_package_name"; field public static final String RELATIVE_PATH = "relative_path"; + field public static final String RESOLUTION = "resolution"; field public static final String SIZE = "_size"; field public static final String TITLE = "title"; field public static final String VOLUME_NAME = "volume_name"; field public static final String WIDTH = "width"; + field public static final String WRITER = "writer"; + field public static final String YEAR = "year"; } public static final class MediaStore.Video { @@ -38821,8 +38844,6 @@ package android.provider { } public static interface MediaStore.Video.VideoColumns extends android.provider.MediaStore.MediaColumns { - field public static final String ALBUM = "album"; - field public static final String ARTIST = "artist"; field public static final String BOOKMARK = "bookmark"; field public static final String CATEGORY = "category"; field public static final String COLOR_RANGE = "color_range"; @@ -38834,7 +38855,6 @@ package android.provider { field @Deprecated public static final String LATITUDE = "latitude"; field @Deprecated public static final String LONGITUDE = "longitude"; field @Deprecated public static final String MINI_THUMB_MAGIC = "mini_thumb_magic"; - field public static final String RESOLUTION = "resolution"; field public static final String TAGS = "tags"; } @@ -38917,6 +38937,7 @@ package android.provider { field public static final String ACTION_SECURITY_SETTINGS = "android.settings.SECURITY_SETTINGS"; field public static final String ACTION_SETTINGS = "android.settings.SETTINGS"; field public static final String ACTION_SHOW_REGULATORY_INFO = "android.settings.SHOW_REGULATORY_INFO"; + field public static final String ACTION_SHOW_WORK_POLICY_INFO = "android.settings.SHOW_WORK_POLICY_INFO"; field public static final String ACTION_SOUND_SETTINGS = "android.settings.SOUND_SETTINGS"; field @Deprecated public static final String ACTION_STORAGE_VOLUME_ACCESS_SETTINGS = "android.settings.STORAGE_VOLUME_ACCESS_SETTINGS"; field public static final String ACTION_SYNC_SETTINGS = "android.settings.SYNC_SETTINGS"; @@ -44377,6 +44398,7 @@ package android.telephony { field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT = "opportunistic_network_exit_threshold_rsrp_int"; field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT = "opportunistic_network_exit_threshold_rssnr_int"; field public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool"; + field public static final String KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL = "prevent_clir_activation_and_deactivation_code_bool"; field public static final String KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY = "radio_restart_failure_causes_int_array"; field public static final String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string"; field public static final String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool"; diff --git a/api/removed.txt b/api/removed.txt index 74a934672adb..a395cc786507 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -444,13 +444,12 @@ package android.provider { method @Deprecated @NonNull public static android.net.Uri createPending(@NonNull android.content.Context, @NonNull android.provider.MediaStore.PendingParams); method @Deprecated @NonNull public static java.util.Set<java.lang.String> getAllVolumeNames(@NonNull android.content.Context); method @Deprecated @NonNull public static android.provider.MediaStore.PendingSession openPending(@NonNull android.content.Context, @NonNull android.net.Uri); - method @Deprecated @NonNull public static android.net.Uri setIncludeTrashed(@NonNull android.net.Uri); - method @Deprecated public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri); - method @Deprecated public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri, long); - method @Deprecated public static void untrash(@NonNull android.content.Context, @NonNull android.net.Uri); } public static interface MediaStore.Audio.AudioColumns extends android.provider.MediaStore.MediaColumns { + field public static final String ALBUM = "album"; + field public static final String ARTIST = "artist"; + field public static final String COMPOSER = "composer"; field public static final String DURATION = "duration"; } @@ -458,6 +457,10 @@ package android.provider { field @Deprecated public static final String DESCRIPTION = "description"; } + public static interface MediaStore.Files.FileColumns extends android.provider.MediaStore.MediaColumns { + field public static final String TITLE = "title"; + } + public static interface MediaStore.Images.ImageColumns extends android.provider.MediaStore.MediaColumns { field public static final String BUCKET_DISPLAY_NAME = "bucket_display_name"; field public static final String BUCKET_ID = "bucket_id"; @@ -468,10 +471,6 @@ package android.provider { public static interface MediaStore.MediaColumns extends android.provider.BaseColumns { field @Deprecated public static final String GROUP_ID = "group_id"; - field @Deprecated public static final String HASH = "_hash"; - field @Deprecated public static final String IS_TRASHED = "is_trashed"; - field @Deprecated public static final String PRIMARY_DIRECTORY = "primary_directory"; - field @Deprecated public static final String SECONDARY_DIRECTORY = "secondary_directory"; } @Deprecated public static class MediaStore.PendingParams { @@ -491,11 +490,14 @@ package android.provider { } public static interface MediaStore.Video.VideoColumns extends android.provider.MediaStore.MediaColumns { + field public static final String ALBUM = "album"; + field public static final String ARTIST = "artist"; field public static final String BUCKET_DISPLAY_NAME = "bucket_display_name"; field public static final String BUCKET_ID = "bucket_id"; field public static final String DATE_TAKEN = "datetaken"; field public static final String DURATION = "duration"; field public static final String GROUP_ID = "group_id"; + field public static final String RESOLUTION = "resolution"; } public static final class Settings.Global extends android.provider.Settings.NameValueTable { diff --git a/api/system-current.txt b/api/system-current.txt index b9dfad5d5df5..1a6ac8d12b9f 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3480,7 +3480,8 @@ package android.location { method @NonNull public static android.location.LocationRequest createFromDeprecatedCriteria(@NonNull android.location.Criteria, long, float, boolean); method @NonNull public static android.location.LocationRequest createFromDeprecatedProvider(@NonNull String, long, float, boolean); method public int describeContents(); - method public long getExpireAt(); + method @Deprecated public long getExpireAt(); + method public long getExpireIn(); method public long getFastestInterval(); method public boolean getHideFromAppOps(); method public long getInterval(); @@ -3491,7 +3492,7 @@ package android.location { method @Nullable public android.os.WorkSource getWorkSource(); method public boolean isLocationSettingsIgnored(); method public boolean isLowPowerMode(); - method @NonNull public android.location.LocationRequest setExpireAt(long); + method @Deprecated @NonNull public android.location.LocationRequest setExpireAt(long); method @NonNull public android.location.LocationRequest setExpireIn(long); method @NonNull public android.location.LocationRequest setFastestInterval(long); method public void setHideFromAppOps(boolean); @@ -3639,7 +3640,7 @@ package android.media { public final class MediaRecorder.AudioSource { field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int ECHO_REFERENCE = 1997; // 0x7cd field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD) public static final int HOTWORD = 1999; // 0x7cf - field public static final int RADIO_TUNER = 1998; // 0x7ce + field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int RADIO_TUNER = 1998; // 0x7ce } public class PlayerProxy { @@ -4734,6 +4735,24 @@ package android.net.wifi { field @Deprecated public byte id; } + public final class SoftApConfiguration implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.net.MacAddress getBssid(); + method @Nullable public String getSsid(); + method @Nullable public String getWpa2Passphrase(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApConfiguration> CREATOR; + } + + public static final class SoftApConfiguration.Builder { + ctor public SoftApConfiguration.Builder(); + ctor public SoftApConfiguration.Builder(@NonNull android.net.wifi.SoftApConfiguration); + method @NonNull public android.net.wifi.SoftApConfiguration build(); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBssid(@Nullable android.net.MacAddress); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setWpa2Passphrase(@Nullable String); + } + public final class WifiClient implements android.os.Parcelable { method public int describeContents(); method @NonNull public android.net.MacAddress getMacAddress(); @@ -4746,6 +4765,7 @@ package android.net.wifi { method @Deprecated public boolean isEphemeral(); method @Deprecated public boolean isNoInternetAccessExpected(); field @Deprecated public boolean allowAutojoin; + field @Deprecated public int carrierId; field @Deprecated public String creatorName; field @Deprecated public int creatorUid; field @Deprecated public String lastUpdateName; @@ -4789,6 +4809,7 @@ package android.net.wifi { method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration); method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback); method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeInitiator(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback); + method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startLocalOnlyHotspot(@NonNull android.net.wifi.SoftApConfiguration, @Nullable java.util.concurrent.Executor, @Nullable android.net.wifi.WifiManager.LocalOnlyHotspotCallback); method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public boolean startScan(android.os.WorkSource); method @RequiresPermission(anyOf={"android.permission.NETWORK_STACK", android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean startSoftAp(@Nullable android.net.wifi.WifiConfiguration); method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startSubscriptionProvisioning(@NonNull android.net.wifi.hotspot2.OsuProvider, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.hotspot2.ProvisioningCallback); @@ -4852,10 +4873,14 @@ package android.net.wifi { field public int numUsage; } + public static final class WifiNetworkSuggestion.Builder { + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public android.net.wifi.WifiNetworkSuggestion.Builder setCarrierId(int); + } + public class WifiScanner { method @Deprecated public void configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.BssidInfo[]); method @Deprecated public void configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings); - method @Nullable @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public java.util.List<java.lang.Integer> getAvailableChannels(int); + method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public java.util.List<java.lang.Integer> getAvailableChannels(int); method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean getScanResults(); method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startBackgroundScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener); method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startBackgroundScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener, android.os.WorkSource); @@ -5666,6 +5691,7 @@ package android.os { 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(); method public boolean hasRestrictedProfiles(); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean hasUserRestrictionForUser(@NonNull String, @NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isAdminUser(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isGuestUser(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isManagedProfile(); @@ -6246,15 +6272,21 @@ package android.provider { field public static final String DELIVERY_TIME = "date"; field public static final String ETWS_WARNING_TYPE = "etws_warning_type"; field public static final String GEOGRAPHICAL_SCOPE = "geo_scope"; + field public static final String GEOMETRIES = "geometries"; field public static final String LAC = "lac"; field public static final String LANGUAGE_CODE = "language"; + field public static final String MAXIMUM_WAIT_TIME = "maximum_wait_time"; field public static final String MESSAGE_BODY = "body"; + field public static final String MESSAGE_BROADCASTED = "message_broadcasted"; field public static final String MESSAGE_FORMAT = "format"; + field @RequiresPermission(android.Manifest.permission.READ_CELL_BROADCASTS) @NonNull public static final android.net.Uri MESSAGE_HISTORY_URI; field public static final String MESSAGE_PRIORITY = "priority"; field public static final String MESSAGE_READ = "read"; field public static final String PLMN = "plmn"; + field public static final String RECEIVED_TIME = "received_time"; field public static final String SERIAL_NUMBER = "serial_number"; field public static final String SERVICE_CATEGORY = "service_category"; + field public static final String SLOT_INDEX = "slot_index"; } public final class TimeZoneRulesDataContract { @@ -6532,6 +6564,28 @@ package android.service.carrier { method @WorkerThread @NonNull public abstract java.util.List<android.content.ContentValues> onRestoreApns(int); } + public abstract class CarrierMessagingServiceWrapper { + ctor public CarrierMessagingServiceWrapper(); + method public boolean bindToCarrierMessagingService(@NonNull android.content.Context, @NonNull String); + method public void disposeConnection(@NonNull android.content.Context); + method public void downloadMms(@NonNull android.net.Uri, int, @NonNull android.net.Uri, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper); + method public void filterSms(@NonNull android.service.carrier.MessagePdu, @NonNull String, int, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper); + method public abstract void onServiceReady(); + method public void sendDataSms(@NonNull byte[], int, @NonNull String, int, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper); + method public void sendMms(@NonNull android.net.Uri, int, @NonNull android.net.Uri, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper); + method public void sendMultipartTextSms(@NonNull java.util.List<java.lang.String>, int, @NonNull String, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper); + method public void sendTextSms(@NonNull String, int, @NonNull String, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper); + } + + public abstract static class CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper { + ctor public CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper(); + method public void onDownloadMmsComplete(int); + method public void onFilterComplete(int); + method public void onSendMmsComplete(int, @Nullable byte[]); + method public void onSendMultipartSmsComplete(int, @Nullable int[]); + method public void onSendSmsComplete(int, int); + } + } package android.service.contentcapture { @@ -6962,9 +7016,11 @@ package android.service.textclassifier { method @Deprecated public final android.view.textclassifier.TextClassifier getLocalTextClassifier(); method @Nullable public final android.os.IBinder onBind(android.content.Intent); method @MainThread public abstract void onClassifyText(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextClassification.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextClassification>); + method public void onConnected(); method @MainThread public void onCreateTextClassificationSession(@NonNull android.view.textclassifier.TextClassificationContext, @NonNull android.view.textclassifier.TextClassificationSessionId); method @MainThread public void onDestroyTextClassificationSession(@NonNull android.view.textclassifier.TextClassificationSessionId); method @MainThread public void onDetectLanguage(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextLanguage.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLanguage>); + method public void onDisconnected(); method @MainThread public abstract void onGenerateLinks(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextLinks.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLinks>); method @Deprecated @MainThread public void onSelectionEvent(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.SelectionEvent); method @MainThread public void onSuggestConversationActions(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.ConversationActions.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.ConversationActions>); @@ -7429,6 +7485,7 @@ package android.telephony { ctor public CellBroadcastService(); method @CallSuper public android.os.IBinder onBind(android.content.Intent); method public abstract void onCdmaCellBroadcastSms(int, byte[], int); + method public abstract void onCdmaScpMessage(int, @NonNull java.util.List<android.telephony.cdma.CdmaSmsCbProgramData>, @NonNull String, @NonNull java.util.function.Consumer<android.os.Bundle>); method public abstract void onGsmCellBroadcastSms(int, byte[]); field public static final String CELL_BROADCAST_SERVICE_INTERFACE = "android.telephony.CellBroadcastService"; } @@ -8341,6 +8398,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean enableDataConnectivity(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean enableModemForSlot(int, boolean); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void enableVideoCalling(boolean); + method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void factoryReset(int); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getAidForAppType(int); method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent); @@ -9008,17 +9066,23 @@ package android.telephony.ims { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR; } - public class ImsMmTelManager { + public class ImsMmTelManager implements android.telephony.ims.RegistrationManager { method @NonNull public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiModeSetting(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiRoamingModeSetting(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAdvancedCallingSettingEnabled(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void isSupported(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, @NonNull java.util.function.Consumer<java.lang.Boolean>, @NonNull java.util.concurrent.Executor) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isTtyOverVolteEnabled(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiRoamingSettingEnabled(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiSettingEnabled(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVtSettingEnabled(); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException; + method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback, @NonNull java.util.concurrent.Executor) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSettingEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean); @@ -9028,7 +9092,8 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingSettingEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiSettingEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVtSettingEnabled(boolean); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback); + method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterMmTelCapabilityCallback(@NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback); field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1 field public static final int WIFI_MODE_WIFI_ONLY = 0; // 0x0 @@ -9040,12 +9105,8 @@ package android.telephony.ims { method public void onCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities); } - public static class ImsMmTelManager.RegistrationCallback { - ctor public ImsMmTelManager.RegistrationCallback(); - method public void onRegistered(int); - method public void onRegistering(int); - method public void onTechnologyChangeFailed(int, @Nullable android.telephony.ims.ImsReasonInfo); - method public void onUnregistered(@Nullable android.telephony.ims.ImsReasonInfo); + @Deprecated public static class ImsMmTelManager.RegistrationCallback extends android.telephony.ims.RegistrationManager.RegistrationCallback { + ctor @Deprecated public ImsMmTelManager.RegistrationCallback(); } public final class ImsReasonInfo implements android.os.Parcelable { @@ -9477,6 +9538,24 @@ package android.telephony.ims { method public void onProvisioningStringChanged(int, @NonNull String); } + public interface RegistrationManager { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback, @NonNull java.util.concurrent.Executor) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); + field public static final int REGISTRATION_STATE_NOT_REGISTERED = 0; // 0x0 + field public static final int REGISTRATION_STATE_REGISTERED = 2; // 0x2 + field public static final int REGISTRATION_STATE_REGISTERING = 1; // 0x1 + } + + public static class RegistrationManager.RegistrationCallback { + ctor public RegistrationManager.RegistrationCallback(); + method public void onRegistered(int); + method public void onRegistering(int); + method public void onTechnologyChangeFailed(int, @Nullable android.telephony.ims.ImsReasonInfo); + method public void onUnregistered(@Nullable android.telephony.ims.ImsReasonInfo); + } + } package android.telephony.ims.feature { @@ -9541,6 +9620,8 @@ package android.telephony.ims.feature { method public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus(); method public void setUiTtyMode(int, @Nullable android.os.Message); method @android.telephony.ims.feature.MmTelFeature.ProcessCallResult public int shouldProcessCall(@NonNull String[]); + field public static final String EXTRA_IS_UNKNOWN_CALL = "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL"; + field public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD"; field public static final int PROCESS_CALL_CSFB = 1; // 0x1 field public static final int PROCESS_CALL_IMS = 0; // 0x0 } diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt index 57a853a42676..a907fa64d9ce 100644 --- a/api/system-lint-baseline.txt +++ b/api/system-lint-baseline.txt @@ -257,6 +257,8 @@ SamShouldBeLast: android.media.session.MediaSessionManager#registerCallback(java SamShouldBeLast: android.net.ConnectivityManager#createSocketKeepalive(android.net.Network, android.net.IpSecManager.UdpEncapsulationSocket, java.net.InetAddress, java.net.InetAddress, java.util.concurrent.Executor, android.net.SocketKeepalive.Callback): +SamShouldBeLast: android.net.wifi.WifiManager#startLocalOnlyHotspot(android.net.wifi.SoftApConfiguration, java.util.concurrent.Executor, android.net.wifi.WifiManager.LocalOnlyHotspotCallback): + SamShouldBeLast: android.net.wifi.rtt.WifiRttManager#startRanging(android.net.wifi.rtt.RangingRequest, java.util.concurrent.Executor, android.net.wifi.rtt.RangingResultCallback): SamShouldBeLast: android.nfc.NfcAdapter#enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle): diff --git a/api/test-current.txt b/api/test-current.txt index a060dfcb8311..5b167432373f 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1136,17 +1136,20 @@ package android.location { public final class LocationRequest implements android.os.Parcelable { method @NonNull public static android.location.LocationRequest create(); method public int describeContents(); - method public long getExpireAt(); + method @Deprecated public long getExpireAt(); + method public long getExpireIn(); method public long getFastestInterval(); method public long getInterval(); method public int getNumUpdates(); method public int getQuality(); method public boolean isLocationSettingsIgnored(); - method @NonNull public android.location.LocationRequest setExpireAt(long); + method public boolean isLowPowerMode(); + method @Deprecated @NonNull public android.location.LocationRequest setExpireAt(long); method @NonNull public android.location.LocationRequest setExpireIn(long); method @NonNull public android.location.LocationRequest setFastestInterval(long); method @NonNull public android.location.LocationRequest setInterval(long); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @NonNull public android.location.LocationRequest setLocationSettingsIgnored(boolean); + method @NonNull public android.location.LocationRequest setLowPowerMode(boolean); method @NonNull public android.location.LocationRequest setNumUpdates(int); method @NonNull public android.location.LocationRequest setProvider(@NonNull String); method @NonNull public android.location.LocationRequest setQuality(int); @@ -2063,6 +2066,8 @@ package android.os { method @NonNull public static String get(@NonNull String); method @NonNull public static String get(@NonNull String, @Nullable String); method public static boolean getBoolean(@NonNull String, boolean); + method public static int getInt(@NonNull String, int); + method public static long getLong(@NonNull String, long); } public final class UserHandle implements android.os.Parcelable { @@ -2456,6 +2461,36 @@ package android.provider { field public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service"; } + public static final class Telephony.CellBroadcasts implements android.provider.BaseColumns { + field public static final String CID = "cid"; + field public static final String CMAS_CATEGORY = "cmas_category"; + field public static final String CMAS_CERTAINTY = "cmas_certainty"; + field public static final String CMAS_MESSAGE_CLASS = "cmas_message_class"; + field public static final String CMAS_RESPONSE_TYPE = "cmas_response_type"; + field public static final String CMAS_SEVERITY = "cmas_severity"; + field public static final String CMAS_URGENCY = "cmas_urgency"; + field @NonNull public static final android.net.Uri CONTENT_URI; + field public static final String DEFAULT_SORT_ORDER = "date DESC"; + field public static final String DELIVERY_TIME = "date"; + field public static final String ETWS_WARNING_TYPE = "etws_warning_type"; + field public static final String GEOGRAPHICAL_SCOPE = "geo_scope"; + field public static final String GEOMETRIES = "geometries"; + field public static final String LAC = "lac"; + field public static final String LANGUAGE_CODE = "language"; + field public static final String MAXIMUM_WAIT_TIME = "maximum_wait_time"; + field public static final String MESSAGE_BODY = "body"; + field public static final String MESSAGE_BROADCASTED = "message_broadcasted"; + field public static final String MESSAGE_FORMAT = "format"; + field @RequiresPermission(android.Manifest.permission.READ_CELL_BROADCASTS) @NonNull public static final android.net.Uri MESSAGE_HISTORY_URI; + field public static final String MESSAGE_PRIORITY = "priority"; + field public static final String MESSAGE_READ = "read"; + field public static final String PLMN = "plmn"; + field public static final String RECEIVED_TIME = "received_time"; + field public static final String SERIAL_NUMBER = "serial_number"; + field public static final String SERVICE_CATEGORY = "service_category"; + field public static final String SLOT_INDEX = "slot_index"; + } + public static final class Telephony.Sms.Intents { field public static final String SMS_CARRIER_PROVISION_ACTION = "android.provider.Telephony.SMS_CARRIER_PROVISION"; } diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java index 55dbc17dba5d..7e278e964ab5 100644 --- a/cmds/content/src/com/android/commands/content/Content.java +++ b/cmds/content/src/com/android/commands/content/Content.java @@ -508,7 +508,7 @@ public class Content { @Override public void onExecute(IContentProvider provider) throws Exception { - provider.insert(resolveCallingPackage(), mUri, mContentValues); + provider.insert(resolveCallingPackage(), null, mUri, mContentValues); } } @@ -522,7 +522,7 @@ public class Content { @Override public void onExecute(IContentProvider provider) throws Exception { - provider.delete(resolveCallingPackage(), mUri, mWhere, null); + provider.delete(resolveCallingPackage(), null, mUri, mWhere, null); } } @@ -557,7 +557,7 @@ public class Content { @Override public void onExecute(IContentProvider provider) throws Exception { - Bundle result = provider.call(null, mUri.getAuthority(), mMethod, mArg, mExtras); + Bundle result = provider.call(null, null, mUri.getAuthority(), mMethod, mArg, mExtras); if (result != null) { result.size(); // unpack } @@ -584,7 +584,7 @@ public class Content { @Override public void onExecute(IContentProvider provider) throws Exception { - try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "r", null, null)) { + try (ParcelFileDescriptor fd = provider.openFile(null, null, mUri, "r", null, null)) { FileUtils.copy(fd.getFileDescriptor(), FileDescriptor.out); } } @@ -597,7 +597,7 @@ public class Content { @Override public void onExecute(IContentProvider provider) throws Exception { - try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "w", null, null)) { + try (ParcelFileDescriptor fd = provider.openFile(null, null, mUri, "w", null, null)) { FileUtils.copy(FileDescriptor.in, fd.getFileDescriptor()); } } @@ -616,7 +616,7 @@ public class Content { @Override public void onExecute(IContentProvider provider) throws Exception { - Cursor cursor = provider.query(resolveCallingPackage(), mUri, mProjection, + Cursor cursor = provider.query(resolveCallingPackage(), null, mUri, mProjection, ContentResolver.createSqlQueryBundle(mWhere, null, mSortOrder), null); if (cursor == null) { System.out.println("No result found."); @@ -679,7 +679,7 @@ public class Content { @Override public void onExecute(IContentProvider provider) throws Exception { - provider.update(resolveCallingPackage(), mUri, mContentValues, mWhere, null); + provider.update(resolveCallingPackage(), null, mUri, mContentValues, mWhere, null); } } diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 6c3dff24e5fe..91cadc97f192 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -200,6 +200,10 @@ void StatsLogProcessor::resetConfigsLocked(const int64_t timestampNs) { } void StatsLogProcessor::OnLogEvent(LogEvent* event) { + OnLogEvent(event, getElapsedRealtimeNs()); +} + +void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) { std::lock_guard<std::mutex> lock(mMetricsMutex); #ifdef VERY_VERBOSE_PRINTING @@ -207,9 +211,9 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { ALOGI("%s", event->ToString().c_str()); } #endif - const int64_t currentTimestampNs = event->GetElapsedTimestampNs(); + const int64_t eventElapsedTimeNs = event->GetElapsedTimestampNs(); - resetIfConfigTtlExpiredLocked(currentTimestampNs); + resetIfConfigTtlExpiredLocked(eventElapsedTimeNs); StatsdStats::getInstance().noteAtomLogged( event->GetTagId(), event->GetElapsedTimestampNs() / NS_PER_SEC); @@ -264,15 +268,16 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { uidsWithActiveConfigsChanged.insert(uid); StatsdStats::getInstance().noteActiveStatusChanged(pair.first, isCurActive); } - flushIfNecessaryLocked(event->GetElapsedTimestampNs(), pair.first, *(pair.second)); + flushIfNecessaryLocked(pair.first, *(pair.second)); } + // Don't use the event timestamp for the guardrail. for (int uid : uidsWithActiveConfigsChanged) { // Send broadcast so that receivers can pull data. auto lastBroadcastTime = mLastActivationBroadcastTimes.find(uid); if (lastBroadcastTime != mLastActivationBroadcastTimes.end()) { - if (currentTimestampNs - lastBroadcastTime->second < - StatsdStats::kMinActivationBroadcastPeriodNs) { + if (elapsedRealtimeNs - lastBroadcastTime->second < + StatsdStats::kMinActivationBroadcastPeriodNs) { StatsdStats::getInstance().noteActivationBroadcastGuardrailHit(uid); VLOG("StatsD would've sent an activation broadcast but the rate limit stopped us."); return; @@ -282,13 +287,13 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { if (activeConfigs != activeConfigsPerUid.end()) { if (mSendActivationBroadcast(uid, activeConfigs->second)) { VLOG("StatsD sent activation notice for uid %d", uid); - mLastActivationBroadcastTimes[uid] = currentTimestampNs; + mLastActivationBroadcastTimes[uid] = elapsedRealtimeNs; } } else { std::vector<int64_t> emptyActiveConfigs; if (mSendActivationBroadcast(uid, emptyActiveConfigs)) { VLOG("StatsD sent EMPTY activation notice for uid %d", uid); - mLastActivationBroadcastTimes[uid] = currentTimestampNs; + mLastActivationBroadcastTimes[uid] = elapsedRealtimeNs; } } } @@ -550,22 +555,23 @@ void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { } } -void StatsLogProcessor::flushIfNecessaryLocked( - int64_t timestampNs, const ConfigKey& key, MetricsManager& metricsManager) { +void StatsLogProcessor::flushIfNecessaryLocked(const ConfigKey& key, + MetricsManager& metricsManager) { + int64_t elapsedRealtimeNs = getElapsedRealtimeNs(); auto lastCheckTime = mLastByteSizeTimes.find(key); if (lastCheckTime != mLastByteSizeTimes.end()) { - if (timestampNs - lastCheckTime->second < StatsdStats::kMinByteSizeCheckPeriodNs) { + if (elapsedRealtimeNs - lastCheckTime->second < StatsdStats::kMinByteSizeCheckPeriodNs) { return; } } // We suspect that the byteSize() computation is expensive, so we set a rate limit. size_t totalBytes = metricsManager.byteSize(); - mLastByteSizeTimes[key] = timestampNs; + mLastByteSizeTimes[key] = elapsedRealtimeNs; bool requestDump = false; - if (totalBytes > - StatsdStats::kMaxMetricsBytesPerConfig) { // Too late. We need to start clearing data. - metricsManager.dropData(timestampNs); + if (totalBytes > StatsdStats::kMaxMetricsBytesPerConfig) { + // Too late. We need to start clearing data. + metricsManager.dropData(elapsedRealtimeNs); StatsdStats::getInstance().noteDataDropped(key, totalBytes); VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str()); } else if ((totalBytes > StatsdStats::kBytesPerConfigTriggerGetData) || @@ -580,7 +586,8 @@ void StatsLogProcessor::flushIfNecessaryLocked( // Send broadcast so that receivers can pull data. auto lastBroadcastTime = mLastBroadcastTimes.find(key); if (lastBroadcastTime != mLastBroadcastTimes.end()) { - if (timestampNs - lastBroadcastTime->second < StatsdStats::kMinBroadcastPeriodNs) { + if (elapsedRealtimeNs - lastBroadcastTime->second < + StatsdStats::kMinBroadcastPeriodNs) { VLOG("StatsD would've sent a broadcast but the rate limit stopped us."); return; } @@ -588,7 +595,7 @@ void StatsLogProcessor::flushIfNecessaryLocked( if (mSendBroadcast(key)) { mOnDiskDataConfigs.erase(key); VLOG("StatsD triggered data fetch for %s", key.ToString().c_str()); - mLastBroadcastTimes[key] = timestampNs; + mLastBroadcastTimes[key] = elapsedRealtimeNs; StatsdStats::getInstance().noteBroadcastSent(key); } } diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 8292a3a9194a..68b12189aa0e 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -147,6 +147,8 @@ private: sp<AlarmMonitor> mPeriodicAlarmMonitor; + void OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs); + void resetIfConfigTtlExpiredLocked(const int64_t timestampNs); void OnConfigUpdatedLocked( @@ -176,8 +178,7 @@ private: /* Check if we should send a broadcast if approaching memory limits and if we're over, we * actually delete the data. */ - void flushIfNecessaryLocked(int64_t timestampNs, const ConfigKey& key, - MetricsManager& metricsManager); + void flushIfNecessaryLocked(const ConfigKey& key, MetricsManager& metricsManager); // Maps the isolated uid in the log event to host uid if the log event contains uid fields. void mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const; diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index 460b9e0995c8..69e11ed9b836 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -76,9 +76,9 @@ TEST(StatsLogProcessorTest, TestRateLimitByteSize) { // Expect only the first flush to trigger a check for byte size since the last two are // rate-limited. EXPECT_CALL(mockMetricsManager, byteSize()).Times(1); - p.flushIfNecessaryLocked(99, key, mockMetricsManager); - p.flushIfNecessaryLocked(100, key, mockMetricsManager); - p.flushIfNecessaryLocked(101, key, mockMetricsManager); + p.flushIfNecessaryLocked(key, mockMetricsManager); + p.flushIfNecessaryLocked(key, mockMetricsManager); + p.flushIfNecessaryLocked(key, mockMetricsManager); } TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { @@ -103,7 +103,7 @@ TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { StatsdStats::kMaxMetricsBytesPerConfig * .95))); // Expect only one broadcast despite always returning a size that should trigger broadcast. - p.flushIfNecessaryLocked(1, key, mockMetricsManager); + p.flushIfNecessaryLocked(key, mockMetricsManager); EXPECT_EQ(1, broadcastCount); // b/73089712 @@ -136,7 +136,7 @@ TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { EXPECT_CALL(mockMetricsManager, dropData(_)).Times(1); // Expect to call the onDumpReport and skip the broadcast. - p.flushIfNecessaryLocked(1, key, mockMetricsManager); + p.flushIfNecessaryLocked(key, mockMetricsManager); EXPECT_EQ(0, broadcastCount); } diff --git a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp index 5da0fca2f3ed..909315552e3f 100644 --- a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp @@ -271,19 +271,19 @@ TEST(DurationMetricE2eTest, TestWithActivation) { // Turn screen off. event = CreateScreenStateChangedEvent( android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 2 * NS_PER_SEC); // 0:02 - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 2 * NS_PER_SEC); // Turn screen on. const int64_t durationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:05 event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), durationStartNs); // Activate metric. const int64_t activationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:10 const int64_t activationEndNs = activationStartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 0:40 event = CreateAppCrashEvent(111, activationStartNs); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), activationStartNs); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 1); @@ -296,7 +296,7 @@ TEST(DurationMetricE2eTest, TestWithActivation) { // Expire activation. const int64_t expirationNs = activationEndNs + 7 * NS_PER_SEC; event = CreateScreenBrightnessChangedEvent(64, expirationNs); // 0:47 - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), expirationNs); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 2); @@ -310,24 +310,24 @@ TEST(DurationMetricE2eTest, TestWithActivation) { // Turn off screen 10 seconds after activation expiration. const int64_t durationEndNs = activationEndNs + 10 * NS_PER_SEC; // 0:50 event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, durationEndNs); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(),durationEndNs); // Turn screen on. const int64_t duration2StartNs = durationEndNs + 5 * NS_PER_SEC; // 0:55 event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, duration2StartNs); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), duration2StartNs); // Turn off screen. const int64_t duration2EndNs = duration2StartNs + 10 * NS_PER_SEC; // 1:05 event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, duration2EndNs); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), duration2EndNs); // Activate metric. const int64_t activation2StartNs = duration2EndNs + 5 * NS_PER_SEC; // 1:10 const int64_t activation2EndNs = activation2StartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 1:40 event = CreateAppCrashEvent(211, activation2StartNs); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), activation2StartNs); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 3); diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp index f1b6029f0ab0..b6a6492fce75 100644 --- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp @@ -290,14 +290,14 @@ TEST(MetricActivationE2eTest, TestCountMetric) { std::unique_ptr<LogEvent> event; event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 0); // Activated by battery save mode. event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 1); @@ -312,12 +312,12 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // First processed event. event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); // Activated by screen on event. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 20); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); @@ -330,7 +330,7 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // 2nd processed event. // The activation by screen_on event expires, but the one by battery save mode is still active. event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); @@ -344,11 +344,11 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // 3rd processed event. event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); // All activations expired. event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); // New broadcast since the config is no longer active. @@ -364,7 +364,7 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // Re-activate metric via screen on. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 3); @@ -379,7 +379,7 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // 4th processed event. event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); ConfigMetricsReportList reports; vector<uint8_t> buffer; @@ -509,14 +509,14 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { std::unique_ptr<LogEvent> event; event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 0); // Activated by battery save mode. event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 1); @@ -532,12 +532,12 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // First processed event. event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); // Activated by screen on event. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 20); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); @@ -551,7 +551,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // 2nd processed event. // The activation by screen_on event expires, but the one by battery save mode is still active. event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(),bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); @@ -566,11 +566,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // 3rd processed event. event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); // All activations expired. event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); // New broadcast since the config is no longer active. @@ -587,7 +587,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // Re-activate metric via screen on. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 3); @@ -603,11 +603,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // 4th processed event. event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); // Re-enable battery saver mode activation. event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 3); @@ -623,11 +623,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // 5th processed event. event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); // Cancel battery saver mode activation. event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 3); @@ -643,7 +643,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // Screen-on activation expired. event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); // New broadcast since the config is no longer active. @@ -658,11 +658,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); // Re-enable battery saver mode activation. event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 5); @@ -678,7 +678,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // Cancel battery saver mode activation. event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 6); @@ -835,14 +835,14 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { std::unique_ptr<LogEvent> event; event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 0); // Activated by battery save mode. event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 1); @@ -859,12 +859,12 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // First processed event. event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); // Activated by screen on event. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 20); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); @@ -879,7 +879,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // 2nd processed event. // The activation by screen_on event expires, but the one by battery save mode is still active. event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); @@ -895,11 +895,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // 3rd processed event. event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); // All activations expired. event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); // New broadcast since the config is no longer active. @@ -917,7 +917,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // Re-activate metric via screen on. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 3); @@ -934,11 +934,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // 4th processed event. event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); // Re-enable battery saver mode activation. event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 3); @@ -955,11 +955,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // 5th processed event. event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); // Cancel battery saver mode and screen on activation. event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); // New broadcast since the config is no longer active. @@ -976,7 +976,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // Screen-on activation expired. event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 4); @@ -991,11 +991,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); // Re-enable battery saver mode activation. event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 5); @@ -1012,7 +1012,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // Cancel battery saver mode and screen on activation. event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 6); @@ -1170,11 +1170,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { // Event that should be ignored. event = CreateAppCrashEvent(111, bucketStartTimeNs + 1); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 1); // Activate metric via screen on for 2 minutes. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 1); @@ -1186,11 +1186,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { // 1st processed event. event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); // Enable battery saver mode activation for 5 minutes. event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 + 10); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 10); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 1); @@ -1201,12 +1201,12 @@ TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { // 2nd processed event. event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 + 40); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 40); // Cancel battery saver mode and screen on activation. int64_t firstDeactivation = bucketStartTimeNs + NS_PER_SEC * 61; event = CreateScreenBrightnessChangedEvent(64, firstDeactivation); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), firstDeactivation); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); // New broadcast since the config is no longer active. @@ -1217,11 +1217,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { // Should be ignored event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 61 + 80); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 61 + 80); // Re-enable battery saver mode activation. event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 3); @@ -1233,12 +1233,12 @@ TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { // 3rd processed event. event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80); // Cancel battery saver mode activation. int64_t secondDeactivation = bucketStartTimeNs + NS_PER_SEC * 60 * 13; event = CreateScreenBrightnessChangedEvent(140, secondDeactivation); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), secondDeactivation); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 4); @@ -1248,7 +1248,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { // Should be ignored. event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80); ConfigMetricsReportList reports; vector<uint8_t> buffer; @@ -1388,9 +1388,9 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { std::unique_ptr<LogEvent> event; event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); event = CreateMoveToForegroundEvent(1111, bucketStartTimeNs + 5); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); EXPECT_FALSE(metricProducer2->mIsActive); @@ -1398,7 +1398,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // Activated by battery save mode. event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); EXPECT_TRUE(metricsManager->isActive()); EXPECT_EQ(broadcastCount, 1); EXPECT_EQ(activeConfigsBroadcast.size(), 1); @@ -1424,14 +1424,14 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // First processed event. event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); event = CreateMoveToForegroundEvent(2222, bucketStartTimeNs + 15); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); // Activated by screen on event. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 20); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); @@ -1455,9 +1455,9 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // 2nd processed event. // The activation by screen_on event expires, but the one by battery save mode is still active. event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); event = CreateMoveToForegroundEvent(3333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); @@ -1482,15 +1482,15 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // 3rd processed event. event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); event = CreateMoveToForegroundEvent(4444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); // All activations expired. event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); event = CreateMoveToForegroundEvent(5555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); EXPECT_FALSE(metricsManager->isActive()); // New broadcast since the config is no longer active. EXPECT_EQ(broadcastCount, 2); @@ -1517,7 +1517,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // Re-activate metric via screen on. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_TRUE(metricsManager->isActive()); EXPECT_EQ(broadcastCount, 3); EXPECT_EQ(activeConfigsBroadcast.size(), 1); @@ -1543,13 +1543,13 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // 4th processed event. event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); event = CreateMoveToForegroundEvent(6666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); // Re-enable battery saver mode activation. event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); EXPECT_TRUE(metricsManager->isActive()); EXPECT_EQ(broadcastCount, 3); EXPECT_EQ(activeConfigsBroadcast.size(), 1); @@ -1575,13 +1575,13 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // 5th processed event. event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); event = CreateMoveToForegroundEvent(7777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); // Cancel battery saver mode and screen on activation. event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(),bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); EXPECT_FALSE(metricsManager->isActive()); // New broadcast since the config is no longer active. EXPECT_EQ(broadcastCount, 4); @@ -1607,9 +1607,9 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // Screen-on activation expired. event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); event = CreateMoveToForegroundEvent(8888, bucketStartTimeNs + NS_PER_SEC * 60 * 13); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); EXPECT_FALSE(metricsManager->isActive()); EXPECT_EQ(broadcastCount, 4); EXPECT_EQ(activeConfigsBroadcast.size(), 0); @@ -1633,13 +1633,13 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); event = CreateMoveToForegroundEvent(9999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); // Re-enable battery saver mode activation. event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); EXPECT_TRUE(metricsManager->isActive()); EXPECT_EQ(broadcastCount, 5); EXPECT_EQ(activeConfigsBroadcast.size(), 1); @@ -1665,7 +1665,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // Cancel battery saver mode and screen on activation. event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(),bucketStartTimeNs + NS_PER_SEC * 60 * 16); EXPECT_FALSE(metricsManager->isActive()); EXPECT_EQ(broadcastCount, 6); EXPECT_EQ(activeConfigsBroadcast.size(), 0); diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java index 455e4bbc0b76..b23bf5da5c8d 100644 --- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java +++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java @@ -67,7 +67,7 @@ public class ShellUiAutomatorBridge extends UiAutomatorBridge { throw new IllegalStateException("Could not find provider: " + providerName); } provider = holder.provider; - cursor = provider.query(null, Settings.Secure.CONTENT_URI, + cursor = provider.query(null, null, Settings.Secure.CONTENT_URI, new String[] { Settings.Secure.VALUE }, diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 7f597fef807a..58c51a9c4b71 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -4087,7 +4087,7 @@ public class ActivityManager { * Action an app can implement to handle reports from {@link #setWatchHeapLimit(long)}. * If your package has an activity handling this action, it will be launched with the * heap data provided to it the same way as {@link Intent#ACTION_SEND}. Note that to - * match the activty must support this action and a MIME type of "*/*". + * match, the activity must support this action and a MIME type of "*/*". */ public static final String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT"; diff --git a/core/java/android/app/NotificationHistory.aidl b/core/java/android/app/NotificationHistory.aidl new file mode 100644 index 000000000000..8150e743335a --- /dev/null +++ b/core/java/android/app/NotificationHistory.aidl @@ -0,0 +1,19 @@ +/** + * 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.app; + +parcelable NotificationHistory;
\ No newline at end of file diff --git a/core/java/android/app/NotificationHistory.java b/core/java/android/app/NotificationHistory.java new file mode 100644 index 000000000000..c35246b49395 --- /dev/null +++ b/core/java/android/app/NotificationHistory.java @@ -0,0 +1,506 @@ +/* + * 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.app; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.graphics.drawable.Icon; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +/** + * @hide + */ +public final class NotificationHistory implements Parcelable { + + /** + * A historical notification. Any new fields added here should also be added to + * {@link #readNotificationFromParcel} and + * {@link #writeNotificationToParcel(HistoricalNotification, Parcel, int)}. + */ + public static final class HistoricalNotification { + private String mPackage; + private String mChannelName; + private String mChannelId; + private int mUid; + private @UserIdInt int mUserId; + private long mPostedTimeMs; + private String mTitle; + private String mText; + private Icon mIcon; + + private HistoricalNotification() {} + + public String getPackage() { + return mPackage; + } + + public String getChannelName() { + return mChannelName; + } + + public String getChannelId() { + return mChannelId; + } + + public int getUid() { + return mUid; + } + + public int getUserId() { + return mUserId; + } + + public long getPostedTimeMs() { + return mPostedTimeMs; + } + + public String getTitle() { + return mTitle; + } + + public String getText() { + return mText; + } + + public Icon getIcon() { + return mIcon; + } + + public String getKey() { + return mPackage + "|" + mUid + "|" + mPostedTimeMs; + } + + @Override + public String toString() { + return "HistoricalNotification{" + + "key='" + getKey() + '\'' + + ", mChannelName='" + mChannelName + '\'' + + ", mChannelId='" + mChannelId + '\'' + + ", mUserId=" + mUserId + + ", mTitle='" + mTitle + '\'' + + ", mText='" + mText + '\'' + + ", mIcon=" + mIcon + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + HistoricalNotification that = (HistoricalNotification) o; + boolean iconsAreSame = getIcon() == null && that.getIcon() == null + || (getIcon() != null && that.getIcon() != null + && getIcon().sameAs(that.getIcon())); + return getUid() == that.getUid() && + getUserId() == that.getUserId() && + getPostedTimeMs() == that.getPostedTimeMs() && + Objects.equals(getPackage(), that.getPackage()) && + Objects.equals(getChannelName(), that.getChannelName()) && + Objects.equals(getChannelId(), that.getChannelId()) && + Objects.equals(getTitle(), that.getTitle()) && + Objects.equals(getText(), that.getText()) && + iconsAreSame; + } + + @Override + public int hashCode() { + return Objects.hash(getPackage(), getChannelName(), getChannelId(), getUid(), + getUserId(), + getPostedTimeMs(), getTitle(), getText(), getIcon()); + } + + public static final class Builder { + private String mPackage; + private String mChannelName; + private String mChannelId; + private int mUid; + private @UserIdInt int mUserId; + private long mPostedTimeMs; + private String mTitle; + private String mText; + private Icon mIcon; + + public Builder() {} + + public Builder setPackage(String aPackage) { + mPackage = aPackage; + return this; + } + + public Builder setChannelName(String channelName) { + mChannelName = channelName; + return this; + } + + public Builder setChannelId(String channelId) { + mChannelId = channelId; + return this; + } + + public Builder setUid(int uid) { + mUid = uid; + return this; + } + + public Builder setUserId(int userId) { + mUserId = userId; + return this; + } + + public Builder setPostedTimeMs(long postedTimeMs) { + mPostedTimeMs = postedTimeMs; + return this; + } + + public Builder setTitle(String title) { + mTitle = title; + return this; + } + + public Builder setText(String text) { + mText = text; + return this; + } + + public Builder setIcon(Icon icon) { + mIcon = icon; + return this; + } + + public HistoricalNotification build() { + HistoricalNotification n = new HistoricalNotification(); + n.mPackage = mPackage; + n.mChannelName = mChannelName; + n.mChannelId = mChannelId; + n.mUid = mUid; + n.mUserId = mUserId; + n.mPostedTimeMs = mPostedTimeMs; + n.mTitle = mTitle; + n.mText = mText; + n.mIcon = mIcon; + return n; + } + } + } + + // Only used when creating the resulting history. Not used for reading/unparceling. + private List<HistoricalNotification> mNotificationsToWrite = new ArrayList<>(); + // ditto + private Set<String> mStringsToWrite = new HashSet<>(); + + // Mostly used for reading/unparceling events. + private Parcel mParcel = null; + private int mHistoryCount; + private int mIndex = 0; + + // Sorted array of commonly used strings to shrink the size of the parcel. populated from + // mStringsToWrite on write and the parcel on read. + private String[] mStringPool; + + /** + * Construct the iterator from a parcel. + */ + private NotificationHistory(Parcel in) { + byte[] bytes = in.readBlob(); + Parcel data = Parcel.obtain(); + data.unmarshall(bytes, 0, bytes.length); + data.setDataPosition(0); + mHistoryCount = data.readInt(); + mIndex = data.readInt(); + if (mHistoryCount > 0) { + mStringPool = data.createStringArray(); + + final int listByteLength = data.readInt(); + final int positionInParcel = data.readInt(); + mParcel = Parcel.obtain(); + mParcel.setDataPosition(0); + mParcel.appendFrom(data, data.dataPosition(), listByteLength); + mParcel.setDataSize(mParcel.dataPosition()); + mParcel.setDataPosition(positionInParcel); + } + } + + /** + * Create an empty iterator. + */ + public NotificationHistory() { + mHistoryCount = 0; + } + + /** + * Returns whether or not there are more events to read using {@link #getNextNotification()}. + * + * @return true if there are more events, false otherwise. + */ + public boolean hasNextNotification() { + return mIndex < mHistoryCount; + } + + /** + * Retrieve the next {@link HistoricalNotification} from the collection and put the + * resulting data into {@code notificationOut}. + * + * @return The next {@link HistoricalNotification} or null if there are no more notifications. + */ + public @Nullable HistoricalNotification getNextNotification() { + if (!hasNextNotification()) { + return null; + } + + HistoricalNotification n = readNotificationFromParcel(mParcel); + + mIndex++; + if (!hasNextNotification()) { + mParcel.recycle(); + mParcel = null; + } + return n; + } + + /** + * Adds all of the pooled strings that have been read from disk + */ + public void addPooledStrings(@NonNull List<String> strings) { + mStringsToWrite.addAll(strings); + } + + /** + * Builds the pooled strings from pending notifications. Useful if the pooled strings on + * disk contains strings that aren't relevant to the notifications in our collection. + */ + public void poolStringsFromNotifications() { + mStringsToWrite.clear(); + for (int i = 0; i < mNotificationsToWrite.size(); i++) { + final HistoricalNotification notification = mNotificationsToWrite.get(i); + mStringsToWrite.add(notification.getPackage()); + mStringsToWrite.add(notification.getChannelName()); + mStringsToWrite.add(notification.getChannelId()); + } + } + + /** + * Used when populating a history from disk; adds an historical notification. + */ + public void addNotificationToWrite(@NonNull HistoricalNotification notification) { + if (notification == null) { + return; + } + mNotificationsToWrite.add(notification); + mHistoryCount++; + } + + /** + * Removes a package's historical notifications and regenerates the string pool + */ + public void removeNotificationsFromWrite(String packageName) { + for (int i = mNotificationsToWrite.size() - 1; i >= 0; i--) { + if (packageName.equals(mNotificationsToWrite.get(i).getPackage())) { + mNotificationsToWrite.remove(i); + } + } + poolStringsFromNotifications(); + } + + /** + * Gets pooled strings in order to write them to disk + */ + public @NonNull String[] getPooledStringsToWrite() { + String[] stringsToWrite = mStringsToWrite.toArray(new String[]{}); + Arrays.sort(stringsToWrite); + return stringsToWrite; + } + + /** + * Gets the historical notifications in order to write them to disk + */ + public @NonNull List<HistoricalNotification> getNotificationsToWrite() { + return mNotificationsToWrite; + } + + /** + * Gets the number of notifications in the collection + */ + public int getHistoryCount() { + return mHistoryCount; + } + + private int findStringIndex(String str) { + final int index = Arrays.binarySearch(mStringPool, str); + if (index < 0) { + throw new IllegalStateException("String '" + str + "' is not in the string pool"); + } + return index; + } + + /** + * Writes a single notification to the parcel. Modify this when updating member variables of + * {@link HistoricalNotification}. + */ + private void writeNotificationToParcel(HistoricalNotification notification, Parcel p, + int flags) { + final int packageIndex; + if (notification.mPackage != null) { + packageIndex = findStringIndex(notification.mPackage); + } else { + packageIndex = -1; + } + + final int channelNameIndex; + if (notification.getChannelName() != null) { + channelNameIndex = findStringIndex(notification.getChannelName()); + } else { + channelNameIndex = -1; + } + + final int channelIdIndex; + if (notification.getChannelId() != null) { + channelIdIndex = findStringIndex(notification.getChannelId()); + } else { + channelIdIndex = -1; + } + + p.writeInt(packageIndex); + p.writeInt(channelNameIndex); + p.writeInt(channelIdIndex); + p.writeInt(notification.getUid()); + p.writeInt(notification.getUserId()); + p.writeLong(notification.getPostedTimeMs()); + p.writeString(notification.getTitle()); + p.writeString(notification.getText()); + notification.getIcon().writeToParcel(p, flags); + } + + /** + * Reads a single notification from the parcel. Modify this when updating member variables of + * {@link HistoricalNotification}. + */ + private HistoricalNotification readNotificationFromParcel(Parcel p) { + HistoricalNotification.Builder notificationOut = new HistoricalNotification.Builder(); + final int packageIndex = p.readInt(); + if (packageIndex >= 0) { + notificationOut.mPackage = mStringPool[packageIndex]; + } else { + notificationOut.mPackage = null; + } + + final int channelNameIndex = p.readInt(); + if (channelNameIndex >= 0) { + notificationOut.setChannelName(mStringPool[channelNameIndex]); + } else { + notificationOut.setChannelName(null); + } + + final int channelIdIndex = p.readInt(); + if (channelIdIndex >= 0) { + notificationOut.setChannelId(mStringPool[channelIdIndex]); + } else { + notificationOut.setChannelId(null); + } + + notificationOut.setUid(p.readInt()); + notificationOut.setUserId(p.readInt()); + notificationOut.setPostedTimeMs(p.readLong()); + notificationOut.setTitle(p.readString()); + notificationOut.setText(p.readString()); + notificationOut.setIcon(Icon.CREATOR.createFromParcel(p)); + + return notificationOut.build(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + Parcel data = Parcel.obtain(); + data.writeInt(mHistoryCount); + data.writeInt(mIndex); + if (mHistoryCount > 0) { + mStringPool = getPooledStringsToWrite(); + data.writeStringArray(mStringPool); + + if (!mNotificationsToWrite.isEmpty()) { + // typically system_server to a process + + // Write out the events + Parcel p = Parcel.obtain(); + try { + p.setDataPosition(0); + for (int i = 0; i < mHistoryCount; i++) { + final HistoricalNotification notification = mNotificationsToWrite.get(i); + writeNotificationToParcel(notification, p, flags); + } + + final int listByteLength = p.dataPosition(); + + // Write the total length of the data. + data.writeInt(listByteLength); + + // Write our current position into the data. + data.writeInt(0); + + // Write the data. + data.appendFrom(p, 0, listByteLength); + } finally { + p.recycle(); + } + + } else if (mParcel != null) { + // typically process to process as mNotificationsToWrite is not populated on + // unparcel. + + // Write the total length of the data. + data.writeInt(mParcel.dataSize()); + + // Write out current position into the data. + data.writeInt(mParcel.dataPosition()); + + // Write the data. + data.appendFrom(mParcel, 0, mParcel.dataSize()); + } else { + throw new IllegalStateException( + "Either mParcel or mNotificationsToWrite must not be null"); + } + } + // Data can be too large for a transact. Write the data as a Blob, which will be written to + // ashmem if too large. + dest.writeBlob(data.marshall()); + } + + public static final @NonNull Creator<NotificationHistory> CREATOR + = new Creator<NotificationHistory>() { + @Override + public NotificationHistory createFromParcel(Parcel source) { + return new NotificationHistory(source); + } + + @Override + public NotificationHistory[] newArray(int size) { + return new NotificationHistory[size]; + } + }; +} diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index cb353576bd8d..9cb73f931773 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -42,11 +42,15 @@ import java.util.function.BiConsumer; /** * System level service for managing companion devices * + * See <a href="{@docRoot}guide/topics/connectivity/companion-device-pairing">this guide</a> + * for a usage example. + * * <p>To obtain an instance call {@link Context#getSystemService}({@link * Context#COMPANION_DEVICE_SERVICE}) Then, call {@link #associate(AssociationRequest, * Callback, Handler)} to initiate the flow of associating current package with a * device selected by user.</p> * + * @see CompanionDeviceManager#associate * @see AssociationRequest */ @SystemService(Context.COMPANION_DEVICE_SERVICE) diff --git a/core/java/android/companion/WifiDeviceFilter.java b/core/java/android/companion/WifiDeviceFilter.java index 62098d556e5b..58bf874efb41 100644 --- a/core/java/android/companion/WifiDeviceFilter.java +++ b/core/java/android/companion/WifiDeviceFilter.java @@ -17,17 +17,18 @@ package android.companion; import static android.companion.BluetoothDeviceFilterUtils.getDeviceDisplayNameInternal; -import static android.companion.BluetoothDeviceFilterUtils.patternFromString; -import static android.companion.BluetoothDeviceFilterUtils.patternToString; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SuppressLint; import android.bluetooth.BluetoothDevice; import android.bluetooth.le.ScanFilter; +import android.net.MacAddress; import android.net.wifi.ScanResult; import android.os.Parcel; -import android.provider.OneTimeUseBuilder; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; +import com.android.internal.util.Parcelling; import java.util.Objects; import java.util.regex.Pattern; @@ -37,30 +38,38 @@ import java.util.regex.Pattern; * * @see ScanFilter */ +@DataClass( + genParcelable = true, + genAidl = false, + genBuilder = true, + genEqualsHashCode = true, + genHiddenGetters = true) public final class WifiDeviceFilter implements DeviceFilter<ScanResult> { - private final Pattern mNamePattern; - - private WifiDeviceFilter(Pattern namePattern) { - mNamePattern = namePattern; - } - - @SuppressLint("ParcelClassLoader") - private WifiDeviceFilter(Parcel in) { - this(patternFromString(in.readString())); - } + /** + * If set, only devices with {@link BluetoothDevice#getName name} matching the given regular + * expression will be shown + */ + @DataClass.ParcelWith(Parcelling.BuiltIn.ForPattern.class) + private @Nullable Pattern mNamePattern = null; - /** @hide */ - @Nullable - public Pattern getNamePattern() { - return mNamePattern; - } + /** + * If set, only devices with BSSID matching the given one will be shown + */ + private @Nullable MacAddress mBssid = null; + /** + * If set, only bits at positions set in this mask, will be compared to the given + * {@link Builder#setBssid BSSID} filter. + */ + private @NonNull MacAddress mBssidMask = MacAddress.BROADCAST_ADDRESS; /** @hide */ @Override public boolean matches(ScanResult device) { - return BluetoothDeviceFilterUtils.matchesName(getNamePattern(), device); + return BluetoothDeviceFilterUtils.matchesName(getNamePattern(), device) + && (mBssid == null + || MacAddress.fromString(device.BSSID).matches(mBssid, mBssidMask)); } /** @hide */ @@ -75,65 +84,249 @@ public final class WifiDeviceFilter implements DeviceFilter<ScanResult> { return MEDIUM_TYPE_WIFI; } + + + // Code below generated by codegen v1.0.11. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/companion/WifiDeviceFilter.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + /* package-private */ WifiDeviceFilter( + @Nullable Pattern namePattern, + @Nullable MacAddress bssid, + @NonNull MacAddress bssidMask) { + this.mNamePattern = namePattern; + this.mBssid = bssid; + this.mBssidMask = bssidMask; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mBssidMask); + + // onConstructed(); // You can define this method to get a callback + } + + /** + * If set, only devices with {@link BluetoothDevice#getName name} matching the given regular + * expression will be shown + * + * @hide + */ + @DataClass.Generated.Member + public @Nullable Pattern getNamePattern() { + return mNamePattern; + } + + /** + * If set, only devices with BSSID matching the given one will be shown + * + * @hide + */ + @DataClass.Generated.Member + public @Nullable MacAddress getBssid() { + return mBssid; + } + + /** + * If set, only bits at positions set in this mask, will be compared to the given + * {@link Builder#setBssid BSSID} filter. + * + * @hide + */ + @DataClass.Generated.Member + public @NonNull MacAddress getBssidMask() { + return mBssidMask; + } + @Override - public boolean equals(Object o) { + @DataClass.Generated.Member + public boolean equals(@Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(WifiDeviceFilter other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") WifiDeviceFilter that = (WifiDeviceFilter) o; - return Objects.equals(mNamePattern, that.mNamePattern); + //noinspection PointlessBooleanExpression + return true + && Objects.equals(mNamePattern, that.mNamePattern) + && Objects.equals(mBssid, that.mBssid) + && Objects.equals(mBssidMask, that.mBssidMask); } @Override + @DataClass.Generated.Member public int hashCode() { - return Objects.hash(mNamePattern); + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + Objects.hashCode(mNamePattern); + _hash = 31 * _hash + Objects.hashCode(mBssid); + _hash = 31 * _hash + Objects.hashCode(mBssidMask); + return _hash; + } + + @DataClass.Generated.Member + static Parcelling<Pattern> sParcellingForNamePattern = + Parcelling.Cache.get( + Parcelling.BuiltIn.ForPattern.class); + static { + if (sParcellingForNamePattern == null) { + sParcellingForNamePattern = Parcelling.Cache.put( + new Parcelling.BuiltIn.ForPattern()); + } } @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(patternToString(getNamePattern())); + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mNamePattern != null) flg |= 0x1; + if (mBssid != null) flg |= 0x2; + dest.writeByte(flg); + sParcellingForNamePattern.parcel(mNamePattern, dest, flags); + if (mBssid != null) dest.writeTypedObject(mBssid, flags); + dest.writeTypedObject(mBssidMask, flags); } @Override - public int describeContents() { - return 0; + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ WifiDeviceFilter(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + Pattern namePattern = sParcellingForNamePattern.unparcel(in); + MacAddress bssid = (flg & 0x2) == 0 ? null : (MacAddress) in.readTypedObject(MacAddress.CREATOR); + MacAddress bssidMask = (MacAddress) in.readTypedObject(MacAddress.CREATOR); + + this.mNamePattern = namePattern; + this.mBssid = bssid; + this.mBssidMask = bssidMask; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mBssidMask); + + // onConstructed(); // You can define this method to get a callback } - public static final @android.annotation.NonNull Creator<WifiDeviceFilter> CREATOR - = new Creator<WifiDeviceFilter>() { + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<WifiDeviceFilter> CREATOR + = new Parcelable.Creator<WifiDeviceFilter>() { @Override - public WifiDeviceFilter createFromParcel(Parcel in) { - return new WifiDeviceFilter(in); + public WifiDeviceFilter[] newArray(int size) { + return new WifiDeviceFilter[size]; } @Override - public WifiDeviceFilter[] newArray(int size) { - return new WifiDeviceFilter[size]; + public WifiDeviceFilter createFromParcel(@NonNull Parcel in) { + return new WifiDeviceFilter(in); } }; /** - * Builder for {@link WifiDeviceFilter} + * A builder for {@link WifiDeviceFilter} */ - public static final class Builder extends OneTimeUseBuilder<WifiDeviceFilter> { - private Pattern mNamePattern; + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static final class Builder { + + private @Nullable Pattern mNamePattern; + private @Nullable MacAddress mBssid; + private @NonNull MacAddress mBssidMask; + + private long mBuilderFieldsSet = 0L; + + public Builder() { + } /** - * @param regex if set, only devices with {@link BluetoothDevice#getName name} matching the - * given regular expression will be shown - * @return self for chaining + * If set, only devices with {@link BluetoothDevice#getName name} matching the given regular + * expression will be shown */ - public Builder setNamePattern(@Nullable Pattern regex) { + @DataClass.Generated.Member + public @NonNull Builder setNamePattern(@Nullable Pattern value) { checkNotUsed(); - mNamePattern = regex; + mBuilderFieldsSet |= 0x1; + mNamePattern = value; return this; } - /** @inheritDoc */ - @Override - @NonNull - public WifiDeviceFilter build() { - markUsed(); - return new WifiDeviceFilter(mNamePattern); + /** + * If set, only devices with BSSID matching the given one will be shown + */ + @DataClass.Generated.Member + public @NonNull Builder setBssid(@Nullable MacAddress value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mBssid = value; + return this; + } + + /** + * If set, only bits at positions set in this mask, will be compared to the given + * {@link Builder#setBssid BSSID} filter. + */ + @DataClass.Generated.Member + public @NonNull Builder setBssidMask(@NonNull MacAddress value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; + mBssidMask = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull WifiDeviceFilter build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x8; // Mark builder used + + if ((mBuilderFieldsSet & 0x1) == 0) { + mNamePattern = null; + } + if ((mBuilderFieldsSet & 0x2) == 0) { + mBssid = null; + } + if ((mBuilderFieldsSet & 0x4) == 0) { + mBssidMask = MacAddress.BROADCAST_ADDRESS; + } + WifiDeviceFilter o = new WifiDeviceFilter( + mNamePattern, + mBssid, + mBssidMask); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x8) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } } } + + @DataClass.Generated( + time = 1571960300742L, + codegenVersion = "1.0.11", + sourceFile = "frameworks/base/core/java/android/companion/WifiDeviceFilter.java", + inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.Nullable java.util.regex.Pattern mNamePattern\nprivate @android.annotation.Nullable android.net.MacAddress mBssid\nprivate @android.annotation.NonNull android.net.MacAddress mBssidMask\npublic @java.lang.Override boolean matches(android.net.wifi.ScanResult)\npublic @java.lang.Override java.lang.String getDeviceDisplayName(android.net.wifi.ScanResult)\npublic @java.lang.Override int getMediumType()\nclass WifiDeviceFilter extends java.lang.Object implements [android.companion.DeviceFilter<android.net.wifi.ScanResult>]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=false, genBuilder=true, genEqualsHashCode=true, genHiddenGetters=true)") + @Deprecated + private void __metadata() {} + } diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 7de87938c1f6..17f1a07d6e01 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -53,6 +53,7 @@ import android.os.UserHandle; import android.os.storage.StorageManager; import android.text.TextUtils; import android.util.Log; +import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; @@ -136,7 +137,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall private boolean mNoPerms; private boolean mSingleUser; - private ThreadLocal<String> mCallingPackage; + private ThreadLocal<Pair<String, String>> mCallingPackage; private Transport mTransport = new Transport(); @@ -226,11 +227,13 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public Cursor query(String callingPkg, Uri uri, @Nullable String[] projection, - @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) { + public Cursor query(String callingPkg, @Nullable String featureId, Uri uri, + @Nullable String[] projection, @Nullable Bundle queryArgs, + @Nullable ICancellationSignal cancellationSignal) { uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); - if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { + if (enforceReadPermission(callingPkg, featureId, uri, null) + != AppOpsManager.MODE_ALLOWED) { // The caller has no access to the data, so return an empty cursor with // the columns in the requested order. The caller may ask for an invalid // column and we would not catch that but this is not a problem in practice. @@ -246,7 +249,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall // we have to execute the query as if allowed to get a cursor with the // columns. We then use the column names to return an empty cursor. Cursor cursor; - final String original = setCallingPackage(callingPkg); + final Pair<String, String> original = setCallingPackage( + new Pair<>(callingPkg, featureId)); try { cursor = mInterface.query( uri, projection, queryArgs, @@ -264,7 +268,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall return new MatrixCursor(cursor.getColumnNames(), 0); } Trace.traceBegin(TRACE_TAG_DATABASE, "query"); - final String original = setCallingPackage(callingPkg); + final Pair<String, String> original = setCallingPackage( + new Pair<>(callingPkg, featureId)); try { return mInterface.query( uri, projection, queryArgs, @@ -293,12 +298,15 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public Uri insert(String callingPkg, Uri uri, ContentValues initialValues) { + public Uri insert(String callingPkg, @Nullable String featureId, Uri uri, + ContentValues initialValues) { uri = validateIncomingUri(uri); int userId = getUserIdFromUri(uri); uri = maybeGetUriWithoutUserId(uri); - if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { - final String original = setCallingPackage(callingPkg); + if (enforceWritePermission(callingPkg, featureId, uri, null) + != AppOpsManager.MODE_ALLOWED) { + final Pair<String, String> original = setCallingPackage( + new Pair<>(callingPkg, featureId)); try { return rejectInsert(uri, initialValues); } finally { @@ -306,7 +314,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } } Trace.traceBegin(TRACE_TAG_DATABASE, "insert"); - final String original = setCallingPackage(callingPkg); + final Pair<String, String> original = setCallingPackage( + new Pair<>(callingPkg, featureId)); try { return maybeAddUserId(mInterface.insert(uri, initialValues), userId); } catch (RemoteException e) { @@ -318,14 +327,17 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public int bulkInsert(String callingPkg, Uri uri, ContentValues[] initialValues) { + public int bulkInsert(String callingPkg, @Nullable String featureId, Uri uri, + ContentValues[] initialValues) { uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); - if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { + if (enforceWritePermission(callingPkg, featureId, uri, null) + != AppOpsManager.MODE_ALLOWED) { return 0; } Trace.traceBegin(TRACE_TAG_DATABASE, "bulkInsert"); - final String original = setCallingPackage(callingPkg); + final Pair<String, String> original = setCallingPackage( + new Pair<>(callingPkg, featureId)); try { return mInterface.bulkInsert(uri, initialValues); } catch (RemoteException e) { @@ -337,8 +349,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public ContentProviderResult[] applyBatch(String callingPkg, String authority, - ArrayList<ContentProviderOperation> operations) + public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String featureId, + String authority, ArrayList<ContentProviderOperation> operations) throws OperationApplicationException { validateIncomingAuthority(authority); int numOperations = operations.size(); @@ -355,20 +367,21 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall operations.set(i, operation); } if (operation.isReadOperation()) { - if (enforceReadPermission(callingPkg, uri, null) + if (enforceReadPermission(callingPkg, featureId, uri, null) != AppOpsManager.MODE_ALLOWED) { throw new OperationApplicationException("App op not allowed", 0); } } if (operation.isWriteOperation()) { - if (enforceWritePermission(callingPkg, uri, null) + if (enforceWritePermission(callingPkg, featureId, uri, null) != AppOpsManager.MODE_ALLOWED) { throw new OperationApplicationException("App op not allowed", 0); } } } Trace.traceBegin(TRACE_TAG_DATABASE, "applyBatch"); - final String original = setCallingPackage(callingPkg); + final Pair<String, String> original = setCallingPackage( + new Pair<>(callingPkg, featureId)); try { ContentProviderResult[] results = mInterface.applyBatch(authority, operations); @@ -390,14 +403,17 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public int delete(String callingPkg, Uri uri, String selection, String[] selectionArgs) { + public int delete(String callingPkg, @Nullable String featureId, Uri uri, String selection, + String[] selectionArgs) { uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); - if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { + if (enforceWritePermission(callingPkg, featureId, uri, null) + != AppOpsManager.MODE_ALLOWED) { return 0; } Trace.traceBegin(TRACE_TAG_DATABASE, "delete"); - final String original = setCallingPackage(callingPkg); + final Pair<String, String> original = setCallingPackage( + new Pair<>(callingPkg, featureId)); try { return mInterface.delete(uri, selection, selectionArgs); } catch (RemoteException e) { @@ -409,15 +425,17 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public int update(String callingPkg, Uri uri, ContentValues values, String selection, - String[] selectionArgs) { + public int update(String callingPkg, @Nullable String featureId, Uri uri, + ContentValues values, String selection, String[] selectionArgs) { uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); - if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { + if (enforceWritePermission(callingPkg, featureId, uri, null) + != AppOpsManager.MODE_ALLOWED) { return 0; } Trace.traceBegin(TRACE_TAG_DATABASE, "update"); - final String original = setCallingPackage(callingPkg); + final Pair<String, String> original = setCallingPackage( + new Pair<>(callingPkg, featureId)); try { return mInterface.update(uri, values, selection, selectionArgs); } catch (RemoteException e) { @@ -429,14 +447,15 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public ParcelFileDescriptor openFile( - String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal, - IBinder callerToken) throws FileNotFoundException { + public ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId, + Uri uri, String mode, ICancellationSignal cancellationSignal, IBinder callerToken) + throws FileNotFoundException { uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); - enforceFilePermission(callingPkg, uri, mode, callerToken); + enforceFilePermission(callingPkg, featureId, uri, mode, callerToken); Trace.traceBegin(TRACE_TAG_DATABASE, "openFile"); - final String original = setCallingPackage(callingPkg); + final Pair<String, String> original = setCallingPackage( + new Pair<>(callingPkg, featureId)); try { return mInterface.openFile( uri, mode, CancellationSignal.fromTransport(cancellationSignal)); @@ -449,14 +468,15 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public AssetFileDescriptor openAssetFile( - String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal) + public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String featureId, + Uri uri, String mode, ICancellationSignal cancellationSignal) throws FileNotFoundException { uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); - enforceFilePermission(callingPkg, uri, mode, null); + enforceFilePermission(callingPkg, featureId, uri, mode, null); Trace.traceBegin(TRACE_TAG_DATABASE, "openAssetFile"); - final String original = setCallingPackage(callingPkg); + final Pair<String, String> original = setCallingPackage( + new Pair<>(callingPkg, featureId)); try { return mInterface.openAssetFile( uri, mode, CancellationSignal.fromTransport(cancellationSignal)); @@ -469,12 +489,13 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public Bundle call(String callingPkg, String authority, String method, @Nullable String arg, - @Nullable Bundle extras) { + public Bundle call(String callingPkg, @Nullable String featureId, String authority, + String method, @Nullable String arg, @Nullable Bundle extras) { validateIncomingAuthority(authority); Bundle.setDefusable(extras, true); Trace.traceBegin(TRACE_TAG_DATABASE, "call"); - final String original = setCallingPackage(callingPkg); + final Pair<String, String> original = setCallingPackage( + new Pair<>(callingPkg, featureId)); try { return mInterface.call(authority, method, arg, extras); } catch (RemoteException e) { @@ -501,14 +522,16 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri uri, String mimeType, - Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException { + public AssetFileDescriptor openTypedAssetFile(String callingPkg, + @Nullable String featureId, Uri uri, String mimeType, Bundle opts, + ICancellationSignal cancellationSignal) throws FileNotFoundException { Bundle.setDefusable(opts, true); uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); - enforceFilePermission(callingPkg, uri, "r", null); + enforceFilePermission(callingPkg, featureId, uri, "r", null); Trace.traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile"); - final String original = setCallingPackage(callingPkg); + final Pair<String, String> original = setCallingPackage( + new Pair<>(callingPkg, featureId)); try { return mInterface.openTypedAssetFile( uri, mimeType, opts, CancellationSignal.fromTransport(cancellationSignal)); @@ -526,15 +549,17 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public Uri canonicalize(String callingPkg, Uri uri) { + public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) { uri = validateIncomingUri(uri); int userId = getUserIdFromUri(uri); uri = getUriWithoutUserId(uri); - if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { + if (enforceReadPermission(callingPkg, featureId, uri, null) + != AppOpsManager.MODE_ALLOWED) { return null; } Trace.traceBegin(TRACE_TAG_DATABASE, "canonicalize"); - final String original = setCallingPackage(callingPkg); + final Pair<String, String> original = setCallingPackage( + new Pair<>(callingPkg, featureId)); try { return maybeAddUserId(mInterface.canonicalize(uri), userId); } catch (RemoteException e) { @@ -546,15 +571,17 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public Uri uncanonicalize(String callingPkg, Uri uri) { + public Uri uncanonicalize(String callingPkg, String featureId, Uri uri) { uri = validateIncomingUri(uri); int userId = getUserIdFromUri(uri); uri = getUriWithoutUserId(uri); - if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { + if (enforceReadPermission(callingPkg, featureId, uri, null) + != AppOpsManager.MODE_ALLOWED) { return null; } Trace.traceBegin(TRACE_TAG_DATABASE, "uncanonicalize"); - final String original = setCallingPackage(callingPkg); + final Pair<String, String> original = setCallingPackage( + new Pair<>(callingPkg, featureId)); try { return maybeAddUserId(mInterface.uncanonicalize(uri), userId); } catch (RemoteException e) { @@ -566,15 +593,17 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public boolean refresh(String callingPkg, Uri uri, Bundle args, + public boolean refresh(String callingPkg, String featureId, Uri uri, Bundle args, ICancellationSignal cancellationSignal) throws RemoteException { uri = validateIncomingUri(uri); uri = getUriWithoutUserId(uri); - if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { + if (enforceReadPermission(callingPkg, featureId, uri, null) + != AppOpsManager.MODE_ALLOWED) { return false; } Trace.traceBegin(TRACE_TAG_DATABASE, "refresh"); - final String original = setCallingPackage(callingPkg); + final Pair<String, String> original = setCallingPackage( + new Pair<>(callingPkg, featureId)); try { return mInterface.refresh(uri, args, CancellationSignal.fromTransport(cancellationSignal)); @@ -585,11 +614,13 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override - public int checkUriPermission(String callingPkg, Uri uri, int uid, int modeFlags) { + public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri, + int uid, int modeFlags) { uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); Trace.traceBegin(TRACE_TAG_DATABASE, "checkUriPermission"); - final String original = setCallingPackage(callingPkg); + final Pair<String, String> original = setCallingPackage( + new Pair<>(callingPkg, featureId)); try { return mInterface.checkUriPermission(uri, uid, modeFlags); } catch (RemoteException e) { @@ -600,44 +631,47 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } } - private void enforceFilePermission(String callingPkg, Uri uri, String mode, - IBinder callerToken) throws FileNotFoundException, SecurityException { + private void enforceFilePermission(String callingPkg, @Nullable String featureId, Uri uri, + String mode, IBinder callerToken) throws FileNotFoundException, SecurityException { if (mode != null && mode.indexOf('w') != -1) { - if (enforceWritePermission(callingPkg, uri, callerToken) + if (enforceWritePermission(callingPkg, featureId, uri, callerToken) != AppOpsManager.MODE_ALLOWED) { throw new FileNotFoundException("App op not allowed"); } } else { - if (enforceReadPermission(callingPkg, uri, callerToken) + if (enforceReadPermission(callingPkg, featureId, uri, callerToken) != AppOpsManager.MODE_ALLOWED) { throw new FileNotFoundException("App op not allowed"); } } } - private int enforceReadPermission(String callingPkg, Uri uri, IBinder callerToken) + private int enforceReadPermission(String callingPkg, @Nullable String featureId, Uri uri, + IBinder callerToken) throws SecurityException { - final int mode = enforceReadPermissionInner(uri, callingPkg, callerToken); + final int mode = enforceReadPermissionInner(uri, callingPkg, featureId, callerToken); if (mode != MODE_ALLOWED) { return mode; } - return noteProxyOp(callingPkg, mReadOp); + return noteProxyOp(callingPkg, featureId, mReadOp); } - private int enforceWritePermission(String callingPkg, Uri uri, IBinder callerToken) + private int enforceWritePermission(String callingPkg, String featureId, Uri uri, + IBinder callerToken) throws SecurityException { - final int mode = enforceWritePermissionInner(uri, callingPkg, callerToken); + final int mode = enforceWritePermissionInner(uri, callingPkg, featureId, callerToken); if (mode != MODE_ALLOWED) { return mode; } - return noteProxyOp(callingPkg, mWriteOp); + return noteProxyOp(callingPkg, featureId, mWriteOp); } - private int noteProxyOp(String callingPkg, int op) { + private int noteProxyOp(String callingPkg, String featureId, int op) { if (op != AppOpsManager.OP_NONE) { - int mode = mAppOpsManager.noteProxyOp(op, callingPkg); + int mode = mAppOpsManager.noteProxyOp(op, callingPkg, Binder.getCallingUid(), + featureId, null); return mode == MODE_DEFAULT ? MODE_IGNORED : mode; } @@ -659,18 +693,19 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall * associated with that permission. */ private int checkPermissionAndAppOp(String permission, String callingPkg, - IBinder callerToken) { + @Nullable String featureId, IBinder callerToken) { if (getContext().checkPermission(permission, Binder.getCallingPid(), Binder.getCallingUid(), callerToken) != PERMISSION_GRANTED) { return MODE_ERRORED; } - return mTransport.noteProxyOp(callingPkg, AppOpsManager.permissionToOpCode(permission)); + return mTransport.noteProxyOp(callingPkg, featureId, + AppOpsManager.permissionToOpCode(permission)); } /** {@hide} */ - protected int enforceReadPermissionInner(Uri uri, String callingPkg, IBinder callerToken) - throws SecurityException { + protected int enforceReadPermissionInner(Uri uri, String callingPkg, + @Nullable String featureId, IBinder callerToken) throws SecurityException { final Context context = getContext(); final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); @@ -684,7 +719,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall if (mExported && checkUser(pid, uid, context)) { final String componentPerm = getReadPermission(); if (componentPerm != null) { - final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, callerToken); + final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, featureId, + callerToken); if (mode == MODE_ALLOWED) { return MODE_ALLOWED; } else { @@ -703,7 +739,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall for (PathPermission pp : pps) { final String pathPerm = pp.getReadPermission(); if (pathPerm != null && pp.match(path)) { - final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, callerToken); + final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, featureId, + callerToken); if (mode == MODE_ALLOWED) { return MODE_ALLOWED; } else { @@ -751,8 +788,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } /** {@hide} */ - protected int enforceWritePermissionInner(Uri uri, String callingPkg, IBinder callerToken) - throws SecurityException { + protected int enforceWritePermissionInner(Uri uri, String callingPkg, + @Nullable String featureId, IBinder callerToken) throws SecurityException { final Context context = getContext(); final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); @@ -766,7 +803,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall if (mExported && checkUser(pid, uid, context)) { final String componentPerm = getWritePermission(); if (componentPerm != null) { - final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, callerToken); + final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, featureId, + callerToken); if (mode == MODE_ALLOWED) { return MODE_ALLOWED; } else { @@ -785,7 +823,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall for (PathPermission pp : pps) { final String pathPerm = pp.getWritePermission(); if (pathPerm != null && pp.match(path)) { - final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, callerToken); + final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, featureId, + callerToken); if (mode == MODE_ALLOWED) { return MODE_ALLOWED; } else { @@ -851,11 +890,11 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } /** - * Set the calling package, returning the current value (or {@code null}) + * Set the calling package/feature, returning the current value (or {@code null}) * which can be used later to restore the previous state. */ - private String setCallingPackage(String callingPackage) { - final String original = mCallingPackage.get(); + private Pair<String, String> setCallingPackage(Pair<String, String> callingPackage) { + final Pair<String, String> original = mCallingPackage.get(); mCallingPackage.set(callingPackage); onCallingPackageChanged(); return original; @@ -876,16 +915,42 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall * calling UID. */ public final @Nullable String getCallingPackage() { - final String pkg = mCallingPackage.get(); + final Pair<String, String> pkg = mCallingPackage.get(); if (pkg != null) { - mTransport.mAppOpsManager.checkPackage(Binder.getCallingUid(), pkg); + mTransport.mAppOpsManager.checkPackage(Binder.getCallingUid(), pkg.first); + return pkg.first; } - return pkg; + + return null; + } + + /** + * Return the feature in the package of the caller that initiated the request being + * processed on the current thread. Returns {@code null} if not currently processing + * a request of the request is for the default feature. + * <p> + * This will always return {@code null} when processing + * {@link #getType(Uri)} or {@link #getStreamTypes(Uri, String)} requests. + * + * @see #getCallingPackage + */ + public final @Nullable String getCallingFeatureId() { + final Pair<String, String> pkg = mCallingPackage.get(); + if (pkg != null) { + return pkg.second; + } + + return null; } /** {@hide} */ public final @Nullable String getCallingPackageUnchecked() { - return mCallingPackage.get(); + final Pair<String, String> pkg = mCallingPackage.get(); + if (pkg != null) { + return pkg.first; + } + + return null; } /** {@hide} */ @@ -899,10 +964,10 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall /** {@hide} */ public final long binderToken; /** {@hide} */ - public final String callingPackage; + public final Pair<String, String> callingPackage; /** {@hide} */ - public CallingIdentity(long binderToken, String callingPackage) { + public CallingIdentity(long binderToken, Pair<String, String> callingPackage) { this.binderToken = binderToken; this.callingPackage = callingPackage; } diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java index 8a4330ec0ede..d2632e78c00c 100644 --- a/core/java/android/content/ContentProviderClient.java +++ b/core/java/android/content/ContentProviderClient.java @@ -80,6 +80,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { private final IContentProvider mContentProvider; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private final String mPackageName; + private final @Nullable String mFeatureId; private final String mAuthority; private final boolean mStable; @@ -103,6 +104,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { mContentResolver = contentResolver; mContentProvider = contentProvider; mPackageName = contentResolver.mPackageName; + mFeatureId = contentResolver.mFeatureId; mAuthority = authority; mStable = stable; @@ -193,7 +195,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { cancellationSignal.setRemote(remoteCancellationSignal); } final Cursor cursor = mContentProvider.query( - mPackageName, uri, projection, queryArgs, remoteCancellationSignal); + mPackageName, mFeatureId, uri, projection, queryArgs, remoteCancellationSignal); if (cursor == null) { return null; } @@ -253,7 +255,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { beforeRemote(); try { - return mContentProvider.canonicalize(mPackageName, url); + return mContentProvider.canonicalize(mPackageName, mFeatureId, url); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -271,7 +273,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { beforeRemote(); try { - return mContentProvider.uncanonicalize(mPackageName, url); + return mContentProvider.uncanonicalize(mPackageName, mFeatureId, url); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -296,7 +298,8 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { remoteCancellationSignal = mContentProvider.createCancellationSignal(); cancellationSignal.setRemote(remoteCancellationSignal); } - return mContentProvider.refresh(mPackageName, url, args, remoteCancellationSignal); + return mContentProvider.refresh(mPackageName, mFeatureId, url, args, + remoteCancellationSignal); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -315,7 +318,8 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { beforeRemote(); try { - return mContentProvider.checkUriPermission(mPackageName, uri, uid, modeFlags); + return mContentProvider.checkUriPermission(mPackageName, mFeatureId, uri, uid, + modeFlags); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -334,7 +338,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { beforeRemote(); try { - return mContentProvider.insert(mPackageName, url, initialValues); + return mContentProvider.insert(mPackageName, mFeatureId, url, initialValues); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -354,7 +358,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { beforeRemote(); try { - return mContentProvider.bulkInsert(mPackageName, url, initialValues); + return mContentProvider.bulkInsert(mPackageName, mFeatureId, url, initialValues); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -373,7 +377,8 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { beforeRemote(); try { - return mContentProvider.delete(mPackageName, url, selection, selectionArgs); + return mContentProvider.delete(mPackageName, mFeatureId, url, selection, + selectionArgs); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -392,7 +397,8 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { beforeRemote(); try { - return mContentProvider.update(mPackageName, url, values, selection, selectionArgs); + return mContentProvider.update(mPackageName, mFeatureId, url, values, selection, + selectionArgs); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -436,7 +442,8 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { remoteSignal = mContentProvider.createCancellationSignal(); signal.setRemote(remoteSignal); } - return mContentProvider.openFile(mPackageName, url, mode, remoteSignal, null); + return mContentProvider.openFile(mPackageName, mFeatureId, url, mode, remoteSignal, + null); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -480,7 +487,8 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { remoteSignal = mContentProvider.createCancellationSignal(); signal.setRemote(remoteSignal); } - return mContentProvider.openAssetFile(mPackageName, url, mode, remoteSignal); + return mContentProvider.openAssetFile(mPackageName, mFeatureId, url, mode, + remoteSignal); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -521,7 +529,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { signal.setRemote(remoteSignal); } return mContentProvider.openTypedAssetFile( - mPackageName, uri, mimeTypeFilter, opts, remoteSignal); + mPackageName, mFeatureId, uri, mimeTypeFilter, opts, remoteSignal); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -548,7 +556,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { beforeRemote(); try { - return mContentProvider.applyBatch(mPackageName, authority, operations); + return mContentProvider.applyBatch(mPackageName, mFeatureId, authority, operations); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); @@ -574,7 +582,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { beforeRemote(); try { - return mContentProvider.call(mPackageName, authority, method, arg, extras); + return mContentProvider.call(mPackageName, mFeatureId, authority, method, arg, extras); } catch (DeadObjectException e) { if (!mStable) { mContentResolver.unstableProviderDied(mContentProvider); diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index cd735d4b10a3..f082690e2ceb 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -83,6 +83,7 @@ abstract public class ContentProviderNative extends Binder implements IContentPr data.enforceInterface(IContentProvider.descriptor); String callingPkg = data.readString(); + String callingFeatureId = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); // String[] projection @@ -101,7 +102,8 @@ abstract public class ContentProviderNative extends Binder implements IContentPr ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface( data.readStrongBinder()); - Cursor cursor = query(callingPkg, url, projection, queryArgs, cancellationSignal); + Cursor cursor = query(callingPkg, callingFeatureId, url, projection, queryArgs, + cancellationSignal); if (cursor != null) { CursorToBulkCursorAdaptor adaptor = null; @@ -148,10 +150,11 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { data.enforceInterface(IContentProvider.descriptor); String callingPkg = data.readString(); + String featureId = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); ContentValues values = ContentValues.CREATOR.createFromParcel(data); - Uri out = insert(callingPkg, url, values); + Uri out = insert(callingPkg, featureId, url, values); reply.writeNoException(); Uri.writeToParcel(reply, out); return true; @@ -161,10 +164,11 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { data.enforceInterface(IContentProvider.descriptor); String callingPkg = data.readString(); + String featureId = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); ContentValues[] values = data.createTypedArray(ContentValues.CREATOR); - int count = bulkInsert(callingPkg, url, values); + int count = bulkInsert(callingPkg, featureId, url, values); reply.writeNoException(); reply.writeInt(count); return true; @@ -174,6 +178,7 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { data.enforceInterface(IContentProvider.descriptor); String callingPkg = data.readString(); + String featureId = data.readString(); String authority = data.readString(); final int numOperations = data.readInt(); final ArrayList<ContentProviderOperation> operations = @@ -181,8 +186,8 @@ abstract public class ContentProviderNative extends Binder implements IContentPr for (int i = 0; i < numOperations; i++) { operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data)); } - final ContentProviderResult[] results = applyBatch(callingPkg, authority, - operations); + final ContentProviderResult[] results = applyBatch(callingPkg, featureId, + authority, operations); reply.writeNoException(); reply.writeTypedArray(results, 0); return true; @@ -192,11 +197,12 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { data.enforceInterface(IContentProvider.descriptor); String callingPkg = data.readString(); + String featureId = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); String selection = data.readString(); String[] selectionArgs = data.readStringArray(); - int count = delete(callingPkg, url, selection, selectionArgs); + int count = delete(callingPkg, featureId, url, selection, selectionArgs); reply.writeNoException(); reply.writeInt(count); @@ -207,12 +213,14 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { data.enforceInterface(IContentProvider.descriptor); String callingPkg = data.readString(); + String featureId = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); ContentValues values = ContentValues.CREATOR.createFromParcel(data); String selection = data.readString(); String[] selectionArgs = data.readStringArray(); - int count = update(callingPkg, url, values, selection, selectionArgs); + int count = update(callingPkg, featureId, url, values, selection, + selectionArgs); reply.writeNoException(); reply.writeInt(count); @@ -223,6 +231,7 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { data.enforceInterface(IContentProvider.descriptor); String callingPkg = data.readString(); + String featureId = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); String mode = data.readString(); ICancellationSignal signal = ICancellationSignal.Stub.asInterface( @@ -230,7 +239,7 @@ abstract public class ContentProviderNative extends Binder implements IContentPr IBinder callerToken = data.readStrongBinder(); ParcelFileDescriptor fd; - fd = openFile(callingPkg, url, mode, signal, callerToken); + fd = openFile(callingPkg, featureId, url, mode, signal, callerToken); reply.writeNoException(); if (fd != null) { reply.writeInt(1); @@ -246,13 +255,14 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { data.enforceInterface(IContentProvider.descriptor); String callingPkg = data.readString(); + String featureId = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); String mode = data.readString(); ICancellationSignal signal = ICancellationSignal.Stub.asInterface( data.readStrongBinder()); AssetFileDescriptor fd; - fd = openAssetFile(callingPkg, url, mode, signal); + fd = openAssetFile(callingPkg, featureId, url, mode, signal); reply.writeNoException(); if (fd != null) { reply.writeInt(1); @@ -269,12 +279,14 @@ abstract public class ContentProviderNative extends Binder implements IContentPr data.enforceInterface(IContentProvider.descriptor); String callingPkg = data.readString(); + String featureId = data.readString(); String authority = data.readString(); String method = data.readString(); String stringArg = data.readString(); Bundle args = data.readBundle(); - Bundle responseBundle = call(callingPkg, authority, method, stringArg, args); + Bundle responseBundle = call(callingPkg, featureId, authority, method, + stringArg, args); reply.writeNoException(); reply.writeBundle(responseBundle); @@ -297,6 +309,7 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { data.enforceInterface(IContentProvider.descriptor); String callingPkg = data.readString(); + String featureId = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); String mimeType = data.readString(); Bundle opts = data.readBundle(); @@ -304,7 +317,7 @@ abstract public class ContentProviderNative extends Binder implements IContentPr data.readStrongBinder()); AssetFileDescriptor fd; - fd = openTypedAssetFile(callingPkg, url, mimeType, opts, signal); + fd = openTypedAssetFile(callingPkg, featureId, url, mimeType, opts, signal); reply.writeNoException(); if (fd != null) { reply.writeInt(1); @@ -330,9 +343,10 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { data.enforceInterface(IContentProvider.descriptor); String callingPkg = data.readString(); + String featureId = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); - Uri out = canonicalize(callingPkg, url); + Uri out = canonicalize(callingPkg, featureId, url); reply.writeNoException(); Uri.writeToParcel(reply, out); return true; @@ -342,9 +356,10 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { data.enforceInterface(IContentProvider.descriptor); String callingPkg = data.readString(); + String featureId = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); - Uri out = uncanonicalize(callingPkg, url); + Uri out = uncanonicalize(callingPkg, featureId, url); reply.writeNoException(); Uri.writeToParcel(reply, out); return true; @@ -353,12 +368,13 @@ abstract public class ContentProviderNative extends Binder implements IContentPr case REFRESH_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); String callingPkg = data.readString(); + String featureId = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); Bundle args = data.readBundle(); ICancellationSignal signal = ICancellationSignal.Stub.asInterface( data.readStrongBinder()); - boolean out = refresh(callingPkg, url, args, signal); + boolean out = refresh(callingPkg, featureId, url, args, signal); reply.writeNoException(); reply.writeInt(out ? 0 : -1); return true; @@ -367,11 +383,12 @@ abstract public class ContentProviderNative extends Binder implements IContentPr case CHECK_URI_PERMISSION_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); String callingPkg = data.readString(); + String featureId = data.readString(); Uri uri = Uri.CREATOR.createFromParcel(data); int uid = data.readInt(); int modeFlags = data.readInt(); - int out = checkUriPermission(callingPkg, uri, uid, modeFlags); + int out = checkUriPermission(callingPkg, featureId, uri, uid, modeFlags); reply.writeNoException(); reply.writeInt(out); return true; @@ -407,8 +424,9 @@ final class ContentProviderProxy implements IContentProvider } @Override - public Cursor query(String callingPkg, Uri url, @Nullable String[] projection, - @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) + public Cursor query(String callingPkg, @Nullable String featureId, Uri url, + @Nullable String[] projection, @Nullable Bundle queryArgs, + @Nullable ICancellationSignal cancellationSignal) throws RemoteException { BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor(); Parcel data = Parcel.obtain(); @@ -417,6 +435,7 @@ final class ContentProviderProxy implements IContentProvider data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(callingPkg); + data.writeString(featureId); url.writeToParcel(data, 0); int length = 0; if (projection != null) { @@ -478,7 +497,8 @@ final class ContentProviderProxy implements IContentProvider } @Override - public Uri insert(String callingPkg, Uri url, ContentValues values) throws RemoteException + public Uri insert(String callingPkg, @Nullable String featureId, Uri url, + ContentValues values) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); @@ -486,6 +506,7 @@ final class ContentProviderProxy implements IContentProvider data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(callingPkg); + data.writeString(featureId); url.writeToParcel(data, 0); values.writeToParcel(data, 0); @@ -501,13 +522,15 @@ final class ContentProviderProxy implements IContentProvider } @Override - public int bulkInsert(String callingPkg, Uri url, ContentValues[] values) throws RemoteException { + public int bulkInsert(String callingPkg, @Nullable String featureId, Uri url, + ContentValues[] values) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(callingPkg); + data.writeString(featureId); url.writeToParcel(data, 0); data.writeTypedArray(values, 0); @@ -523,14 +546,15 @@ final class ContentProviderProxy implements IContentProvider } @Override - public ContentProviderResult[] applyBatch(String callingPkg, String authority, - ArrayList<ContentProviderOperation> operations) - throws RemoteException, OperationApplicationException { + public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String featureId, + String authority, ArrayList<ContentProviderOperation> operations) + throws RemoteException, OperationApplicationException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(callingPkg); + data.writeString(featureId); data.writeString(authority); data.writeInt(operations.size()); for (ContentProviderOperation operation : operations) { @@ -549,14 +573,15 @@ final class ContentProviderProxy implements IContentProvider } @Override - public int delete(String callingPkg, Uri url, String selection, String[] selectionArgs) - throws RemoteException { + public int delete(String callingPkg, @Nullable String featureId, Uri url, String selection, + String[] selectionArgs) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(callingPkg); + data.writeString(featureId); url.writeToParcel(data, 0); data.writeString(selection); data.writeStringArray(selectionArgs); @@ -573,14 +598,15 @@ final class ContentProviderProxy implements IContentProvider } @Override - public int update(String callingPkg, Uri url, ContentValues values, String selection, - String[] selectionArgs) throws RemoteException { + public int update(String callingPkg, @Nullable String featureId, Uri url, + ContentValues values, String selection, String[] selectionArgs) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(callingPkg); + data.writeString(featureId); url.writeToParcel(data, 0); values.writeToParcel(data, 0); data.writeString(selection); @@ -598,8 +624,8 @@ final class ContentProviderProxy implements IContentProvider } @Override - public ParcelFileDescriptor openFile( - String callingPkg, Uri url, String mode, ICancellationSignal signal, IBinder token) + public ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId, Uri url, + String mode, ICancellationSignal signal, IBinder token) throws RemoteException, FileNotFoundException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); @@ -607,6 +633,7 @@ final class ContentProviderProxy implements IContentProvider data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(callingPkg); + data.writeString(featureId); url.writeToParcel(data, 0); data.writeString(mode); data.writeStrongBinder(signal != null ? signal.asBinder() : null); @@ -626,8 +653,8 @@ final class ContentProviderProxy implements IContentProvider } @Override - public AssetFileDescriptor openAssetFile( - String callingPkg, Uri url, String mode, ICancellationSignal signal) + public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String featureId, + Uri url, String mode, ICancellationSignal signal) throws RemoteException, FileNotFoundException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); @@ -635,6 +662,7 @@ final class ContentProviderProxy implements IContentProvider data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(callingPkg); + data.writeString(featureId); url.writeToParcel(data, 0); data.writeString(mode); data.writeStrongBinder(signal != null ? signal.asBinder() : null); @@ -653,14 +681,15 @@ final class ContentProviderProxy implements IContentProvider } @Override - public Bundle call(String callingPkg, String authority, String method, String request, - Bundle args) throws RemoteException { + public Bundle call(String callingPkg, @Nullable String featureId, String authority, + String method, String request, Bundle args) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(callingPkg); + data.writeString(featureId); data.writeString(authority); data.writeString(method); data.writeString(request); @@ -700,14 +729,16 @@ final class ContentProviderProxy implements IContentProvider } @Override - public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri url, String mimeType, - Bundle opts, ICancellationSignal signal) throws RemoteException, FileNotFoundException { + public AssetFileDescriptor openTypedAssetFile(String callingPkg, @Nullable String featureId, + Uri url, String mimeType, Bundle opts, ICancellationSignal signal) + throws RemoteException, FileNotFoundException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(callingPkg); + data.writeString(featureId); url.writeToParcel(data, 0); data.writeString(mimeType); data.writeBundle(opts); @@ -747,14 +778,15 @@ final class ContentProviderProxy implements IContentProvider } @Override - public Uri canonicalize(String callingPkg, Uri url) throws RemoteException - { + public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri url) + throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(callingPkg); + data.writeString(featureId); url.writeToParcel(data, 0); mRemote.transact(IContentProvider.CANONICALIZE_TRANSACTION, data, reply, 0); @@ -769,13 +801,15 @@ final class ContentProviderProxy implements IContentProvider } @Override - public Uri uncanonicalize(String callingPkg, Uri url) throws RemoteException { + public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri url) + throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(callingPkg); + data.writeString(featureId); url.writeToParcel(data, 0); mRemote.transact(IContentProvider.UNCANONICALIZE_TRANSACTION, data, reply, 0); @@ -790,14 +824,15 @@ final class ContentProviderProxy implements IContentProvider } @Override - public boolean refresh(String callingPkg, Uri url, Bundle args, ICancellationSignal signal) - throws RemoteException { + public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, Bundle args, + ICancellationSignal signal) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(callingPkg); + data.writeString(featureId); url.writeToParcel(data, 0); data.writeBundle(args); data.writeStrongBinder(signal != null ? signal.asBinder() : null); @@ -814,14 +849,15 @@ final class ContentProviderProxy implements IContentProvider } @Override - public int checkUriPermission(String callingPkg, Uri url, int uid, int modeFlags) - throws RemoteException { + public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri url, int uid, + int modeFlags) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(callingPkg); + data.writeString(featureId); url.writeToParcel(data, 0); data.writeInt(uid); data.writeInt(modeFlags); diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 7f9ea76c95d6..2657cc54b91a 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -649,6 +649,7 @@ public abstract class ContentResolver implements ContentInterface { public ContentResolver(@Nullable Context context, @Nullable ContentInterface wrapped) { mContext = context != null ? context : ActivityThread.currentApplication(); mPackageName = mContext.getOpPackageName(); + mFeatureId = mContext.getFeatureId(); mTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion; mWrapped = wrapped; } @@ -968,7 +969,7 @@ public abstract class ContentResolver implements ContentInterface { cancellationSignal.setRemote(remoteCancellationSignal); } try { - qCursor = unstableProvider.query(mPackageName, uri, projection, + qCursor = unstableProvider.query(mPackageName, mFeatureId, uri, projection, queryArgs, remoteCancellationSignal); } catch (DeadObjectException e) { // The remote process has died... but we only hold an unstable @@ -979,8 +980,8 @@ public abstract class ContentResolver implements ContentInterface { if (stableProvider == null) { return null; } - qCursor = stableProvider.query( - mPackageName, uri, projection, queryArgs, remoteCancellationSignal); + qCursor = stableProvider.query(mPackageName, mFeatureId, uri, projection, + queryArgs, remoteCancellationSignal); } if (qCursor == null) { return null; @@ -1070,7 +1071,7 @@ public abstract class ContentResolver implements ContentInterface { } try { - return provider.canonicalize(mPackageName, url); + return provider.canonicalize(mPackageName, mFeatureId, url); } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. @@ -1114,7 +1115,7 @@ public abstract class ContentResolver implements ContentInterface { } try { - return provider.uncanonicalize(mPackageName, url); + return provider.uncanonicalize(mPackageName, mFeatureId, url); } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. @@ -1163,7 +1164,8 @@ public abstract class ContentResolver implements ContentInterface { remoteCancellationSignal = provider.createCancellationSignal(); cancellationSignal.setRemote(remoteCancellationSignal); } - return provider.refresh(mPackageName, url, args, remoteCancellationSignal); + return provider.refresh(mPackageName, mFeatureId, url, args, + remoteCancellationSignal); } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. @@ -1564,7 +1566,7 @@ public abstract class ContentResolver implements ContentInterface { try { fd = unstableProvider.openAssetFile( - mPackageName, uri, mode, remoteCancellationSignal); + mPackageName, mFeatureId, uri, mode, remoteCancellationSignal); if (fd == null) { // The provider will be released by the finally{} clause return null; @@ -1579,7 +1581,7 @@ public abstract class ContentResolver implements ContentInterface { throw new FileNotFoundException("No content provider: " + uri); } fd = stableProvider.openAssetFile( - mPackageName, uri, mode, remoteCancellationSignal); + mPackageName, mFeatureId, uri, mode, remoteCancellationSignal); if (fd == null) { // The provider will be released by the finally{} clause return null; @@ -1730,7 +1732,7 @@ public abstract class ContentResolver implements ContentInterface { try { fd = unstableProvider.openTypedAssetFile( - mPackageName, uri, mimeType, opts, remoteCancellationSignal); + mPackageName, mFeatureId, uri, mimeType, opts, remoteCancellationSignal); if (fd == null) { // The provider will be released by the finally{} clause return null; @@ -1745,7 +1747,7 @@ public abstract class ContentResolver implements ContentInterface { throw new FileNotFoundException("No content provider: " + uri); } fd = stableProvider.openTypedAssetFile( - mPackageName, uri, mimeType, opts, remoteCancellationSignal); + mPackageName, mFeatureId, uri, mimeType, opts, remoteCancellationSignal); if (fd == null) { // The provider will be released by the finally{} clause return null; @@ -1870,7 +1872,7 @@ public abstract class ContentResolver implements ContentInterface { } try { long startTime = SystemClock.uptimeMillis(); - Uri createdRow = provider.insert(mPackageName, url, values); + Uri createdRow = provider.insert(mPackageName, mFeatureId, url, values); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */); return createdRow; @@ -1951,7 +1953,7 @@ public abstract class ContentResolver implements ContentInterface { } try { long startTime = SystemClock.uptimeMillis(); - int rowsCreated = provider.bulkInsert(mPackageName, url, values); + int rowsCreated = provider.bulkInsert(mPackageName, mFeatureId, url, values); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogUpdateToEventLog(durationMillis, url, "bulkinsert", null /* where */); return rowsCreated; @@ -1991,7 +1993,8 @@ public abstract class ContentResolver implements ContentInterface { } try { long startTime = SystemClock.uptimeMillis(); - int rowsDeleted = provider.delete(mPackageName, url, where, selectionArgs); + int rowsDeleted = provider.delete(mPackageName, mFeatureId, url, where, + selectionArgs); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogUpdateToEventLog(durationMillis, url, "delete", where); return rowsDeleted; @@ -2035,7 +2038,8 @@ public abstract class ContentResolver implements ContentInterface { } try { long startTime = SystemClock.uptimeMillis(); - int rowsUpdated = provider.update(mPackageName, uri, values, where, selectionArgs); + int rowsUpdated = provider.update(mPackageName, mFeatureId, uri, values, where, + selectionArgs); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogUpdateToEventLog(durationMillis, uri, "update", where); return rowsUpdated; @@ -2084,7 +2088,8 @@ public abstract class ContentResolver implements ContentInterface { throw new IllegalArgumentException("Unknown authority " + authority); } try { - final Bundle res = provider.call(mPackageName, authority, method, arg, extras); + final Bundle res = provider.call(mPackageName, mFeatureId, authority, method, arg, + extras); Bundle.setDefusable(res, true); return res; } catch (RemoteException e) { @@ -3436,6 +3441,11 @@ public abstract class ContentResolver implements ContentInterface { return mPackageName; } + /** @hide */ + public @Nullable String getFeatureId() { + return mFeatureId; + } + @UnsupportedAppUsage private static volatile IContentService sContentService; @UnsupportedAppUsage @@ -3443,6 +3453,7 @@ public abstract class ContentResolver implements ContentInterface { @UnsupportedAppUsage final String mPackageName; + final @Nullable String mFeatureId; final int mTargetSdkVersion; final ContentInterface mWrapped; @@ -3638,19 +3649,19 @@ public abstract class ContentResolver implements ContentInterface { orientation.value = (extras != null) ? extras.getInt(EXTRA_ORIENTATION, 0) : 0; return afd; }), (ImageDecoder decoder, ImageInfo info, Source source) -> { - decoder.setAllocator(allocator); - - // One last-ditch check to see if we've been canceled. - if (signal != null) signal.throwIfCanceled(); - - // We requested a rough thumbnail size, but the remote size may have - // returned something giant, so defensively scale down as needed. - final int widthSample = info.getSize().getWidth() / size.getWidth(); - final int heightSample = info.getSize().getHeight() / size.getHeight(); - final int sample = Math.min(widthSample, heightSample); - if (sample > 1) { - decoder.setTargetSampleSize(sample); - } + decoder.setAllocator(allocator); + + // One last-ditch check to see if we've been canceled. + if (signal != null) signal.throwIfCanceled(); + + // We requested a rough thumbnail size, but the remote size may have + // returned something giant, so defensively scale down as needed. + final int widthSample = info.getSize().getWidth() / size.getWidth(); + final int heightSample = info.getSize().getHeight() / size.getHeight(); + final int sample = Math.max(widthSample, heightSample); + if (sample > 1) { + decoder.setTargetSampleSize(sample); + } }); // Transform the bitmap if requested. We use a side-channel to diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java index fade0ab6bb5c..d2c97c4ebdd3 100644 --- a/core/java/android/content/IContentProvider.java +++ b/core/java/android/content/IContentProvider.java @@ -16,7 +16,6 @@ package android.content; -import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.content.res.AssetFileDescriptor; @@ -38,66 +37,96 @@ import java.util.ArrayList; * @hide */ public interface IContentProvider extends IInterface { - public Cursor query(String callingPkg, Uri url, @Nullable String[] projection, + public Cursor query(String callingPkg, @Nullable String featureId, Uri url, + @Nullable String[] projection, @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) throws RemoteException; public String getType(Uri url) throws RemoteException; - @UnsupportedAppUsage - public Uri insert(String callingPkg, Uri url, ContentValues initialValues) - throws RemoteException; - @UnsupportedAppUsage - public int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues) - throws RemoteException; - @UnsupportedAppUsage - public int delete(String callingPkg, Uri url, String selection, String[] selectionArgs) + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link " + + "ContentProviderClient#insert(android.net.Uri, android.content.ContentValues)} " + + "instead") + public default Uri insert(String callingPkg, Uri url, ContentValues initialValues) + throws RemoteException { + return insert(callingPkg, null, url, initialValues); + } + public Uri insert(String callingPkg, String featureId, Uri url, ContentValues initialValues) throws RemoteException; - @UnsupportedAppUsage - public int update(String callingPkg, Uri url, ContentValues values, String selection, + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link " + + "ContentProviderClient#bulkInsert(android.net.Uri, android.content.ContentValues[])" + + "} instead") + public default int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues) + throws RemoteException { + return bulkInsert(callingPkg, null, url, initialValues); + } + public int bulkInsert(String callingPkg, String featureId, Uri url, + ContentValues[] initialValues) throws RemoteException; + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link " + + "ContentProviderClient#delete(android.net.Uri, java.lang.String, java.lang" + + ".String[])} instead") + public default int delete(String callingPkg, Uri url, String selection, String[] selectionArgs) + throws RemoteException { + return delete(callingPkg, null, url, selection, selectionArgs); + } + public int delete(String callingPkg, String featureId, Uri url, String selection, String[] selectionArgs) throws RemoteException; - public ParcelFileDescriptor openFile( - String callingPkg, Uri url, String mode, ICancellationSignal signal, - IBinder callerToken) - throws RemoteException, FileNotFoundException; - public AssetFileDescriptor openAssetFile( - String callingPkg, Uri url, String mode, ICancellationSignal signal) - throws RemoteException, FileNotFoundException; - @Deprecated - public default ContentProviderResult[] applyBatch(String callingPkg, - ArrayList<ContentProviderOperation> operations) - throws RemoteException, OperationApplicationException { - return applyBatch(callingPkg, "unknown", operations); + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link " + + "ContentProviderClient#update(android.net.Uri, android.content.ContentValues, java" + + ".lang.String, java.lang.String[])} instead") + public default int update(String callingPkg, Uri url, ContentValues values, String selection, + String[] selectionArgs) throws RemoteException { + return update(callingPkg, null, url, values, selection, selectionArgs); } + public int update(String callingPkg, String featureId, Uri url, ContentValues values, + String selection, String[] selectionArgs) throws RemoteException; + + public ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId, Uri url, + String mode, ICancellationSignal signal, IBinder callerToken) + throws RemoteException, FileNotFoundException; - public ContentProviderResult[] applyBatch(String callingPkg, String authority, - ArrayList<ContentProviderOperation> operations) + public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String featureId, + Uri url, String mode, ICancellationSignal signal) + throws RemoteException, FileNotFoundException; + + public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String featureId, + String authority, ArrayList<ContentProviderOperation> operations) throws RemoteException, OperationApplicationException; @Deprecated - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link " + + "ContentProviderClient#call(java.lang.String, java.lang.String, android.os.Bundle)} " + + "instead") public default Bundle call(String callingPkg, String method, @Nullable String arg, @Nullable Bundle extras) throws RemoteException { - return call(callingPkg, "unknown", method, arg, extras); + return call(callingPkg, null, "unknown", method, arg, extras); } - public Bundle call(String callingPkg, String authority, String method, - @Nullable String arg, @Nullable Bundle extras) throws RemoteException; + public Bundle call(String callingPkg, @Nullable String featureId, String authority, + String method, @Nullable String arg, @Nullable Bundle extras) throws RemoteException; - public int checkUriPermission(String callingPkg, Uri uri, int uid, int modeFlags) - throws RemoteException; + public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri, int uid, + int modeFlags) throws RemoteException; public ICancellationSignal createCancellationSignal() throws RemoteException; - public Uri canonicalize(String callingPkg, Uri uri) throws RemoteException; - public Uri uncanonicalize(String callingPkg, Uri uri) throws RemoteException; + public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) + throws RemoteException; + + public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri) + throws RemoteException; - public boolean refresh(String callingPkg, Uri url, @Nullable Bundle args, - ICancellationSignal cancellationSignal) throws RemoteException; + public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, + @Nullable Bundle args, ICancellationSignal cancellationSignal) throws RemoteException; // Data interchange. public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException; - public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri url, String mimeType, - Bundle opts, ICancellationSignal signal) throws RemoteException, FileNotFoundException; + + public AssetFileDescriptor openTypedAssetFile(String callingPkg, @Nullable String featureId, + Uri url, String mimeType, Bundle opts, ICancellationSignal signal) + throws RemoteException, FileNotFoundException; /* IPC constants */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index ac1cbd4619df..578d08617b5f 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -66,7 +66,6 @@ import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.lang.annotation.Retention; @@ -2641,75 +2640,4 @@ public final class Configuration implements Parcelable, Comparable<Configuration // For persistence, we don't care about assetsSeq and WindowConfiguration, so do not read it // out. } - - - /** - * Writes the Configuration's member fields as attributes into the XmlSerializer. - * The serializer is expected to have already started a tag so that attributes can be - * immediately written. - * - * @param xml The serializer to which to write the attributes. - * @param config The Configuration whose member fields to write. - * {@hide} - */ - public static void writeXmlAttrs(XmlSerializer xml, Configuration config) throws IOException { - XmlUtils.writeIntAttribute(xml, XML_ATTR_FONT_SCALE, - Float.floatToIntBits(config.fontScale)); - if (config.mcc != 0) { - XmlUtils.writeIntAttribute(xml, XML_ATTR_MCC, config.mcc); - } - if (config.mnc != 0) { - XmlUtils.writeIntAttribute(xml, XML_ATTR_MNC, config.mnc); - } - config.fixUpLocaleList(); - if (!config.mLocaleList.isEmpty()) { - XmlUtils.writeStringAttribute(xml, XML_ATTR_LOCALES, config.mLocaleList.toLanguageTags()); - } - if (config.touchscreen != TOUCHSCREEN_UNDEFINED) { - XmlUtils.writeIntAttribute(xml, XML_ATTR_TOUCHSCREEN, config.touchscreen); - } - if (config.keyboard != KEYBOARD_UNDEFINED) { - XmlUtils.writeIntAttribute(xml, XML_ATTR_KEYBOARD, config.keyboard); - } - if (config.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED) { - XmlUtils.writeIntAttribute(xml, XML_ATTR_KEYBOARD_HIDDEN, config.keyboardHidden); - } - if (config.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED) { - XmlUtils.writeIntAttribute(xml, XML_ATTR_HARD_KEYBOARD_HIDDEN, - config.hardKeyboardHidden); - } - if (config.navigation != NAVIGATION_UNDEFINED) { - XmlUtils.writeIntAttribute(xml, XML_ATTR_NAVIGATION, config.navigation); - } - if (config.navigationHidden != NAVIGATIONHIDDEN_UNDEFINED) { - XmlUtils.writeIntAttribute(xml, XML_ATTR_NAVIGATION_HIDDEN, config.navigationHidden); - } - if (config.orientation != ORIENTATION_UNDEFINED) { - XmlUtils.writeIntAttribute(xml, XML_ATTR_ORIENTATION, config.orientation); - } - if (config.screenLayout != SCREENLAYOUT_UNDEFINED) { - XmlUtils.writeIntAttribute(xml, XML_ATTR_SCREEN_LAYOUT, config.screenLayout); - } - if (config.colorMode != COLOR_MODE_UNDEFINED) { - XmlUtils.writeIntAttribute(xml, XML_ATTR_COLOR_MODE, config.colorMode); - } - if (config.uiMode != 0) { - XmlUtils.writeIntAttribute(xml, XML_ATTR_UI_MODE, config.uiMode); - } - if (config.screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED) { - XmlUtils.writeIntAttribute(xml, XML_ATTR_SCREEN_WIDTH, config.screenWidthDp); - } - if (config.screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED) { - XmlUtils.writeIntAttribute(xml, XML_ATTR_SCREEN_HEIGHT, config.screenHeightDp); - } - if (config.smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) { - XmlUtils.writeIntAttribute(xml, XML_ATTR_SMALLEST_WIDTH, config.smallestScreenWidthDp); - } - if (config.densityDpi != DENSITY_DPI_UNDEFINED) { - XmlUtils.writeIntAttribute(xml, XML_ATTR_DENSITY, config.densityDpi); - } - - // For persistence, we do not care about assetsSeq and window configuration, so do not write - // it out. - } } diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java index f641731fa08f..124b6c6f7377 100644 --- a/core/java/android/os/ServiceManagerNative.java +++ b/core/java/android/os/ServiceManagerNative.java @@ -86,6 +86,10 @@ class ServiceManagerProxy implements IServiceManager { throw new RemoteException(); } + public boolean isDeclared(String name) throws RemoteException { + throw new RemoteException(); + } + /** * Same as mServiceManager but used by apps. * diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java index d4abf28d2e63..9d14d9d8acf8 100644 --- a/core/java/android/os/SystemProperties.java +++ b/core/java/android/os/SystemProperties.java @@ -151,6 +151,7 @@ public class SystemProperties { * @hide */ @SystemApi + @TestApi public static int getInt(@NonNull String key, int def) { if (TRACK_KEY_ACCESS) onKeyAccess(key); return native_get_int(key, def); @@ -166,6 +167,7 @@ public class SystemProperties { * @hide */ @SystemApi + @TestApi public static long getLong(@NonNull String key, long def) { if (TRACK_KEY_ACCESS) onKeyAccess(key); return native_get_long(key, def); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 71b94ed41351..3476b18ceee0 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1314,7 +1314,8 @@ public class UserManager { } /** - * Returns whether switching users is currently allowed. + * Returns whether switching users is currently allowed for the user this process is running + * under. * <p> * Switching users is not allowed in the following cases: * <li>the user is in a phone call</li> @@ -1329,10 +1330,24 @@ public class UserManager { android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) public @UserSwitchabilityResult int getUserSwitchability() { - final boolean allowUserSwitchingWhenSystemUserLocked = Settings.Global.getInt( - mContext.getContentResolver(), - Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED, 0) != 0; - final boolean systemUserUnlocked = isUserUnlocked(UserHandle.SYSTEM); + return getUserSwitchability(Process.myUserHandle()); + } + + /** + * Returns whether switching users is currently allowed for the provided user. + * <p> + * Switching users is not allowed in the following cases: + * <li>the user is in a phone call</li> + * <li>{@link #DISALLOW_USER_SWITCH} is set</li> + * <li>system user hasn't been unlocked yet</li> + * + * @return A {@link UserSwitchabilityResult} flag indicating if the user is switchable. + * @hide + */ + @RequiresPermission(allOf = {Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) + public @UserSwitchabilityResult int getUserSwitchability(UserHandle userHandle) { final TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); @@ -1340,12 +1355,22 @@ public class UserManager { if (tm.getCallState() != TelephonyManager.CALL_STATE_IDLE) { flags |= SWITCHABILITY_STATUS_USER_IN_CALL; } - if (hasUserRestriction(DISALLOW_USER_SWITCH)) { + if (hasUserRestriction(DISALLOW_USER_SWITCH, userHandle)) { flags |= SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED; } - if (!allowUserSwitchingWhenSystemUserLocked && !systemUserUnlocked) { - flags |= SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED; + + // System User is always unlocked in Headless System User Mode, so ignore this flag + if (!isHeadlessSystemUserMode()) { + final boolean allowUserSwitchingWhenSystemUserLocked = Settings.Global.getInt( + mContext.getContentResolver(), + Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED, 0) != 0; + final boolean systemUserUnlocked = isUserUnlocked(UserHandle.SYSTEM); + + if (!allowUserSwitchingWhenSystemUserLocked && !systemUserUnlocked) { + flags |= SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED; + } } + return flags; } @@ -1884,7 +1909,14 @@ public class UserManager { * Returns the user-wide restrictions imposed on the user specified by <code>userHandle</code>. * @param userHandle the UserHandle of the user for whom to retrieve the restrictions. * @return a Bundle containing all the restrictions. + * + * <p>Requires {@code android.permission.MANAGE_USERS} or + * {@code android.permission.INTERACT_ACROSS_USERS}, otherwise specified {@link UserHandle user} + * must be the calling user or a managed profile associated with it. */ + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) public Bundle getUserRestrictions(UserHandle userHandle) { try { return mService.getUserRestrictions(userHandle.getIdentifier()); @@ -1975,7 +2007,7 @@ public class UserManager { * @return {@code true} if the current user has the given restriction, {@code false} otherwise. */ public boolean hasUserRestriction(String restrictionKey) { - return hasUserRestriction(restrictionKey, Process.myUserHandle()); + return hasUserRestrictionForUser(restrictionKey, Process.myUserHandle()); } /** @@ -1987,9 +2019,29 @@ public class UserManager { */ @UnsupportedAppUsage public boolean hasUserRestriction(String restrictionKey, UserHandle userHandle) { + return hasUserRestrictionForUser(restrictionKey, userHandle); + } + + /** + * Returns whether the given user has been disallowed from performing certain actions + * or setting certain settings. + * @param restrictionKey the string key representing the restriction + * @param userHandle the UserHandle of the user for whom to retrieve the restrictions. + * + * <p>Requires {@code android.permission.MANAGE_USERS} or + * {@code android.permission.INTERACT_ACROSS_USERS}, otherwise specified {@link UserHandle user} + * must be the calling user or a managed profile associated with it. + * + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) + public boolean hasUserRestrictionForUser(@NonNull String restrictionKey, + @NonNull UserHandle userHandle) { try { - return mService.hasUserRestriction(restrictionKey, - userHandle.getIdentifier()); + return mService.hasUserRestriction(restrictionKey, userHandle.getIdentifier()); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 8b8afd549c9f..bb8b041f6b46 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -586,9 +586,8 @@ public final class DeviceConfig { @RequiresPermission(WRITE_DEVICE_CONFIG) public static boolean setProperty(@NonNull String namespace, @NonNull String name, @Nullable String value, boolean makeDefault) { - String compositeName = createCompositeName(namespace, name); ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver(); - return Settings.Config.putString(contentResolver, compositeName, value, makeDefault); + return Settings.Config.putString(contentResolver, namespace, name, value, makeDefault); } /** @@ -672,12 +671,6 @@ public final class DeviceConfig { } } - private static String createCompositeName(@NonNull String namespace, @NonNull String name) { - Preconditions.checkNotNull(namespace); - Preconditions.checkNotNull(name); - return namespace + "/" + name; - } - private static Uri createNamespaceUri(@NonNull String namespace) { Preconditions.checkNotNull(namespace); return CONTENT_URI.buildUpon().appendPath(namespace).build(); diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java index 2143a0deb723..a80153d691f0 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -1081,7 +1081,8 @@ public abstract class DocumentsProvider extends ContentProvider { // signed with platform signature can hold MANAGE_DOCUMENTS, we are going to check for // MANAGE_DOCUMENTS or associated URI permission here instead final Uri rootUri = extras.getParcelable(DocumentsContract.EXTRA_URI); - enforceWritePermissionInner(rootUri, getCallingPackage(), null); + enforceWritePermissionInner(rootUri, getCallingPackage(), getCallingFeatureId(), + null); final String rootId = DocumentsContract.getRootId(rootUri); ejectRoot(rootId); @@ -1102,7 +1103,8 @@ public abstract class DocumentsProvider extends ContentProvider { enforceTree(documentUri); if (METHOD_IS_CHILD_DOCUMENT.equals(method)) { - enforceReadPermissionInner(documentUri, getCallingPackage(), null); + enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(), + null); final Uri childUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI); final String childAuthority = childUri.getAuthority(); @@ -1114,7 +1116,8 @@ public abstract class DocumentsProvider extends ContentProvider { && isChildDocument(documentId, childId)); } else if (METHOD_CREATE_DOCUMENT.equals(method)) { - enforceWritePermissionInner(documentUri, getCallingPackage(), null); + enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(), + null); final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE); final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME); @@ -1128,7 +1131,8 @@ public abstract class DocumentsProvider extends ContentProvider { out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri); } else if (METHOD_CREATE_WEB_LINK_INTENT.equals(method)) { - enforceWritePermissionInner(documentUri, getCallingPackage(), null); + enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(), + null); final Bundle options = extras.getBundle(DocumentsContract.EXTRA_OPTIONS); final IntentSender intentSender = createWebLinkIntent(documentId, options); @@ -1136,7 +1140,8 @@ public abstract class DocumentsProvider extends ContentProvider { out.putParcelable(DocumentsContract.EXTRA_RESULT, intentSender); } else if (METHOD_RENAME_DOCUMENT.equals(method)) { - enforceWritePermissionInner(documentUri, getCallingPackage(), null); + enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(), + null); final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME); final String newDocumentId = renameDocument(documentId, displayName); @@ -1160,7 +1165,8 @@ public abstract class DocumentsProvider extends ContentProvider { } } else if (METHOD_DELETE_DOCUMENT.equals(method)) { - enforceWritePermissionInner(documentUri, getCallingPackage(), null); + enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(), + null); deleteDocument(documentId); // Document no longer exists, clean up any grants. @@ -1170,8 +1176,10 @@ public abstract class DocumentsProvider extends ContentProvider { final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI); final String targetId = DocumentsContract.getDocumentId(targetUri); - enforceReadPermissionInner(documentUri, getCallingPackage(), null); - enforceWritePermissionInner(targetUri, getCallingPackage(), null); + enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(), + null); + enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingFeatureId(), + null); final String newDocumentId = copyDocument(documentId, targetId); @@ -1194,9 +1202,12 @@ public abstract class DocumentsProvider extends ContentProvider { final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI); final String targetId = DocumentsContract.getDocumentId(targetUri); - enforceWritePermissionInner(documentUri, getCallingPackage(), null); - enforceReadPermissionInner(parentSourceUri, getCallingPackage(), null); - enforceWritePermissionInner(targetUri, getCallingPackage(), null); + enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(), + null); + enforceReadPermissionInner(parentSourceUri, getCallingPackage(), getCallingFeatureId(), + null); + enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingFeatureId(), + null); final String newDocumentId = moveDocument(documentId, parentSourceId, targetId); @@ -1217,8 +1228,10 @@ public abstract class DocumentsProvider extends ContentProvider { final Uri parentSourceUri = extras.getParcelable(DocumentsContract.EXTRA_PARENT_URI); final String parentSourceId = DocumentsContract.getDocumentId(parentSourceUri); - enforceReadPermissionInner(parentSourceUri, getCallingPackage(), null); - enforceWritePermissionInner(documentUri, getCallingPackage(), null); + enforceReadPermissionInner(parentSourceUri, getCallingPackage(), getCallingFeatureId(), + null); + enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(), + null); removeDocument(documentId, parentSourceId); // It's responsibility of the provider to revoke any grants, as the document may be @@ -1227,7 +1240,8 @@ public abstract class DocumentsProvider extends ContentProvider { final boolean isTreeUri = isTreeUri(documentUri); if (isTreeUri) { - enforceReadPermissionInner(documentUri, getCallingPackage(), null); + enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(), + null); } else { getContext().enforceCallingPermission(Manifest.permission.MANAGE_DOCUMENTS, null); } diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java index 8f772d4ec780..fcbda5f68f96 100644 --- a/core/java/android/provider/FontsContract.java +++ b/core/java/android/provider/FontsContract.java @@ -334,10 +334,17 @@ public class FontsContract { return cachedTypeface; } - // Unfortunately the typeface is not available at this time, but requesting from the font - // provider takes too much time. For now, request the font data to ensure it is in the cache - // next time and return. synchronized (sLock) { + // It is possible that Font is loaded during the thread sleep time + // re-check the cache to avoid re-loading the font + cachedTypeface = sTypefaceCache.get(id); + if (cachedTypeface != null) { + return cachedTypeface; + } + + // Unfortunately the typeface is not available at this time, but requesting from + // the font provider takes too much time. For now, request the font data to ensure + // it is in the cache next time and return. if (sHandler == null) { sThread = new HandlerThread("fonts", Process.THREAD_PRIORITY_BACKGROUND); sThread.start(); diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index a1333df13820..aa67d9779da4 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -47,6 +47,7 @@ import android.graphics.PostProcessor; import android.media.ExifInterface; import android.media.MediaFile; import android.media.MediaFormat; +import android.media.MediaMetadataRetriever; import android.net.Uri; import android.os.Bundle; import android.os.CancellationSignal; @@ -589,9 +590,7 @@ public final class MediaStore { * @see MediaStore#setIncludeTrashed(Uri) * @see MediaStore#trash(Context, Uri) * @see MediaStore#untrash(Context, Uri) - * @removed */ - @Deprecated public static @NonNull Uri setIncludeTrashed(@NonNull Uri uri) { return uri.buildUpon().appendQueryParameter(PARAM_INCLUDE_TRASHED, "1").build(); } @@ -830,9 +829,7 @@ public final class MediaStore { * @see MediaStore#setIncludeTrashed(Uri) * @see MediaStore#trash(Context, Uri) * @see MediaStore#untrash(Context, Uri) - * @removed */ - @Deprecated public static void trash(@NonNull Context context, @NonNull Uri uri) { trash(context, uri, 48 * DateUtils.HOUR_IN_MILLIS); } @@ -850,9 +847,7 @@ public final class MediaStore { * @see MediaStore#setIncludeTrashed(Uri) * @see MediaStore#trash(Context, Uri) * @see MediaStore#untrash(Context, Uri) - * @removed */ - @Deprecated public static void trash(@NonNull Context context, @NonNull Uri uri, @DurationMillisLong long timeoutMillis) { if (timeoutMillis < 0) { @@ -874,9 +869,7 @@ public final class MediaStore { * @see MediaStore#setIncludeTrashed(Uri) * @see MediaStore#trash(Context, Uri) * @see MediaStore#untrash(Context, Uri) - * @removed */ - @Deprecated public static void untrash(@NonNull Context context, @NonNull Uri uri) { final ContentValues values = new ContentValues(); values.put(MediaColumns.IS_TRASHED, 0); @@ -907,26 +900,8 @@ public final class MediaStore { public static final String DATA = "_data"; /** - * Hash of the media item on disk. - * <p> - * Contains a 20-byte binary blob which is the SHA-1 hash of the file as - * persisted on disk. For performance reasons, the hash may not be - * immediately available, in which case a {@code NULL} value will be - * returned. If the underlying file is modified, this value will be - * cleared and recalculated. - * <p> - * If you require the hash of a specific item, you can call - * {@link ContentResolver#canonicalize(Uri)}, which will block until the - * hash is calculated. - * - * @removed - */ - @Deprecated - @Column(value = Cursor.FIELD_TYPE_BLOB, readOnly = true) - public static final String HASH = "_hash"; - - /** - * The size of the media item. + * Indexed value of {@link File#length()} extracted from this media + * item. */ @BytesLong @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) @@ -943,12 +918,6 @@ public final class MediaStore { public static final String DISPLAY_NAME = "_display_name"; /** - * The title of the media item. - */ - @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) - public static final String TITLE = "title"; - - /** * The time the media item was first added. */ @CurrentTimeSecondsLong @@ -956,14 +925,22 @@ public final class MediaStore { public static final String DATE_ADDED = "date_added"; /** - * The time the media item was last modified. + * Indexed value of {@link File#lastModified()} extracted from this + * media item. */ @CurrentTimeSecondsLong @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String DATE_MODIFIED = "date_modified"; /** - * The time the media item was taken. + * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_DATE} or + * {@link ExifInterface#TAG_DATETIME_ORIGINAL} extracted from this media + * item. + * <p> + * Note that images must define both + * {@link ExifInterface#TAG_DATETIME_ORIGINAL} and + * {@code ExifInterface#TAG_OFFSET_TIME_ORIGINAL} to reliably determine + * this value in relation to the epoch. */ @CurrentTimeMillisLong @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) @@ -989,17 +966,6 @@ public final class MediaStore { public static final String MIME_TYPE = "mime_type"; /** - * The MTP object handle of a newly transfered file. - * Used to pass the new file's object handle through the media scanner - * from MTP to the media provider - * For internal use only by MTP, media scanner and media provider. - * @hide - */ - @Deprecated - // @Column(Cursor.FIELD_TYPE_INTEGER) - public static final String MEDIA_SCANNER_NEW_OBJECT_ID = "media_scanner_new_object_id"; - - /** * Non-zero if the media file is drm-protected * @hide */ @@ -1012,6 +978,10 @@ public final class MediaStore { * Flag indicating if a media item is pending, and still being inserted * by its owner. While this flag is set, only the owner of the item can * open the underlying file; requests from other apps will be rejected. + * <p> + * Pending items are retained either until they are published by setting + * the field to {@code 0}, or until they expire as defined by + * {@link #DATE_EXPIRES}. * * @see MediaStore#setIncludePending(Uri) */ @@ -1020,38 +990,54 @@ public final class MediaStore { /** * Flag indicating if a media item is trashed. + * <p> + * Trashed items are retained until they expire as defined by + * {@link #DATE_EXPIRES}. * * @see MediaColumns#IS_TRASHED * @see MediaStore#setIncludeTrashed(Uri) * @see MediaStore#trash(Context, Uri) * @see MediaStore#untrash(Context, Uri) - * @removed */ - @Deprecated @Column(Cursor.FIELD_TYPE_INTEGER) public static final String IS_TRASHED = "is_trashed"; /** * The time the media item should be considered expired. Typically only - * meaningful in the context of {@link #IS_PENDING}. + * meaningful in the context of {@link #IS_PENDING} or + * {@link #IS_TRASHED}. */ @CurrentTimeSecondsLong @Column(Cursor.FIELD_TYPE_INTEGER) public static final String DATE_EXPIRES = "date_expires"; /** - * The width of the media item, in pixels. + * Indexed value of + * {@link MediaMetadataRetriever#METADATA_KEY_VIDEO_WIDTH}, + * {@link MediaMetadataRetriever#METADATA_KEY_IMAGE_WIDTH} or + * {@link ExifInterface#TAG_IMAGE_WIDTH} extracted from this media item. */ @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String WIDTH = "width"; /** - * The height of the media item, in pixels. + * Indexed value of + * {@link MediaMetadataRetriever#METADATA_KEY_VIDEO_HEIGHT}, + * {@link MediaMetadataRetriever#METADATA_KEY_IMAGE_HEIGHT} or + * {@link ExifInterface#TAG_IMAGE_LENGTH} extracted from this media + * item. */ @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String HEIGHT = "height"; /** + * Calculated value that combines {@link #WIDTH} and {@link #HEIGHT} + * into a user-presentable string. + */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + public static final String RESOLUTION = "resolution"; + + /** * Package name that contributed this media. The value may be * {@code NULL} if ownership cannot be reliably determined. */ @@ -1097,28 +1083,6 @@ public final class MediaStore { public static final String RELATIVE_PATH = "relative_path"; /** - * The primary directory name this media exists under. The value may be - * {@code NULL} if the media doesn't have a primary directory name. - * - * @removed - * @deprecated Replaced by {@link #RELATIVE_PATH}. - */ - @Column(Cursor.FIELD_TYPE_STRING) - @Deprecated - public static final String PRIMARY_DIRECTORY = "primary_directory"; - - /** - * The secondary directory name this media exists under. The value may - * be {@code NULL} if the media doesn't have a secondary directory name. - * - * @removed - * @deprecated Replaced by {@link #RELATIVE_PATH}. - */ - @Column(Cursor.FIELD_TYPE_STRING) - @Deprecated - public static final String SECONDARY_DIRECTORY = "secondary_directory"; - - /** * The primary bucket ID of this media item. This can be useful to * present the user a first-level clustering of related media items. * This is a read-only column that is automatically computed. @@ -1191,18 +1155,171 @@ public final class MediaStore { public static final String ORIGINAL_DOCUMENT_ID = "original_document_id"; /** - * The duration of the media item. + * Indexed value of + * {@link MediaMetadataRetriever#METADATA_KEY_VIDEO_ROTATION}, + * {@link MediaMetadataRetriever#METADATA_KEY_IMAGE_ROTATION}, or + * {@link ExifInterface#TAG_ORIENTATION} extracted from this media item. + * <p> + * For consistency the indexed value is expressed in degrees, such as 0, + * 90, 180, or 270. + */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) + public static final String ORIENTATION = "orientation"; + + /** + * Flag indicating if the media item has been marked as being a + * "favorite" by the user. + */ + @Column(Cursor.FIELD_TYPE_INTEGER) + public static final String IS_FAVORITE = "is_favorite"; + + // ======================================= + // ==== MediaMetadataRetriever values ==== + // ======================================= + + /** + * Indexed value of + * {@link MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER} extracted + * from this media item. + */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + public static final String CD_TRACK_NUMBER = "cd_track_number"; + + /** + * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_ALBUM} + * extracted from this media item. + */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + public static final String ALBUM = "album"; + + /** + * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_ARTIST} + * or {@link ExifInterface#TAG_ARTIST} extracted from this media item. + */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + public static final String ARTIST = "artist"; + + /** + * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_AUTHOR} + * extracted from this media item. + */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + public static final String AUTHOR = "author"; + + /** + * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_COMPOSER} + * extracted from this media item. + */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + public static final String COMPOSER = "composer"; + + // METADATA_KEY_DATE is DATE_TAKEN + + /** + * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_GENRE} + * extracted from this media item. + */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + public static final String GENRE = "genre"; + + /** + * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_TITLE} + * extracted from this media item. + */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + public static final String TITLE = "title"; + + /** + * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_YEAR} + * extracted from this media item. + */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) + public static final String YEAR = "year"; + + /** + * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_DURATION} + * extracted from this media item. */ @DurationMillisLong @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String DURATION = "duration"; /** - * The orientation for the media item, expressed in degrees. For - * example, 0, 90, 180, or 270 degrees. + * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_NUM_TRACKS} + * extracted from this media item. */ @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) - public static final String ORIENTATION = "orientation"; + public static final String NUM_TRACKS = "num_tracks"; + + /** + * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_WRITER} + * extracted from this media item. + */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + public static final String WRITER = "writer"; + + // METADATA_KEY_MIMETYPE is MIME_TYPE + + /** + * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST} + * extracted from this media item. + */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + public static final String ALBUM_ARTIST = "album_artist"; + + /** + * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER} + * extracted from this media item. + */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + public static final String DISC_NUMBER = "disc_number"; + + /** + * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_COMPILATION} + * extracted from this media item. + */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + public static final String COMPILATION = "compilation"; + + // HAS_AUDIO is ignored + // HAS_VIDEO is ignored + // VIDEO_WIDTH is WIDTH + // VIDEO_HEIGHT is HEIGHT + + /** + * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_BITRATE} + * extracted from this media item. + */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) + public static final String BITRATE = "bitrate"; + + // TIMED_TEXT_LANGUAGES is ignored + // IS_DRM is ignored + // LOCATION is LATITUDE and LONGITUDE + // VIDEO_ROTATION is ORIENTATION + + /** + * Indexed value of + * {@link MediaMetadataRetriever#METADATA_KEY_CAPTURE_FRAMERATE} + * extracted from this media item. + */ + @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true) + public static final String CAPTURE_FRAMERATE = "capture_framerate"; + + // HAS_IMAGE is ignored + // IMAGE_COUNT is ignored + // IMAGE_PRIMARY is ignored + // IMAGE_WIDTH is WIDTH + // IMAGE_HEIGHT is HEIGHT + // IMAGE_ROTATION is ORIENTATION + // VIDEO_FRAME_COUNT is ignored + // EXIF_OFFSET is ignored + // EXIF_LENGTH is ignored + // COLOR_STANDARD is ignored + // COLOR_TRANSFER is ignored + // COLOR_RANGE is ignored + // SAMPLERATE is ignored + // BITS_PER_SAMPLE is ignored } /** @@ -1331,10 +1448,7 @@ public final class MediaStore { @Column(Cursor.FIELD_TYPE_STRING) public static final String MIME_TYPE = "mime_type"; - /** - * The title of the media item. - */ - @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + /** @removed promoted to parent interface */ public static final String TITLE = "title"; /** @@ -1589,12 +1703,6 @@ public final class MediaStore { */ public interface ImageColumns extends MediaColumns { /** - * The description of the image - */ - @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) - public static final String DESCRIPTION = "description"; - - /** * The picasa id of the image * * @deprecated this value was only relevant for images hosted on @@ -1656,6 +1764,34 @@ public final class MediaStore { public static final String BUCKET_DISPLAY_NAME = "bucket_display_name"; /** @removed promoted to parent interface */ public static final String GROUP_ID = "group_id"; + + /** + * Indexed value of {@link ExifInterface#TAG_IMAGE_DESCRIPTION} + * extracted from this media item. + */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + public static final String DESCRIPTION = "description"; + + /** + * Indexed value of {@link ExifInterface#TAG_EXPOSURE_TIME} + * extracted from this media item. + */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + public static final String EXPOSURE_TIME = "exposure_time"; + + /** + * Indexed value of {@link ExifInterface#TAG_F_NUMBER} + * extracted from this media item. + */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + public static final String F_NUMBER = "f_number"; + + /** + * Indexed value of {@link ExifInterface#TAG_ISO_SPEED_RATINGS} + * extracted from this media item. + */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) + public static final String ISO = "iso"; } public static final class Media implements ImageColumns { @@ -1904,6 +2040,10 @@ public final class MediaStore { * generated. Callers are responsible for their own in-memory * caching of returned values. * + * As of {@link android.os.Build.VERSION_CODES#Q}, this output + * of the thumbnail has correct rotation, don't need to rotate + * it again. + * * @param imageId the image item to obtain a thumbnail for. * @param kind optimal thumbnail size desired. * @return decoded thumbnail, or {@code null} if problem was @@ -1946,6 +2086,10 @@ public final class MediaStore { * generated. Callers are responsible for their own in-memory * caching of returned values. * + * As of {@link android.os.Build.VERSION_CODES#Q}, this output + * of the thumbnail has correct rotation, don't need to rotate + * it again. + * * @param imageId the image item to obtain a thumbnail for. * @param kind optimal thumbnail size desired. * @return decoded thumbnail, or {@code null} if problem was @@ -2000,6 +2144,9 @@ public final class MediaStore { * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain * access. * + * As of {@link android.os.Build.VERSION_CODES#Q}, this thumbnail + * has correct rotation, don't need to rotate it again. + * * @deprecated Apps may not have filesystem permissions to directly * access this path. Instead of trying to open this path * directly, apps should use @@ -2093,10 +2240,7 @@ public final class MediaStore { @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String ARTIST_ID = "artist_id"; - /** - * The artist who created the audio file, if any - */ - @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + /** @removed promoted to parent interface */ public static final String ARTIST = "artist"; /** @@ -2107,14 +2251,6 @@ public final class MediaStore { public static final String ALBUM_ARTIST = "album_artist"; /** - * Whether the song is part of a compilation - * @hide - */ - @Deprecated - // @Column(Cursor.FIELD_TYPE_STRING) - public static final String COMPILATION = "compilation"; - - /** * A non human readable key calculated from the ARTIST, used for * searching, sorting and grouping * @@ -2131,10 +2267,7 @@ public final class MediaStore { @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String ARTIST_KEY = "artist_key"; - /** - * The composer of the audio file, if any - */ - @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + /** @removed promoted to parent interface */ public static final String COMPOSER = "composer"; /** @@ -2143,10 +2276,7 @@ public final class MediaStore { @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String ALBUM_ID = "album_id"; - /** - * The album the audio file is from, if any - */ - @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + /** @removed promoted to parent interface */ public static final String ALBUM = "album"; /** @@ -2973,23 +3103,11 @@ public final class MediaStore { public interface VideoColumns extends MediaColumns { /** @removed promoted to parent interface */ public static final String DURATION = "duration"; - - /** - * The artist who created the video file, if any - */ - @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + /** @removed promoted to parent interface */ public static final String ARTIST = "artist"; - - /** - * The album the video file is from, if any - */ - @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + /** @removed promoted to parent interface */ public static final String ALBUM = "album"; - - /** - * The resolution of the video file, formatted as "XxY" - */ - @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + /** @removed promoted to parent interface */ public static final String RESOLUTION = "resolution"; /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index aeed20db2850..44ab09ed8ad4 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -78,6 +78,7 @@ import android.util.MemoryIntArray; import android.view.Display; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; import com.android.internal.widget.ILockSettings; import java.io.IOException; @@ -1722,6 +1723,20 @@ public final class Settings { = "android.settings.ENTERPRISE_PRIVACY_SETTINGS"; /** + * Activity Action: Show Work Policy info. + * DPC apps can implement an activity that handles this intent in order to show device policies + * associated with the work profile or managed device. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + * + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_SHOW_WORK_POLICY_INFO = + "android.settings.SHOW_WORK_POLICY_INFO"; + + /** * Activity Action: Show screen that let user select its Autofill Service. * <p> * Input: Intent's data URI set with an application name, using the @@ -2292,8 +2307,8 @@ public final class Settings { arg.putBoolean(CALL_METHOD_MAKE_DEFAULT_KEY, true); } IContentProvider cp = mProviderHolder.getProvider(cr); - cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(), - mCallSetCommand, name, arg); + cp.call(cr.getPackageName(), cr.getFeatureId(), + mProviderHolder.mUri.getAuthority(), mCallSetCommand, name, arg); } catch (RemoteException e) { Log.w(TAG, "Can't set key " + name + " in " + mUri, e); return false; @@ -2366,14 +2381,15 @@ public final class Settings { if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) { final long token = Binder.clearCallingIdentity(); try { - b = cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(), - mCallGetCommand, name, args); + b = cp.call(cr.getPackageName(), cr.getFeatureId(), + mProviderHolder.mUri.getAuthority(), mCallGetCommand, name, + args); } finally { Binder.restoreCallingIdentity(token); } } else { - b = cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(), - mCallGetCommand, name, args); + b = cp.call(cr.getPackageName(), cr.getFeatureId(), + mProviderHolder.mUri.getAuthority(), mCallGetCommand, name, args); } if (b != null) { String value = b.getString(Settings.NameValueTable.VALUE); @@ -2441,14 +2457,14 @@ public final class Settings { if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) { final long token = Binder.clearCallingIdentity(); try { - c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE_PROJECTION, queryArgs, - null); + c = cp.query(cr.getPackageName(), cr.getFeatureId(), mUri, + SELECT_VALUE_PROJECTION, queryArgs, null); } finally { Binder.restoreCallingIdentity(token); } } else { - c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE_PROJECTION, queryArgs, - null); + c = cp.query(cr.getPackageName(), cr.getFeatureId(), mUri, + SELECT_VALUE_PROJECTION, queryArgs, null); } if (c == null) { Log.w(TAG, "Can't get key " + name + " from " + mUri); @@ -2492,7 +2508,7 @@ public final class Settings { boolean prefixCached = false; int size = mValues.size(); for (int i = 0; i < size; ++i) { - if (mValues.keyAt(i).startsWith(prefix + "/")) { + if (mValues.keyAt(i).startsWith(prefix)) { prefixCached = true; break; } @@ -2507,7 +2523,7 @@ public final class Settings { } else { for (int i = 0; i < size; ++i) { String key = mValues.keyAt(i); - if (key.startsWith(prefix + "/")) { + if (key.startsWith(prefix)) { keyValues.put(key, mValues.get(key)); } } @@ -2543,8 +2559,8 @@ public final class Settings { } // Fetch all flags for the namespace at once for caching purposes - Bundle b = cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(), - mCallListCommand, null, args); + Bundle b = cp.call(cr.getPackageName(), cr.getFeatureId(), + mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args); if (b == null) { // Invalid response, return an empty map return keyValues; @@ -5118,8 +5134,8 @@ public final class Settings { } arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode); IContentProvider cp = sProviderHolder.getProvider(resolver); - cp.call(resolver.getPackageName(), sProviderHolder.mUri.getAuthority(), - CALL_METHOD_RESET_SECURE, null, arg); + cp.call(resolver.getPackageName(), resolver.getFeatureId(), + sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_SECURE, null, arg); } catch (RemoteException e) { Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e); } @@ -8254,6 +8270,12 @@ public final class Settings { public static final String AWARE_LOCK_ENABLED = "aware_lock_enabled"; /** + * Controls whether tap gesture is enabled. + * @hide + */ + public static final String TAP_GESTURE = "tap_gesture"; + + /** * Keys we no longer back up under the current schema, but want to continue to * process when restoring historical backup datasets. * @@ -12807,8 +12829,8 @@ public final class Settings { } arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode); IContentProvider cp = sProviderHolder.getProvider(resolver); - cp.call(resolver.getPackageName(), sProviderHolder.mUri.getAuthority(), - CALL_METHOD_RESET_GLOBAL, null, arg); + cp.call(resolver.getPackageName(), resolver.getFeatureId(), + sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_GLOBAL, null, arg); } catch (RemoteException e) { Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e); } @@ -13664,10 +13686,10 @@ public final class Settings { } /** - * Look up a list of names in the database, based on a common prefix. + * Look up a list of names in the database, within the specified namespace. * * @param resolver to access the database with - * @param prefix to apply to all of the names which will be fetched + * @param namespace to which the names belong * @param names to look up in the table * @return a non null, but possibly empty, map from name to value for any of the names that * were found during lookup. @@ -13676,16 +13698,17 @@ public final class Settings { */ @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG) static Map<String, String> getStrings(@NonNull ContentResolver resolver, - @NonNull String prefix, @NonNull List<String> names) { - List<String> concatenatedNames = new ArrayList<>(names.size()); + @NonNull String namespace, @NonNull List<String> names) { + List<String> compositeNames = new ArrayList<>(names.size()); for (String name : names) { - concatenatedNames.add(prefix + "/" + name); + compositeNames.add(createCompositeName(namespace, name)); } + String prefix = createPrefix(namespace); ArrayMap<String, String> rawKeyValues = sNameValueCache.getStringsForPrefix( - resolver, prefix, concatenatedNames); + resolver, prefix, compositeNames); int size = rawKeyValues.size(); - int substringLength = prefix.length() + 1; + int substringLength = prefix.length(); ArrayMap<String, String> keyValues = new ArrayMap<>(size); for (int i = 0; i < size; ++i) { keyValues.put(rawKeyValues.keyAt(i).substring(substringLength), @@ -13695,7 +13718,7 @@ public final class Settings { } /** - * Store a name/value pair into the database. + * Store a name/value pair into the database within the specified namespace. * <p> * Also the method takes an argument whether to make the value the default for this setting. * If the system already specified a default value, then the one passed in here will @@ -13703,6 +13726,7 @@ public final class Settings { * </p> * * @param resolver to access the database with. + * @param namespace to store the name/value pair in. * @param name to store. * @param value to associate with the name. * @param makeDefault whether to make the value the default one. @@ -13713,10 +13737,10 @@ public final class Settings { * @hide */ @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG) - static boolean putString(@NonNull ContentResolver resolver, @NonNull String name, - @Nullable String value, boolean makeDefault) { - return sNameValueCache.putStringForUser(resolver, name, value, null, makeDefault, - resolver.getUserId()); + static boolean putString(@NonNull ContentResolver resolver, @NonNull String namespace, + @NonNull String name, @Nullable String value, boolean makeDefault) { + return sNameValueCache.putStringForUser(resolver, createCompositeName(namespace, name), + value, null, makeDefault, resolver.getUserId()); } /** @@ -13727,29 +13751,40 @@ public final class Settings { * * @param resolver Handle to the content resolver. * @param resetMode The reset mode to use. - * @param prefix Optionally, to limit which which pairs are reset. + * @param namespace Optionally, to limit which which namespace is reset. * - * @see #putString(ContentResolver, String, String, boolean) + * @see #putString(ContentResolver, String, String, String, boolean) * * @hide */ @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG) static void resetToDefaults(@NonNull ContentResolver resolver, @ResetMode int resetMode, - @Nullable String prefix) { + @Nullable String namespace) { try { Bundle arg = new Bundle(); arg.putInt(CALL_METHOD_USER_KEY, resolver.getUserId()); arg.putInt(CALL_METHOD_RESET_MODE_KEY, resetMode); - if (prefix != null) { - arg.putString(Settings.CALL_METHOD_PREFIX_KEY, prefix); + if (namespace != null) { + arg.putString(Settings.CALL_METHOD_PREFIX_KEY, createPrefix(namespace)); } IContentProvider cp = sProviderHolder.getProvider(resolver); - cp.call(resolver.getPackageName(), sProviderHolder.mUri.getAuthority(), - CALL_METHOD_RESET_CONFIG, null, arg); + cp.call(resolver.getPackageName(), resolver.getFeatureId(), + sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_CONFIG, null, arg); } catch (RemoteException e) { Log.w(TAG, "Can't reset to defaults for " + DeviceConfig.CONTENT_URI, e); } } + + private static String createCompositeName(@NonNull String namespace, @NonNull String name) { + Preconditions.checkNotNull(namespace); + Preconditions.checkNotNull(name); + return createPrefix(namespace) + name; + } + + private static String createPrefix(@NonNull String namespace) { + Preconditions.checkNotNull(namespace); + return namespace + "/"; + } } /** diff --git a/core/java/android/provider/SettingsStringUtil.java b/core/java/android/provider/SettingsStringUtil.java index a3dc9471a1d0..9e495dd775dc 100644 --- a/core/java/android/provider/SettingsStringUtil.java +++ b/core/java/android/provider/SettingsStringUtil.java @@ -126,7 +126,7 @@ public class SettingsStringUtil { @Override protected String itemToString(ComponentName item) { - return item.flattenToString(); + return item != null ? item.flattenToString() : "null"; } public static String add(String delimitedElements, ComponentName element) { diff --git a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java new file mode 100644 index 000000000000..de90b94d7535 --- /dev/null +++ b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java @@ -0,0 +1,389 @@ +/* + * 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.service.carrier; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.net.Uri; +import android.os.IBinder; +import android.os.RemoteException; + +import com.android.internal.util.Preconditions; + +import java.util.List; + +/** + * Provides basic structure for platform to connect to the carrier messaging service. + * <p> + * <code> + * CarrierMessagingServiceWrapper carrierMessagingServiceWrapper = + * new CarrierMessagingServiceWrapperImpl(); + * if (carrierMessagingServiceWrapper.bindToCarrierMessagingService(context, carrierPackageName)) { + * // wait for onServiceReady callback + * } else { + * // Unable to bind: handle error. + * } + * </code> + * <p> Upon completion {@link #disposeConnection} should be called to unbind the + * CarrierMessagingService. + * @hide + */ +@SystemApi +public abstract class CarrierMessagingServiceWrapper { + // Populated by bindToCarrierMessagingService. bindToCarrierMessagingService must complete + // prior to calling disposeConnection so that mCarrierMessagingServiceConnection is initialized. + private volatile CarrierMessagingServiceConnection mCarrierMessagingServiceConnection; + + private volatile ICarrierMessagingService mICarrierMessagingService; + + /** + * Binds to the carrier messaging service under package {@code carrierPackageName}. This method + * should be called exactly once. + * + * @param context the context + * @param carrierPackageName the carrier package name + * @return true upon successfully binding to a carrier messaging service, false otherwise + * @hide + */ + @SystemApi + public boolean bindToCarrierMessagingService(@NonNull Context context, + @NonNull String carrierPackageName) { + Preconditions.checkState(mCarrierMessagingServiceConnection == null); + + Intent intent = new Intent(CarrierMessagingService.SERVICE_INTERFACE); + intent.setPackage(carrierPackageName); + mCarrierMessagingServiceConnection = new CarrierMessagingServiceConnection(); + return context.bindService(intent, mCarrierMessagingServiceConnection, + Context.BIND_AUTO_CREATE); + } + + /** + * Unbinds the carrier messaging service. This method should be called exactly once. + * + * @param context the context + * @hide + */ + @SystemApi + public void disposeConnection(@NonNull Context context) { + Preconditions.checkNotNull(mCarrierMessagingServiceConnection); + context.unbindService(mCarrierMessagingServiceConnection); + mCarrierMessagingServiceConnection = null; + } + + /** + * Implemented by subclasses to use the carrier messaging service once it is ready. + * @hide + */ + @SystemApi + public abstract void onServiceReady(); + + /** + * Called when connection with service is established. + * + * @param carrierMessagingService the carrier messaing service interface + */ + private void onServiceReady(ICarrierMessagingService carrierMessagingService) { + mICarrierMessagingService = carrierMessagingService; + onServiceReady(); + } + + /** + * Request filtering an incoming SMS message. + * The service will call callback.onFilterComplete with the filtering result. + * + * @param pdu the PDUs of the message + * @param format the format of the PDUs, typically "3gpp" or "3gpp2" + * @param destPort the destination port of a data SMS. It will be -1 for text SMS + * @param subId SMS subscription ID of the SIM + * @param callback the callback to notify upon completion + * @hide + */ + @SystemApi + public void filterSms(@NonNull MessagePdu pdu, @NonNull String format, int destPort, + int subId, @NonNull final CarrierMessagingCallbackWrapper callback) { + if (mICarrierMessagingService != null) { + try { + mICarrierMessagingService.filterSms(pdu, format, destPort, subId, + new CarrierMessagingCallbackWrapperInternal(callback)); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + } + + /** + * Request sending a new text SMS from the device. + * The service will call {@link ICarrierMessagingCallback#onSendSmsComplete} with the send + * status. + * + * @param text the text to send + * @param subId SMS subscription ID of the SIM + * @param destAddress phone number of the recipient of the message + * @param sendSmsFlag flag for sending SMS + * @param callback the callback to notify upon completion + * @hide + */ + @SystemApi + public void sendTextSms(@NonNull String text, int subId, @NonNull String destAddress, + int sendSmsFlag, @NonNull final CarrierMessagingCallbackWrapper callback) { + if (mICarrierMessagingService != null) { + try { + mICarrierMessagingService.sendTextSms(text, subId, destAddress, sendSmsFlag, + new CarrierMessagingCallbackWrapperInternal(callback)); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + } + + /** + * Request sending a new data SMS from the device. + * The service will call {@link ICarrierMessagingCallback#onSendSmsComplete} with the send + * status. + * + * @param data the data to send + * @param subId SMS subscription ID of the SIM + * @param destAddress phone number of the recipient of the message + * @param destPort port number of the recipient of the message + * @param sendSmsFlag flag for sending SMS + * @param callback the callback to notify upon completion + * @hide + */ + @SystemApi + public void sendDataSms(@NonNull byte[] data, int subId, @NonNull String destAddress, + int destPort, int sendSmsFlag, + @NonNull final CarrierMessagingCallbackWrapper callback) { + if (mICarrierMessagingService != null) { + try { + mICarrierMessagingService.sendDataSms(data, subId, destAddress, destPort, + sendSmsFlag, new CarrierMessagingCallbackWrapperInternal(callback)); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + } + + /** + * Request sending a new multi-part text SMS from the device. + * The service will call {@link ICarrierMessagingCallback#onSendMultipartSmsComplete} + * with the send status. + * + * @param parts the parts of the multi-part text SMS to send + * @param subId SMS subscription ID of the SIM + * @param destAddress phone number of the recipient of the message + * @param sendSmsFlag flag for sending SMS + * @param callback the callback to notify upon completion + * @hide + */ + @SystemApi + public void sendMultipartTextSms(@NonNull List<String> parts, int subId, + @NonNull String destAddress, int sendSmsFlag, + @NonNull final CarrierMessagingCallbackWrapper callback) { + if (mICarrierMessagingService != null) { + try { + mICarrierMessagingService.sendMultipartTextSms(parts, subId, destAddress, + sendSmsFlag, new CarrierMessagingCallbackWrapperInternal(callback)); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + } + + /** + * Request sending a new MMS PDU from the device. + * The service will call {@link ICarrierMessagingCallback#onSendMmsComplete} with the send + * status. + * + * @param pduUri the content provider URI of the PDU to send + * @param subId SMS subscription ID of the SIM + * @param location the optional URI to send this MMS PDU. If this is {code null}, + * the PDU should be sent to the default MMSC URL. + * @param callback the callback to notify upon completion + * @hide + */ + @SystemApi + public void sendMms(@NonNull Uri pduUri, int subId, @NonNull Uri location, + @NonNull final CarrierMessagingCallbackWrapper callback) { + if (mICarrierMessagingService != null) { + try { + mICarrierMessagingService.sendMms(pduUri, subId, location, + new CarrierMessagingCallbackWrapperInternal(callback)); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + } + + /** + * Request downloading a new MMS. + * The service will call {@link ICarrierMessagingCallback#onDownloadMmsComplete} with the + * download status. + * + * @param pduUri the content provider URI of the PDU to be downloaded. + * @param subId SMS subscription ID of the SIM + * @param location the URI of the message to be downloaded. + * @param callback the callback to notify upon completion + * @hide + */ + @SystemApi + public void downloadMms(@NonNull Uri pduUri, int subId, @NonNull Uri location, + @NonNull final CarrierMessagingCallbackWrapper callback) { + if (mICarrierMessagingService != null) { + try { + mICarrierMessagingService.downloadMms(pduUri, subId, location, + new CarrierMessagingCallbackWrapperInternal(callback)); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + } + + /** + * A basic {@link ServiceConnection}. + */ + private final class CarrierMessagingServiceConnection implements ServiceConnection { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + onServiceReady(ICarrierMessagingService.Stub.asInterface(service)); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + } + } + + /** + * Callback wrapper used for response to requests exposed by + * {@link CarrierMessagingServiceWrapper}. + * @hide + */ + @SystemApi + public abstract static class CarrierMessagingCallbackWrapper { + + /** + * Response callback for {@link CarrierMessagingServiceWrapper#filterSms}. + * @param result a bitmask integer to indicate how the incoming text SMS should be handled + * by the platform. Bits set can be + * {@link CarrierMessagingService#RECEIVE_OPTIONS_DROP} and + * {@link CarrierMessagingService# + * RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE}. + * {@see CarrierMessagingService#onReceiveTextSms}. + * @hide + */ + @SystemApi + public void onFilterComplete(int result) { + + } + + /** + * Response callback for {@link CarrierMessagingServiceWrapper#sendTextSms} and + * {@link CarrierMessagingServiceWrapper#sendDataSms}. + * @param result send status, one of {@link CarrierMessagingService#SEND_STATUS_OK}, + * {@link CarrierMessagingService#SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, + * and {@link CarrierMessagingService#SEND_STATUS_ERROR}. + * @param messageRef message reference of the just-sent message. This field is applicable + * only if result is {@link CarrierMessagingService#SEND_STATUS_OK}. + * @hide + */ + @SystemApi + public void onSendSmsComplete(int result, int messageRef) { + + } + + /** + * Response callback for {@link CarrierMessagingServiceWrapper#sendMultipartTextSms}. + * @param result send status, one of {@link CarrierMessagingService#SEND_STATUS_OK}, + * {@link CarrierMessagingService#SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, + * and {@link CarrierMessagingService#SEND_STATUS_ERROR}. + * @param messageRefs an array of message references, one for each part of the + * multipart SMS. This field is applicable only if result is + * {@link CarrierMessagingService#SEND_STATUS_OK}. + * @hide + */ + @SystemApi + public void onSendMultipartSmsComplete(int result, @Nullable int[] messageRefs) { + + } + + /** + * Response callback for {@link CarrierMessagingServiceWrapper#sendMms}. + * @param result send status, one of {@link CarrierMessagingService#SEND_STATUS_OK}, + * {@link CarrierMessagingService#SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, + * and {@link CarrierMessagingService#SEND_STATUS_ERROR}. + * @param sendConfPdu a possibly {code null} SendConf PDU, which confirms that the message + * was sent. sendConfPdu is ignored if the {@code result} is not + * {@link CarrierMessagingService#SEND_STATUS_OK}. + * @hide + */ + @SystemApi + public void onSendMmsComplete(int result, @Nullable byte[] sendConfPdu) { + + } + + /** + * Response callback for {@link CarrierMessagingServiceWrapper#downloadMms}. + * @param result download status, one of {@link CarrierMessagingService#SEND_STATUS_OK}, + * {@link CarrierMessagingService#SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, + * and {@link CarrierMessagingService#SEND_STATUS_ERROR}. + * @hide + */ + @SystemApi + public void onDownloadMmsComplete(int result) { + + } + } + + private final class CarrierMessagingCallbackWrapperInternal + extends ICarrierMessagingCallback.Stub { + CarrierMessagingCallbackWrapper mCarrierMessagingCallbackWrapper; + + CarrierMessagingCallbackWrapperInternal(CarrierMessagingCallbackWrapper callback) { + mCarrierMessagingCallbackWrapper = callback; + } + + @Override + public void onFilterComplete(int result) throws RemoteException { + mCarrierMessagingCallbackWrapper.onFilterComplete(result); + } + + @Override + public void onSendSmsComplete(int result, int messageRef) throws RemoteException { + mCarrierMessagingCallbackWrapper.onSendSmsComplete(result, messageRef); + } + + @Override + public void onSendMultipartSmsComplete(int result, int[] messageRefs) + throws RemoteException { + mCarrierMessagingCallbackWrapper.onSendMultipartSmsComplete(result, messageRefs); + } + + @Override + public void onSendMmsComplete(int result, byte[] sendConfPdu) throws RemoteException { + mCarrierMessagingCallbackWrapper.onSendMmsComplete(result, sendConfPdu); + } + + @Override + public void onDownloadMmsComplete(int result) throws RemoteException { + mCarrierMessagingCallbackWrapper.onDownloadMmsComplete(result); + } + } +} diff --git a/core/java/android/service/textclassifier/ITextClassifierService.aidl b/core/java/android/service/textclassifier/ITextClassifierService.aidl index 2f8d67b6ccee..da57506a5530 100644 --- a/core/java/android/service/textclassifier/ITextClassifierService.aidl +++ b/core/java/android/service/textclassifier/ITextClassifierService.aidl @@ -74,4 +74,6 @@ oneway interface ITextClassifierService { in TextClassificationSessionId sessionId, in ConversationActions.Request request, in ITextClassifierCallback callback); + + void onConnectedStateChanged(int connected); } diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java index 2470d197c3fc..4d58ae4ea773 100644 --- a/core/java/android/service/textclassifier/TextClassifierService.java +++ b/core/java/android/service/textclassifier/TextClassifierService.java @@ -17,6 +17,7 @@ package android.service.textclassifier; import android.Manifest; +import android.annotation.IntDef; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; @@ -51,6 +52,8 @@ import android.view.textclassifier.TextSelection; import com.android.internal.util.Preconditions; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -99,6 +102,18 @@ public abstract class TextClassifierService extends Service { "android.service.textclassifier.TextClassifierService"; /** @hide **/ + public static final int CONNECTED = 0; + /** @hide **/ + public static final int DISCONNECTED = 1; + /** @hide */ + @IntDef(value = { + CONNECTED, + DISCONNECTED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ConnectionState{} + + /** @hide **/ private static final String KEY_RESULT = "key_result"; private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper(), null, true); @@ -195,6 +210,12 @@ public abstract class TextClassifierService extends Service { mMainThreadHandler.post( () -> TextClassifierService.this.onDestroyTextClassificationSession(sessionId)); } + + @Override + public void onConnectedStateChanged(@ConnectionState int connected) { + mMainThreadHandler.post(connected == CONNECTED ? TextClassifierService.this::onConnected + : TextClassifierService.this::onDisconnected); + } }; @Nullable @@ -206,6 +227,26 @@ public abstract class TextClassifierService extends Service { return null; } + @Override + public boolean onUnbind(@NonNull Intent intent) { + onDisconnected(); + return super.onUnbind(intent); + } + + /** + * Called when the Android system connects to service. + */ + public void onConnected() { + } + + /** + * Called when the Android system disconnects from the service. + * + * <p> At this point this service may no longer be an active {@link TextClassifierService}. + */ + public void onDisconnected() { + } + /** * Returns suggested text selection start and end indices, recognized entity types, and their * associated confidence scores. The entity types are ordered from highest to lowest scoring. diff --git a/core/java/android/util/StatsEvent.java b/core/java/android/util/StatsEvent.java index 91a5ec0303f7..8d9607ff11e2 100644 --- a/core/java/android/util/StatsEvent.java +++ b/core/java/android/util/StatsEvent.java @@ -20,312 +20,540 @@ import static java.nio.charset.StandardCharsets.UTF_8; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.SystemClock; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; /** * StatsEvent builds and stores the buffer sent over the statsd socket. * This class defines and encapsulates the socket protocol. + * + * <p>Usage:</p> + * <pre> + * StatsEvent statsEvent = StatsEvent.newBuilder() + * .setAtomId(atomId) + * .writeBoolean(false) + * .writeString("annotated String field") + * .addBooleanAnnotation(annotationId, true) + * .build(); + * + * StatsLog.write(statsEvent); + * </pre> * @hide **/ -public final class StatsEvent implements AutoCloseable { - private static final int POS_NUM_ELEMENTS = 1; - private static final int POS_TIMESTAMP = POS_NUM_ELEMENTS + 1; - +public final class StatsEvent { private static final int LOGGER_ENTRY_MAX_PAYLOAD = 4068; - // Max payload size is 4 KB less 4 bytes which are reserved for statsEventTag. + // Max payload size is 4 bytes less as 4 bytes are reserved for statsEventTag. // See android_util_StatsLog.cpp. - private static final int MAX_EVENT_PAYLOAD = LOGGER_ENTRY_MAX_PAYLOAD - 4; - - private static final byte INT_TYPE = 0; - private static final byte LONG_TYPE = 1; - private static final byte STRING_TYPE = 2; - private static final byte LIST_TYPE = 3; - private static final byte FLOAT_TYPE = 4; - - private static final int INT_TYPE_SIZE = 5; - private static final int FLOAT_TYPE_SIZE = 5; - private static final int LONG_TYPE_SIZE = 9; - - private static final int STRING_TYPE_OVERHEAD = 5; - private static final int LIST_TYPE_OVERHEAD = 2; - - public static final int SUCCESS = 0; - public static final int ERROR_BUFFER_LIMIT_EXCEEDED = -1; - public static final int ERROR_NO_TIMESTAMP = -2; - public static final int ERROR_TIMESTAMP_ALREADY_WRITTEN = -3; - public static final int ERROR_NO_ATOM_ID = -4; - public static final int ERROR_ATOM_ID_ALREADY_WRITTEN = -5; - public static final int ERROR_UID_TAG_COUNT_MISMATCH = -6; - - private static Object sLock = new Object(); - - @GuardedBy("sLock") - private static StatsEvent sPool; - - private final byte[] mBuffer = new byte[MAX_EVENT_PAYLOAD]; - private int mPos; - private int mNumElements; - private int mAtomId; - - private StatsEvent() { - // Write LIST_TYPE to buffer - mBuffer[0] = LIST_TYPE; - reset(); - } + private static final int MAX_PAYLOAD_SIZE = LOGGER_ENTRY_MAX_PAYLOAD - 4; - private void reset() { - // Reset state. - mPos = POS_TIMESTAMP; - mNumElements = 0; - mAtomId = 0; + private final Buffer mBuffer; + private final int mNumBytes; + + private StatsEvent(@NonNull final Buffer buffer, final int numBytes) { + mBuffer = buffer; + mNumBytes = numBytes; } /** - * Returns a StatsEvent object from the pool. + * Returns a new StatsEvent.Builder for building StatsEvent object. **/ @NonNull - public static StatsEvent obtain() { - final StatsEvent statsEvent; - synchronized (sLock) { - statsEvent = null == sPool ? new StatsEvent() : sPool; - sPool = null; - } - statsEvent.reset(); - return statsEvent; + public StatsEvent.Builder newBuilder() { + return new StatsEvent.Builder(Buffer.obtain()); } - @Override - public void close() { - synchronized (sLock) { - if (null == sPool) { - sPool = this; - } - } - } - - /** - * Writes the event timestamp to the buffer. - **/ - public int writeTimestampNs(final long timestampNs) { - if (hasTimestamp()) { - return ERROR_TIMESTAMP_ALREADY_WRITTEN; - } - return writeLong(timestampNs); + @NonNull + byte[] getBytes() { + return mBuffer.getBytes(); } - private boolean hasTimestamp() { - return mPos > POS_TIMESTAMP; + int getNumBytes() { + return mNumBytes; } - private boolean hasAtomId() { - return mAtomId != 0; + void release() { + mBuffer.release(); } /** - * Writes the atom id to the buffer. + * Builder for constructing a StatsEvent object. + * + * <p>This class defines and encapsulates the socket encoding for the buffer. + * The write methods must be called in the same order as the order of fields in the + * atom definition.</p> + * + * <p>setAtomId() can be called anytime before build().</p> + * + * <p>Example:</p> + * <pre> + * // Atom definition. + * message MyAtom { + * optional int32 field1 = 1; + * optional int64 field2 = 2; + * optional string field3 = 3 [(annotation1) = true]; + * } + * + * // StatsEvent construction. + * StatsEvent.newBuilder() + * StatsEvent statsEvent = StatsEvent.newBuilder() + * .setAtomId(atomId) + * .writeInt(3) // field1 + * .writeLong(8L) // field2 + * .writeString("foo") // field 3 + * .addBooleanAnnotation(annotation1Id, true) + * .build(); + * </pre> + * @hide **/ - public int writeAtomId(final int atomId) { - if (!hasTimestamp()) { - return ERROR_NO_TIMESTAMP; - } else if (hasAtomId()) { - return ERROR_ATOM_ID_ALREADY_WRITTEN; + public static final class Builder { + // Type Ids. + private static final byte TYPE_INT = 0x00; + private static final byte TYPE_LONG = 0x01; + private static final byte TYPE_STRING = 0x02; + private static final byte TYPE_LIST = 0x03; + private static final byte TYPE_FLOAT = 0x04; + private static final byte TYPE_BOOLEAN = 0x05; + private static final byte TYPE_OBJECT = 0x06; + private static final byte TYPE_BYTE_ARRAY = 0x07; + private static final byte TYPE_ATTRIBUTION_CHAIN = 0x08; + private static final byte TYPE_ERRORS = 0x0F; + + // Error flags. + private static final int ERROR_NO_TIMESTAMP = 0x1; + private static final int ERROR_NO_ATOM_ID = 0x2; + private static final int ERROR_OVERFLOW = 0x4; + private static final int ERROR_ATTRIBUTION_CHAIN_TOO_LONG = 0x8; + private static final int ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD = 0x10; + private static final int ERROR_INVALID_ANNOTATION_ID = 0x20; + private static final int ERROR_ANNOTATION_ID_TOO_LARGE = 0x40; + private static final int ERROR_TOO_MANY_ANNOTATIONS = 0x80; + private static final int ERROR_TOO_MANY_FIELDS = 0x100; + private static final int ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL = 0x200; + + // Size limits. + private static final int MAX_ANNOTATION_COUNT = 15; + private static final int MAX_ATTRIBUTION_NODES = 127; + private static final int MAX_NUM_ELEMENTS = 127; + + // Fixed positions. + private static final int POS_NUM_ELEMENTS = 1; + private static final int POS_TIMESTAMP_NS = POS_NUM_ELEMENTS + Byte.BYTES; + private static final int POS_ATOM_ID = POS_TIMESTAMP_NS + Byte.BYTES + Long.BYTES; + + private final Buffer mBuffer; + private long mTimestampNs; + private int mAtomId; + private byte mCurrentAnnotationCount; + private int mPos; + private int mPosLastField; + private byte mLastType; + private int mNumElements; + private int mErrorMask; + + private Builder(final Buffer buffer) { + mBuffer = buffer; + mCurrentAnnotationCount = 0; + mAtomId = 0; + mTimestampNs = SystemClock.elapsedRealtimeNanos(); + mNumElements = 0; + + // Set mPos to 0 for writing TYPE_OBJECT at 0th position. + mPos = 0; + writeTypeId(TYPE_OBJECT); + + // Set mPos to after atom id's location in the buffer. + // First 2 elements in the buffer are event timestamp followed by the atom id. + mPos = POS_ATOM_ID + Byte.BYTES + Integer.BYTES; + mPosLastField = 0; + mLastType = 0; } - final int writeResult = writeInt(atomId); - if (SUCCESS == writeResult) { + /** + * Sets the atom id for this StatsEvent. + **/ + @NonNull + public Builder setAtomId(final int atomId) { mAtomId = atomId; + return this; } - return writeResult; - } - /** - * Appends the given int to the StatsEvent buffer. - **/ - public int writeInt(final int value) { - if (!hasTimestamp()) { - return ERROR_NO_TIMESTAMP; - } else if (!hasAtomId()) { - return ERROR_NO_ATOM_ID; - } else if (mPos + INT_TYPE_SIZE > MAX_EVENT_PAYLOAD) { - return ERROR_BUFFER_LIMIT_EXCEEDED; + /** + * Sets the timestamp in nanos for this StatsEvent. + **/ + @VisibleForTesting + @NonNull + public Builder setTimestampNs(final long timestampNs) { + mTimestampNs = timestampNs; + return this; } - mBuffer[mPos] = INT_TYPE; - copyInt(mBuffer, mPos + 1, value); - mPos += INT_TYPE_SIZE; - mNumElements++; - return SUCCESS; - } + /** + * Write a boolean field to this StatsEvent. + **/ + @NonNull + public Builder writeBoolean(final boolean value) { + // Write boolean typeId byte followed by boolean byte representation. + writeTypeId(TYPE_BOOLEAN); + mPos += mBuffer.putBoolean(mPos, value); + mNumElements++; + return this; + } - /** - * Appends the given long to the StatsEvent buffer. - **/ - public int writeLong(final long value) { - if (!hasTimestamp()) { - return ERROR_NO_TIMESTAMP; - } else if (!hasAtomId()) { - return ERROR_NO_ATOM_ID; - } else if (mPos + LONG_TYPE_SIZE > MAX_EVENT_PAYLOAD) { - return ERROR_BUFFER_LIMIT_EXCEEDED; + /** + * Write an integer field to this StatsEvent. + **/ + @NonNull + public Builder writeInt(final int value) { + // Write integer typeId byte followed by 4-byte representation of value. + writeTypeId(TYPE_INT); + mPos += mBuffer.putInt(mPos, value); + mNumElements++; + return this; } - mBuffer[mPos] = LONG_TYPE; - copyLong(mBuffer, mPos + 1, value); - mPos += LONG_TYPE_SIZE; - mNumElements++; - return SUCCESS; - } + /** + * Write a long field to this StatsEvent. + **/ + @NonNull + public Builder writeLong(final long value) { + // Write long typeId byte followed by 8-byte representation of value. + writeTypeId(TYPE_LONG); + mPos += mBuffer.putLong(mPos, value); + mNumElements++; + return this; + } - /** - * Appends the given float to the StatsEvent buffer. - **/ - public int writeFloat(final float value) { - if (!hasTimestamp()) { - return ERROR_NO_TIMESTAMP; - } else if (!hasAtomId()) { - return ERROR_NO_ATOM_ID; - } else if (mPos + FLOAT_TYPE_SIZE > MAX_EVENT_PAYLOAD) { - return ERROR_BUFFER_LIMIT_EXCEEDED; + /** + * Write a float field to this StatsEvent. + **/ + @NonNull + public Builder writeFloat(final float value) { + // Write float typeId byte followed by 4-byte representation of value. + writeTypeId(TYPE_FLOAT); + mPos += mBuffer.putFloat(mPos, value); + mNumElements++; + return this; } - mBuffer[mPos] = FLOAT_TYPE; - copyInt(mBuffer, mPos + 1, Float.floatToIntBits(value)); - mPos += FLOAT_TYPE_SIZE; - mNumElements++; - return SUCCESS; - } + /** + * Write a String field to this StatsEvent. + **/ + @NonNull + public Builder writeString(@NonNull final String value) { + // Write String typeId byte, followed by 4-byte representation of number of bytes + // in the UTF-8 encoding, followed by the actual UTF-8 byte encoding of value. + final byte[] valueBytes = stringToBytes(value); + writeByteArray(valueBytes, TYPE_STRING); + return this; + } - /** - * Appends the given boolean to the StatsEvent buffer. - **/ - public int writeBoolean(final boolean value) { - return writeInt(value ? 1 : 0); - } + /** + * Write a byte array field to this StatsEvent. + **/ + @NonNull + public Builder writeByteArray(@NonNull final byte[] value) { + // Write byte array typeId byte, followed by 4-byte representation of number of bytes + // in value, followed by the actual byte array. + writeByteArray(value, TYPE_BYTE_ARRAY); + return this; + } - /** - * Appends the given byte array to the StatsEvent buffer. - **/ - public int writeByteArray(@NonNull final byte[] value) { - if (!hasTimestamp()) { - return ERROR_NO_TIMESTAMP; - } else if (!hasAtomId()) { - return ERROR_NO_ATOM_ID; - } else if (mPos + STRING_TYPE_OVERHEAD + value.length > MAX_EVENT_PAYLOAD) { - return ERROR_BUFFER_LIMIT_EXCEEDED; + private void writeByteArray(@NonNull final byte[] value, final byte typeId) { + writeTypeId(typeId); + final int numBytes = value.length; + mPos += mBuffer.putInt(mPos, numBytes); + mPos += mBuffer.putByteArray(mPos, value); + mNumElements++; } - mBuffer[mPos] = STRING_TYPE; - copyInt(mBuffer, mPos + 1, value.length); - System.arraycopy(value, 0, mBuffer, mPos + STRING_TYPE_OVERHEAD, value.length); - mPos += STRING_TYPE_OVERHEAD + value.length; - mNumElements++; - return SUCCESS; - } + /** + * Write an attribution chain field to this StatsEvent. + * + * The sizes of uids and tags must be equal. The AttributionNode at position i is + * made up of uids[i] and tags[i]. + * + * @param uids array of uids in the attribution nodes. + * @param tags array of tags in the attribution nodes. + **/ + @NonNull + public Builder writeAttributionNode( + @NonNull final int[] uids, @NonNull final String[] tags) { + final byte numUids = (byte) uids.length; + final byte numTags = (byte) tags.length; + + if (numUids != numTags) { + mErrorMask |= ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL; + } else if (numUids > MAX_ATTRIBUTION_NODES) { + mErrorMask |= ERROR_ATTRIBUTION_CHAIN_TOO_LONG; + } else { + // Write attribution chain typeId byte, followed by 1-byte representation of + // number of attribution nodes, followed by encoding of each attribution node. + writeTypeId(TYPE_ATTRIBUTION_CHAIN); + mPos += mBuffer.putByte(mPos, numUids); + for (int i = 0; i < numUids; i++) { + // Each uid is encoded as 4-byte representation of its int value. + mPos += mBuffer.putInt(mPos, uids[i]); + + // Each tag is encoded as 4-byte representation of number of bytes in its + // UTF-8 encoding, followed by the actual UTF-8 bytes. + final byte[] tagBytes = stringToBytes(tags[i]); + mPos += mBuffer.putInt(mPos, tagBytes.length); + mPos += mBuffer.putByteArray(mPos, tagBytes); + } + mNumElements++; + } + return this; + } - /** - * Appends the given String to the StatsEvent buffer. - **/ - public int writeString(@NonNull final String value) { - final byte[] valueBytes = stringToBytes(value); - return writeByteArray(valueBytes); + /** + * Write a boolean annotation for the last field written. + **/ + @NonNull + public Builder addBooleanAnnotation( + final byte annotationId, final boolean value) { + // Ensure there's a field written to annotate. + if (0 == mPosLastField) { + mErrorMask |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD; + } else if (mCurrentAnnotationCount >= MAX_ANNOTATION_COUNT) { + mErrorMask |= ERROR_TOO_MANY_ANNOTATIONS; + } else { + mPos += mBuffer.putByte(mPos, annotationId); + mPos += mBuffer.putByte(mPos, TYPE_BOOLEAN); + mPos += mBuffer.putBoolean(mPos, value); + mCurrentAnnotationCount++; + writeAnnotationCount(); + } + return this; + } + + /** + * Write an integer annotation for the last field written. + **/ + @NonNull + public Builder addIntAnnotation(final byte annotationId, final int value) { + if (0 == mPosLastField) { + mErrorMask |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD; + } else if (mCurrentAnnotationCount >= MAX_ANNOTATION_COUNT) { + mErrorMask |= ERROR_TOO_MANY_ANNOTATIONS; + } else { + mPos += mBuffer.putByte(mPos, annotationId); + mPos += mBuffer.putByte(mPos, TYPE_INT); + mPos += mBuffer.putInt(mPos, value); + mCurrentAnnotationCount++; + writeAnnotationCount(); + } + return this; + } + + /** + * Builds a StatsEvent object with values entered in this Builder. + **/ + @NonNull + public StatsEvent build() { + if (0L == mTimestampNs) { + mErrorMask |= ERROR_NO_TIMESTAMP; + } + if (0 == mAtomId) { + mErrorMask |= ERROR_NO_ATOM_ID; + } + if (mBuffer.hasOverflowed()) { + mErrorMask |= ERROR_OVERFLOW; + } + if (mNumElements > MAX_NUM_ELEMENTS) { + mErrorMask |= ERROR_TOO_MANY_FIELDS; + } + + int size = mPos; + mPos = POS_TIMESTAMP_NS; + writeLong(mTimestampNs); + writeInt(mAtomId); + if (0 == mErrorMask) { + mBuffer.putByte(POS_NUM_ELEMENTS, (byte) mNumElements); + } else { + mBuffer.putByte(0, TYPE_ERRORS); + mBuffer.putByte(POS_NUM_ELEMENTS, (byte) 3); + mPos += mBuffer.putInt(mPos, mErrorMask); + size = mPos; + } + + return new StatsEvent(mBuffer, size); + } + + private void writeTypeId(final byte typeId) { + mPosLastField = mPos; + mLastType = typeId; + mCurrentAnnotationCount = 0; + final byte encodedId = (byte) (typeId & 0x0F); + mPos += mBuffer.putByte(mPos, encodedId); + } + + private void writeAnnotationCount() { + // Use first 4 bits for annotation count and last 4 bits for typeId. + final byte encodedId = (byte) ((mCurrentAnnotationCount << 4) | (mLastType & 0x0F)); + mBuffer.putByte(mPosLastField, encodedId); + } + + @NonNull + private static byte[] stringToBytes(@Nullable final String value) { + return (null == value ? "" : value).getBytes(UTF_8); + } } - /** - * Appends the AttributionNode specified as array of uids and array of tags. - **/ - public int writeAttributionNode(@NonNull final int[] uids, @NonNull final String[] tags) { - if (!hasTimestamp()) { - return ERROR_NO_TIMESTAMP; - } else if (!hasAtomId()) { - return ERROR_NO_ATOM_ID; - } else if (mPos + LIST_TYPE_OVERHEAD > MAX_EVENT_PAYLOAD) { - return ERROR_BUFFER_LIMIT_EXCEEDED; + private static final class Buffer { + private static Object sLock = new Object(); + + @GuardedBy("sLock") + private static Buffer sPool; + + private final byte[] mBytes = new byte[MAX_PAYLOAD_SIZE]; + private boolean mOverflow = false; + + @NonNull + private static Buffer obtain() { + final Buffer buffer; + synchronized (sLock) { + buffer = null == sPool ? new Buffer() : sPool; + sPool = null; + } + buffer.reset(); + return buffer; } - final int numTags = tags.length; - final int numUids = uids.length; - if (numTags != numUids) { - return ERROR_UID_TAG_COUNT_MISMATCH; + private Buffer() { } - int pos = mPos; - mBuffer[pos] = LIST_TYPE; - mBuffer[pos + 1] = (byte) numTags; - pos += LIST_TYPE_OVERHEAD; - for (int i = 0; i < numTags; i++) { - final byte[] tagBytes = stringToBytes(tags[i]); + @NonNull + private byte[] getBytes() { + return mBytes; + } - if (pos + LIST_TYPE_OVERHEAD + INT_TYPE_SIZE - + STRING_TYPE_OVERHEAD + tagBytes.length > MAX_EVENT_PAYLOAD) { - return ERROR_BUFFER_LIMIT_EXCEEDED; + private void release() { + synchronized (sLock) { + if (null == sPool) { + sPool = this; + } } + } - mBuffer[pos] = LIST_TYPE; - mBuffer[pos + 1] = 2; - pos += LIST_TYPE_OVERHEAD; - mBuffer[pos] = INT_TYPE; - copyInt(mBuffer, pos + 1, uids[i]); - pos += INT_TYPE_SIZE; - mBuffer[pos] = STRING_TYPE; - copyInt(mBuffer, pos + 1, tagBytes.length); - System.arraycopy(tagBytes, 0, mBuffer, pos + STRING_TYPE_OVERHEAD, tagBytes.length); - pos += STRING_TYPE_OVERHEAD + tagBytes.length; + private void reset() { + mOverflow = false; } - mPos = pos; - mNumElements++; - return SUCCESS; - } - /** - * Returns the byte array containing data in the statsd socket format. - * @hide - **/ - @NonNull - public byte[] getBuffer() { - // Encode number of elements in the buffer. - mBuffer[POS_NUM_ELEMENTS] = (byte) mNumElements; - return mBuffer; - } + private boolean hasOverflowed() { + return mOverflow; + } - /** - * Returns number of bytes used by the buffer. - * @hide - **/ - public int size() { - return mPos; - } + /** + * Checks for available space in the byte array. + * + * @param index starting position in the buffer to start the check. + * @param numBytes number of bytes to check from index. + * @return true if space is available, false otherwise. + **/ + private boolean hasEnoughSpace(final int index, final int numBytes) { + final boolean result = index + numBytes < MAX_PAYLOAD_SIZE; + if (!result) { + mOverflow = true; + } + return result; + } - /** - * Getter for atom id. - * @hide - **/ - public int getAtomId() { - return mAtomId; - } + /** + * Writes a byte into the buffer. + * + * @param index position in the buffer where the byte is written. + * @param value the byte to write. + * @return number of bytes written to buffer from this write operation. + **/ + private int putByte(final int index, final byte value) { + if (hasEnoughSpace(index, Byte.BYTES)) { + mBytes[index] = (byte) (value); + return Byte.BYTES; + } + return 0; + } - @NonNull - private static byte[] stringToBytes(@Nullable final String value) { - return (null == value ? "" : value).getBytes(UTF_8); - } + /** + * Writes a boolean into the buffer. + * + * @param index position in the buffer where the boolean is written. + * @param value the boolean to write. + * @return number of bytes written to buffer from this write operation. + **/ + private int putBoolean(final int index, final boolean value) { + return putByte(index, (byte) (value ? 1 : 0)); + } - // Helper methods for copying primitives - private static void copyInt(@NonNull byte[] buff, int pos, int value) { - buff[pos] = (byte) (value); - buff[pos + 1] = (byte) (value >> 8); - buff[pos + 2] = (byte) (value >> 16); - buff[pos + 3] = (byte) (value >> 24); - } + /** + * Writes an integer into the buffer. + * + * @param index position in the buffer where the integer is written. + * @param value the integer to write. + * @return number of bytes written to buffer from this write operation. + **/ + private int putInt(final int index, final int value) { + if (hasEnoughSpace(index, Integer.BYTES)) { + // Use little endian byte order. + mBytes[index] = (byte) (value); + mBytes[index + 1] = (byte) (value >> 8); + mBytes[index + 2] = (byte) (value >> 16); + mBytes[index + 3] = (byte) (value >> 24); + return Integer.BYTES; + } + return 0; + } + + /** + * Writes a long into the buffer. + * + * @param index position in the buffer where the long is written. + * @param value the long to write. + * @return number of bytes written to buffer from this write operation. + **/ + private int putLong(final int index, final long value) { + if (hasEnoughSpace(index, Long.BYTES)) { + // Use little endian byte order. + mBytes[index] = (byte) (value); + mBytes[index + 1] = (byte) (value >> 8); + mBytes[index + 2] = (byte) (value >> 16); + mBytes[index + 3] = (byte) (value >> 24); + mBytes[index + 4] = (byte) (value >> 32); + mBytes[index + 5] = (byte) (value >> 40); + mBytes[index + 6] = (byte) (value >> 48); + mBytes[index + 7] = (byte) (value >> 56); + return Long.BYTES; + } + return 0; + } + + /** + * Writes a float into the buffer. + * + * @param index position in the buffer where the float is written. + * @param value the float to write. + * @return number of bytes written to buffer from this write operation. + **/ + private int putFloat(final int index, final float value) { + return putInt(index, Float.floatToIntBits(value)); + } - private static void copyLong(@NonNull byte[] buff, int pos, long value) { - buff[pos] = (byte) (value); - buff[pos + 1] = (byte) (value >> 8); - buff[pos + 2] = (byte) (value >> 16); - buff[pos + 3] = (byte) (value >> 24); - buff[pos + 4] = (byte) (value >> 32); - buff[pos + 5] = (byte) (value >> 40); - buff[pos + 6] = (byte) (value >> 48); - buff[pos + 7] = (byte) (value >> 56); + /** + * Copies a byte array into the buffer. + * + * @param index position in the buffer where the byte array is copied. + * @param value the byte array to copy. + * @return number of bytes written to buffer from this write operation. + **/ + private int putByteArray(final int index, @NonNull final byte[] value) { + final int numBytes = value.length; + if (hasEnoughSpace(index, numBytes)) { + System.arraycopy(value, 0, mBytes, index, numBytes); + return numBytes; + } + return 0; + } } } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 2f0a4ebb84f8..59e9ed1512ee 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -712,6 +712,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mSurfaceAlpha = 1f; synchronized (mSurfaceControlLock) { + mSurface.release(); + if (mRtHandlingPositionUpdates) { mRtReleaseSurfaces = true; return; @@ -725,7 +727,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mTmpTransaction.remove(mBackgroundControl); mBackgroundControl = null; } - mSurface.release(); mTmpTransaction.apply(); } } @@ -1198,7 +1199,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mRtTransaction.remove(mBackgroundControl); mSurfaceControl = null; mBackgroundControl = null; - mSurface.release(); } mRtHandlingPositionUpdates = false; } diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index ad59ae5d2bee..36daa5c57700 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -945,6 +945,94 @@ public class ViewDebug { return null; } + private static class StreamingPictureCallbackHandler implements AutoCloseable, + HardwareRenderer.PictureCapturedCallback, Runnable { + private final HardwareRenderer mRenderer; + private final Callable<OutputStream> mCallback; + private final Executor mExecutor; + private final ReentrantLock mLock = new ReentrantLock(false); + private final ArrayDeque<byte[]> mQueue = new ArrayDeque<>(3); + private final ByteArrayOutputStream mByteStream = new ByteArrayOutputStream(); + private boolean mStopListening; + private Thread mRenderThread; + + private StreamingPictureCallbackHandler(HardwareRenderer renderer, + Callable<OutputStream> callback, Executor executor) { + mRenderer = renderer; + mCallback = callback; + mExecutor = executor; + mRenderer.setPictureCaptureCallback(this); + } + + @Override + public void close() { + mLock.lock(); + mStopListening = true; + mLock.unlock(); + mRenderer.setPictureCaptureCallback(null); + } + + @Override + public void onPictureCaptured(Picture picture) { + mLock.lock(); + if (mStopListening) { + mLock.unlock(); + mRenderer.setPictureCaptureCallback(null); + return; + } + if (mRenderThread == null) { + mRenderThread = Thread.currentThread(); + } + boolean needsInvoke = true; + if (mQueue.size() == 3) { + mQueue.removeLast(); + needsInvoke = false; + } + picture.writeToStream(mByteStream); + mQueue.add(mByteStream.toByteArray()); + mByteStream.reset(); + mLock.unlock(); + + if (needsInvoke) { + mExecutor.execute(this); + } + } + + @Override + public void run() { + mLock.lock(); + final byte[] picture = mQueue.poll(); + final boolean isStopped = mStopListening; + mLock.unlock(); + if (Thread.currentThread() == mRenderThread) { + close(); + throw new IllegalStateException( + "ViewDebug#startRenderingCommandsCapture must be given an executor that " + + "invokes asynchronously"); + } + if (isStopped) { + return; + } + OutputStream stream = null; + try { + stream = mCallback.call(); + } catch (Exception ex) { + Log.w("ViewDebug", "Aborting rendering commands capture " + + "because callback threw exception", ex); + } + if (stream != null) { + try { + stream.write(picture); + } catch (IOException ex) { + Log.w("ViewDebug", "Aborting rendering commands capture " + + "due to IOException writing to output stream", ex); + } + } else { + close(); + } + } + } + /** * Begins capturing the entire rendering commands for the view tree referenced by the given * view. The view passed may be any View in the tree as long as it is attached. That is, @@ -990,18 +1078,7 @@ public class ViewDebug { } final HardwareRenderer renderer = attachInfo.mThreadedRenderer; if (renderer != null) { - return new PictureCallbackHandler(renderer, (picture -> { - try { - OutputStream stream = callback.call(); - if (stream != null) { - picture.writeToStream(stream); - return true; - } - } catch (Exception ex) { - // fall through - } - return false; - }), executor); + return new StreamingPictureCallbackHandler(renderer, callback, executor); } return null; } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 20dc23492ae5..85bf19f855bb 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -439,7 +439,6 @@ public final class ViewRootImpl implements ViewParent, boolean mReportNextDraw; boolean mFullRedrawNeeded; boolean mNewSurfaceNeeded; - boolean mHasHadWindowFocus; boolean mLastWasImTarget; boolean mForceNextWindowRelayout; CountDownLatch mWindowDrawCountDown; @@ -2123,11 +2122,6 @@ public final class ViewRootImpl implements ViewParent, endDragResizing(); destroyHardwareResources(); } - if (viewVisibility == View.GONE) { - // After making a window gone, we will count it as being - // shown for the first time the next time it gets focus. - mHasHadWindowFocus = false; - } } // Non-visible windows can't hold accessibility focus. @@ -2823,8 +2817,7 @@ public final class ViewRootImpl implements ViewParent, if (imm != null && imTarget) { imm.onPreWindowFocus(mView, hasWindowFocus); imm.onPostWindowFocus(mView, mView.findFocus(), - mWindowAttributes.softInputMode, - !mHasHadWindowFocus, mWindowAttributes.flags); + mWindowAttributes.softInputMode, mWindowAttributes.flags); } } } @@ -3017,8 +3010,7 @@ public final class ViewRootImpl implements ViewParent, if (hasWindowFocus) { if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) { imm.onPostWindowFocus(mView, mView.findFocus(), - mWindowAttributes.softInputMode, - !mHasHadWindowFocus, mWindowAttributes.flags); + mWindowAttributes.softInputMode, mWindowAttributes.flags); } // Clear the forward bit. We can just do this directly, since // the window manager doesn't care about it. @@ -3028,7 +3020,6 @@ public final class ViewRootImpl implements ViewParent, .softInputMode &= ~WindowManager.LayoutParams .SOFT_INPUT_IS_FORWARD_NAVIGATION; - mHasHadWindowFocus = true; // Refocusing a window that has a focused view should fire a // focus event for the view since the global focused view changed. diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 6420d71216cd..7ee53f26b1f8 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -358,7 +358,7 @@ public final class InputMethodManager { boolean mActive = false; /** - * {@code true} if next {@link #onPostWindowFocus(View, View, int, boolean, int)} needs to + * {@code true} if next {@link #onPostWindowFocus(View, View, int, int)} needs to * restart input. */ boolean mRestartOnNextWindowFocus = true; @@ -1925,13 +1925,12 @@ public final class InputMethodManager { * @hide */ public void onPostWindowFocus(View rootView, View focusedView, - @SoftInputModeFlags int softInputMode, boolean first, int windowFlags) { + @SoftInputModeFlags int softInputMode, int windowFlags) { boolean forceNewFocus = false; synchronized (mH) { if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode) - + " first=" + first + " flags=#" - + Integer.toHexString(windowFlags)); + + " flags=#" + Integer.toHexString(windowFlags)); if (mRestartOnNextWindowFocus) { if (DEBUG) Log.v(TAG, "Restarting due to mRestartOnNextWindowFocus"); mRestartOnNextWindowFocus = false; @@ -1947,9 +1946,6 @@ public final class InputMethodManager { startInputFlags |= StartInputFlags.IS_TEXT_EDITOR; } } - if (first) { - startInputFlags |= StartInputFlags.FIRST_WINDOW_FOCUS_GAIN; - } if (checkFocusNoStartInput(forceNewFocus)) { // We need to restart input on the current focus view. This diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java index 23d12374453f..3824c22a40a7 100644 --- a/core/java/android/webkit/CookieManager.java +++ b/core/java/android/webkit/CookieManager.java @@ -22,7 +22,10 @@ import android.net.WebAddress; /** * Manages the cookies used by an application's {@link WebView} instances. - * Cookies are manipulated according to RFC2109. + * <p> + * CookieManager represents cookies as strings in the same format as the + * HTTP {@code Cookie} and {@code Set-Cookie} header fields (defined in + * <a href="https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03">RFC6265bis</a>). */ public abstract class CookieManager { /** diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index 4db630808ef1..f8522edb7dcd 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -71,11 +71,24 @@ public class WebChromeClient { } /** - * Notify the host application that the current page has entered full - * screen mode. The host application must show the custom View which - * contains the web contents — video or other HTML content — - * in full screen mode. Also see "Full screen support" documentation on - * {@link WebView}. + * Notify the host application that the current page has entered full screen mode. After this + * call, web content will no longer be rendered in the WebView, but will instead be rendered + * in {@code view}. The host application should add this View to a Window which is configured + * with {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN} flag in order to + * actually display this web content full screen. + * + * <p>The application may explicitly exit fullscreen mode by invoking {@code callback} (ex. when + * the user presses the back button). However, this is generally not necessary as the web page + * will often show its own UI to close out of fullscreen. Regardless of how the WebView exits + * fullscreen mode, WebView will invoke {@link #onHideCustomView()}, signaling for the + * application to remove the custom View. + * + * <p>If this method is not overridden, WebView will report to the web page it does not support + * fullscreen mode and will not honor the web page's request to run in fullscreen mode. + * + * <p class="note"><b>Note:</b> if overriding this method, the application must also override + * {@link #onHideCustomView()}. + * * @param view is the View object to be shown. * @param callback invoke this callback to request the page to exit * full screen mode. @@ -98,10 +111,13 @@ public class WebChromeClient { CustomViewCallback callback) {}; /** - * Notify the host application that the current page has exited full - * screen mode. The host application must hide the custom View, ie. the - * View passed to {@link #onShowCustomView} when the content entered fullscreen. - * Also see "Full screen support" documentation on {@link WebView}. + * Notify the host application that the current page has exited full screen mode. The host + * application must hide the custom View (the View which was previously passed to {@link + * #onShowCustomView(View, CustomViewCallback) onShowCustomView()}). After this call, web + * content will render in the original WebView again. + * + * <p class="note"><b>Note:</b> if overriding this method, the application must also override + * {@link #onShowCustomView(View, CustomViewCallback) onShowCustomView()}. */ public void onHideCustomView() {} diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 57ce28e5059a..27402a463d4f 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -1189,6 +1189,9 @@ public class Editor { } public boolean performLongClick(boolean handled) { + if (TextView.DEBUG_CURSOR) { + logCursor("performLongClick", "handled=%s", handled); + } // Long press in empty space moves cursor and starts the insertion action mode. if (!handled && !isPositionOnText(mLastDownPositionX, mLastDownPositionY) && mInsertionControllerEnabled) { @@ -1252,6 +1255,10 @@ public class Editor { } void onFocusChanged(boolean focused, int direction) { + if (TextView.DEBUG_CURSOR) { + logCursor("onFocusChanged", "focused=%s", focused); + } + mShowCursor = SystemClock.uptimeMillis(); ensureEndedBatchEdit(); @@ -1450,12 +1457,22 @@ public class Editor { } else { mTapState = TAP_STATE_TRIPLE_CLICK; } + if (TextView.DEBUG_CURSOR) { + logCursor("updateTapState", "ACTION_DOWN: %s tap detected", + (mTapState == TAP_STATE_DOUBLE_TAP ? "double" : "triple")); + } } else { mTapState = TAP_STATE_FIRST_TAP; + if (TextView.DEBUG_CURSOR) { + logCursor("updateTapState", "ACTION_DOWN: first tap detected"); + } } } if (action == MotionEvent.ACTION_UP) { mLastTouchUpTime = SystemClock.uptimeMillis(); + if (TextView.DEBUG_CURSOR) { + logCursor("updateTapState", "ACTION_UP"); + } } } @@ -2354,6 +2371,9 @@ public class Editor { } void onTouchUpEvent(MotionEvent event) { + if (TextView.DEBUG_CURSOR) { + logCursor("onTouchUpEvent", null); + } if (getSelectionActionModeHelper().resetSelection( getTextView().getOffsetForPosition(event.getX(), event.getY()))) { return; @@ -2481,6 +2501,9 @@ public class Editor { loadCursorDrawable(); final int left = clampHorizontalPosition(mDrawableForCursor, horizontal); final int width = mDrawableForCursor.getIntrinsicWidth(); + if (TextView.DEBUG_CURSOR) { + logCursor("updateCursorPosition", "left=%s, top=%s", left, (top - mTempRect.top)); + } mDrawableForCursor.setBounds(left, top - mTempRect.top, left + width, bottom + mTempRect.bottom); } @@ -4621,6 +4644,11 @@ public class Editor { } public void show() { + if (TextView.DEBUG_CURSOR) { + logCursor(getClass().getSimpleName() + ": HandleView: show()", "offset=%s", + getCurrentCursorOffset()); + } + if (isShowing()) return; getPositionListener().addSubscriber(this, true /* local position may change */); @@ -4637,6 +4665,11 @@ public class Editor { } public void hide() { + if (TextView.DEBUG_CURSOR) { + logCursor(getClass().getSimpleName() + ": HandleView: hide()", "offset=%s", + getCurrentCursorOffset()); + } + dismiss(); getPositionListener().removeSubscriber(this); @@ -5033,6 +5066,11 @@ public class Editor { @Override public boolean onTouchEvent(MotionEvent ev) { + if (TextView.DEBUG_CURSOR) { + logCursor(this.getClass().getSimpleName() + ": HandleView: onTouchEvent", + MotionEvent.actionToString(ev.getActionMasked())); + } + updateFloatingToolbarVisibility(ev); switch (ev.getActionMasked()) { @@ -5951,6 +5989,10 @@ public class Editor { distanceSquared < doubleTapSlop * doubleTapSlop; if (stayedInArea && (isMouse || isPositionOnText(eventX, eventY))) { + if (TextView.DEBUG_CURSOR) { + logCursor("SelectionModifierCursorController: onTouchEvent", + "ACTION_DOWN: select and start drag"); + } if (mTapState == TAP_STATE_DOUBLE_TAP) { selectCurrentWordAndStartDrag(); } else if (mTapState == TAP_STATE_TRIPLE_CLICK) { @@ -6028,6 +6070,9 @@ public class Editor { break; case MotionEvent.ACTION_UP: + if (TextView.DEBUG_CURSOR) { + logCursor("SelectionModifierCursorController: onTouchEvent", "ACTION_UP"); + } if (!isDragAcceleratorActive()) { break; } @@ -7119,4 +7164,12 @@ public class Editor { return resolveInfo.loadLabel(mPackageManager); } } + + private static void logCursor(String location, @Nullable String msgFormat, Object ... msgArgs) { + if (msgFormat == null) { + Log.d(TAG, location); + } else { + Log.d(TAG, location + ": " + String.format(msgFormat, msgArgs)); + } + } } diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 2e9574345692..d58d858c3b8a 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -163,7 +163,6 @@ import java.util.ArrayList; public class ProgressBar extends View { private static final int MAX_LEVEL = 10000; - private static final int TIMEOUT_SEND_ACCESSIBILITY_EVENT = 200; /** Interpolator used for smooth progress animations. */ private static final DecelerateInterpolator PROGRESS_ANIM_INTERPOLATOR = @@ -244,8 +243,6 @@ public class ProgressBar extends View { private final ArrayList<RefreshData> mRefreshData = new ArrayList<RefreshData>(); - private AccessibilityEventSender mAccessibilityEventSender; - /** * Create a new progress bar with range 0...100 and initial progress of 0. * @param context the application environment @@ -1556,7 +1553,7 @@ public class ProgressBar extends View { void onProgressRefresh(float scale, boolean fromUser, int progress) { if (AccessibilityManager.getInstance(mContext).isEnabled()) { - scheduleAccessibilityEventSender(); + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); } } @@ -2250,9 +2247,6 @@ public class ProgressBar extends View { removeCallbacks(mRefreshProgressRunnable); mRefreshIsPosted = false; } - if (mAccessibilityEventSender != null) { - removeCallbacks(mAccessibilityEventSender); - } // This should come after stopAnimation(), otherwise an invalidate message remains in the // queue, which can prevent the entire view hierarchy from being GC'ed during a rotation super.onDetachedFromWindow(); @@ -2285,22 +2279,6 @@ public class ProgressBar extends View { } } - /** - * Schedule a command for sending an accessibility event. - * </br> - * Note: A command is used to ensure that accessibility events - * are sent at most one in a given time frame to save - * system resources while the progress changes quickly. - */ - private void scheduleAccessibilityEventSender() { - if (mAccessibilityEventSender == null) { - mAccessibilityEventSender = new AccessibilityEventSender(); - } else { - removeCallbacks(mAccessibilityEventSender); - } - postDelayed(mAccessibilityEventSender, TIMEOUT_SEND_ACCESSIBILITY_EVENT); - } - /** @hide */ @Override protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { @@ -2324,15 +2302,6 @@ public class ProgressBar extends View { return isIndeterminate() && getWindowVisibility() == VISIBLE && isShown(); } - /** - * Command for sending an accessibility event. - */ - private class AccessibilityEventSender implements Runnable { - public void run() { - sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); - } - } - private static class ProgressTintInfo { ColorStateList mIndeterminateTintList; BlendMode mIndeterminateBlendMode; diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java index 562cc4ffeeaa..217693eed686 100644 --- a/core/java/android/widget/SimpleMonthView.java +++ b/core/java/android/widget/SimpleMonthView.java @@ -28,6 +28,7 @@ import android.graphics.Paint.Style; import android.graphics.Rect; import android.graphics.Typeface; import android.icu.text.DisplayContext; +import android.icu.text.RelativeDateTimeFormatter; import android.icu.text.SimpleDateFormat; import android.icu.util.Calendar; import android.os.Bundle; @@ -1095,6 +1096,14 @@ class SimpleMonthView extends View { node.setText(getDayText(virtualViewId)); node.setContentDescription(getDayDescription(virtualViewId)); + if (virtualViewId == mToday) { + RelativeDateTimeFormatter fmt = RelativeDateTimeFormatter.getInstance(); + node.setStateDescription(fmt.format(RelativeDateTimeFormatter.Direction.THIS, + RelativeDateTimeFormatter.AbsoluteUnit.DAY)); + } + if (virtualViewId == mActivatedDay) { + node.setSelected(true); + } node.setBoundsInParent(mTempRect); final boolean isDayEnabled = isDayEnabled(virtualViewId); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 31f50555af1d..90e8ef2c6423 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -345,6 +345,8 @@ import java.util.function.Supplier; public class TextView extends View implements ViewTreeObserver.OnPreDrawListener { static final String LOG_TAG = "TextView"; static final boolean DEBUG_EXTRACT = false; + static final boolean DEBUG_CURSOR = false; + private static final float[] TEMP_POSITION = new float[2]; // Enum for the "typeface" XML parameter. @@ -10857,6 +10859,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public boolean onTouchEvent(MotionEvent event) { + if (DEBUG_CURSOR) { + logCursor("onTouchEvent", MotionEvent.actionToString(event.getActionMasked())); + } + final int action = event.getActionMasked(); if (mEditor != null) { mEditor.onTouchEvent(event); @@ -10868,6 +10874,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } final boolean superResult = super.onTouchEvent(event); + if (DEBUG_CURSOR) { + logCursor("onTouchEvent", "superResult=%s", superResult); + } /* * Don't handle the release after a long press, because it will move the selection away from @@ -10876,7 +10885,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ if (mEditor != null && mEditor.mDiscardNextActionUp && action == MotionEvent.ACTION_UP) { mEditor.mDiscardNextActionUp = false; - + if (DEBUG_CURSOR) { + logCursor("onTouchEvent", "release after long press detected"); + } if (mEditor.mIsInsertionActionModeStartPending) { mEditor.startInsertionActionMode(); mEditor.mIsInsertionActionModeStartPending = false; @@ -12254,6 +12265,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public boolean performLongClick() { + if (DEBUG_CURSOR) { + logCursor("performLongClick", null); + } + boolean handled = false; boolean performedHapticFeedback = false; @@ -13481,4 +13496,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener TextView.this.spanChange(buf, what, s, -1, e, -1); } } + + private static void logCursor(String location, @Nullable String msgFormat, Object ... msgArgs) { + if (msgFormat == null) { + Log.d(LOG_TAG, location); + } else { + Log.d(LOG_TAG, location + ": " + String.format(msgFormat, msgArgs)); + } + } } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 3b82f186d80e..b1752a47ea93 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -45,8 +45,6 @@ import android.content.IntentSender.SendIntentException; import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; -import android.content.pm.LabeledIntent; -import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; @@ -61,9 +59,7 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.drawable.AnimatedVectorDrawable; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; -import android.graphics.drawable.Icon; import android.metrics.LogMaker; import android.net.Uri; import android.os.AsyncTask; @@ -85,7 +81,6 @@ import android.service.chooser.ChooserTarget; import android.service.chooser.ChooserTargetService; import android.service.chooser.IChooserTargetResult; import android.service.chooser.IChooserTargetService; -import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.util.AttributeSet; import android.util.HashedStringCache; @@ -110,6 +105,14 @@ import android.widget.Toast; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.ResolverListAdapter.ActivityInfoPresentationGetter; +import com.android.internal.app.ResolverListAdapter.ViewHolder; +import com.android.internal.app.chooser.ChooserTargetInfo; +import com.android.internal.app.chooser.DisplayResolveInfo; +import com.android.internal.app.chooser.NotSelectableTargetInfo; +import com.android.internal.app.chooser.SelectableTargetInfo; +import com.android.internal.app.chooser.SelectableTargetInfo.SelectableTargetInfoCommunicator; +import com.android.internal.app.chooser.TargetInfo; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.content.PackageMonitor; import com.android.internal.logging.MetricsLogger; @@ -124,7 +127,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.text.Collator; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -138,7 +140,9 @@ import java.util.Set; * for example, those generated by @see android.content.Intent#createChooser(Intent, CharSequence). * */ -public class ChooserActivity extends ResolverActivity { +public class ChooserActivity extends ResolverActivity implements + ChooserListAdapter.ChooserListCommunicator, + SelectableTargetInfoCommunicator { private static final String TAG = "ChooserActivity"; @@ -154,12 +158,6 @@ public class ChooserActivity extends ResolverActivity { private static final boolean DEBUG = false; - /** - * If {@link #USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS} and this is set to true, - * {@link AppPredictionManager} will be queried for direct share targets. - */ - // TODO(b/123089490): Replace with system flag - private static final boolean USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS = true; private static final boolean USE_PREDICTION_MANAGER_FOR_SHARE_ACTIVITIES = true; // TODO(b/123088566) Share these in a better way. private static final String APP_PREDICTION_SHARE_UI_SURFACE = "share"; @@ -167,24 +165,21 @@ public class ChooserActivity extends ResolverActivity { private static final int APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20; public static final String APP_PREDICTION_INTENT_FILTER_KEY = "intent_filter"; + @VisibleForTesting + public static final int LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS = 250; + private boolean mIsAppPredictorComponentAvailable; private AppPredictor mAppPredictor; private AppPredictor.Callback mAppPredictorCallback; private Map<ChooserTarget, AppTarget> mDirectShareAppTargetCache; - /** - * If set to true, use ShortcutManager to retrieve the matching direct share targets, instead of - * binding to every ChooserTargetService implementation. - */ - // TODO(b/121287573): Replace with a system flag (setprop?) - private static final boolean USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS = true; - private static final boolean USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS = true; - public static final int TARGET_TYPE_DEFAULT = 0; public static final int TARGET_TYPE_CHOOSER_TARGET = 1; public static final int TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER = 2; public static final int TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE = 3; + private static final boolean USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS = true; + @IntDef(flag = false, prefix = { "TARGET_TYPE_" }, value = { TARGET_TYPE_DEFAULT, TARGET_TYPE_CHOOSER_TARGET, @@ -233,10 +228,6 @@ public class ChooserActivity extends ResolverActivity { private int mCurrAvailableWidth = 0; - /** {@link ChooserActivity#getBaseScore} */ - public static final float CALLER_TARGET_SCORE_BOOST = 900.f; - /** {@link ChooserActivity#getBaseScore} */ - public static final float SHORTCUT_TARGET_SCORE_BOOST = 90.f; private static final String TARGET_DETAILS_FRAGMENT_TAG = "targetDetailsFragment"; // TODO: Update to handle landscape instead of using static value private static final int MAX_RANKED_TARGETS = 4; @@ -246,14 +237,9 @@ public class ChooserActivity extends ResolverActivity { private static final int MAX_LOG_RANK_POSITION = 12; - @VisibleForTesting - public static final int LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS = 250; - private static final int MAX_EXTRA_INITIAL_INTENTS = 2; private static final int MAX_EXTRA_CHOOSER_TARGETS = 2; - private boolean mListViewDataChanged = false; - @Retention(SOURCE) @IntDef({CONTENT_PREVIEW_FILE, CONTENT_PREVIEW_IMAGE, CONTENT_PREVIEW_TEXT}) private @interface ContentPreviewType { @@ -266,9 +252,6 @@ public class ChooserActivity extends ResolverActivity { private static final int CONTENT_PREVIEW_TEXT = 3; protected MetricsLogger mMetricsLogger; - // Sorted list of DisplayResolveInfos for the alphabetical app section. - private List<ResolverActivity.DisplayResolveInfo> mSortedList = new ArrayList<>(); - private ContentPreviewCoordinator mPreviewCoord; private class ContentPreviewCoordinator { @@ -645,8 +628,7 @@ public class ChooserActivity extends ResolverActivity { if (isFinishing() || isDestroyed()) { return; } - // May be null if there are no apps to perform share/open action. - if (mChooserListAdapter == null) { + if (mChooserListAdapter.getCount() == 0) { return; } if (resultList.isEmpty()) { @@ -775,7 +757,7 @@ public class ChooserActivity extends ResolverActivity { @Override public void onSomePackagesChanged() { mAdapter.handlePackagesChanged(); - bindProfileView(); + updateProfileViewButton(); } }; } @@ -1191,7 +1173,7 @@ public class ChooserActivity extends ResolverActivity { } } - @Override + @Override // ResolverListCommunicator public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) { Intent result = defIntent; if (mReplacementExtras != null) { @@ -1231,9 +1213,8 @@ public class ChooserActivity extends ResolverActivity { } @Override - public void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter) { + public void onPrepareAdapterView(AbsListView adapterView, ResolverListAdapter adapter) { final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null; - mChooserListAdapter = (ChooserListAdapter) adapter; if (mCallerChooserTargets != null && mCallerChooserTargets.length > 0) { mChooserListAdapter.addServiceResults(null, Lists.newArrayList(mCallerChooserTargets), TARGET_TYPE_DEFAULT); @@ -1245,11 +1226,17 @@ public class ChooserActivity extends ResolverActivity { } @Override + protected boolean rebuildList() { + mChooserListAdapter = (ChooserListAdapter) mAdapter; + return rebuildListInternal(); + } + + @Override public int getLayoutResource() { return R.layout.chooser_grid; } - @Override + @Override // ResolverListCommunicator public boolean shouldGetActivityMetadata() { return true; } @@ -1328,7 +1315,7 @@ public class ChooserActivity extends ResolverActivity { final long selectionCost = System.currentTimeMillis() - mChooserShownTime; super.startSelected(which, always, filtered); - if (mChooserListAdapter != null) { + if (mChooserListAdapter.getCount() > 0) { // Log the index of which type of target the user picked. // Lower values mean the ranking was better. int cat = 0; @@ -1342,7 +1329,7 @@ public class ChooserActivity extends ResolverActivity { // Log the package name + target name to answer the question if most users // share to mostly the same person or to a bunch of different people. ChooserTarget target = - mChooserListAdapter.mServiceTargets.get(value).getChooserTarget(); + mChooserListAdapter.getChooserTargetForValue(value); directTargetHashed = HashedStringCache.getInstance().hashString( this, TAG, @@ -1428,7 +1415,7 @@ public class ChooserActivity extends ResolverActivity { continue; } final ActivityInfo ai = dri.getResolveInfo().activityInfo; - if (USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS + if (ChooserFlags.USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS && sm.hasShareTargets(ai.packageName)) { // Share targets will be queried from ShortcutManager continue; @@ -1817,8 +1804,8 @@ public class ChooserActivity extends ResolverActivity { */ @Nullable private AppPredictor getAppPredictorForDirectShareIfEnabled() { - return USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS && !ActivityManager.isLowRamDeviceStatic() - ? getAppPredictor() : null; + return ChooserFlags.USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS + && !ActivityManager.isLowRamDeviceStatic() ? getAppPredictor() : null; } /** @@ -1900,24 +1887,18 @@ public class ChooserActivity extends ResolverActivity { } } - private void updateAlphabeticalList() { - mSortedList.clear(); - mSortedList.addAll(getDisplayList()); - Collections.sort(mSortedList, new AzInfoComparator(ChooserActivity.this)); - } - /** * Sort intents alphabetically based on display label. */ - class AzInfoComparator implements Comparator<ResolverActivity.DisplayResolveInfo> { + static class AzInfoComparator implements Comparator<DisplayResolveInfo> { Collator mCollator; AzInfoComparator(Context context) { mCollator = Collator.getInstance(context.getResources().getConfiguration().locale); } @Override - public int compare(ResolverActivity.DisplayResolveInfo lhsp, - ResolverActivity.DisplayResolveInfo rhsp) { + public int compare( + DisplayResolveInfo lhsp, DisplayResolveInfo rhsp) { return mCollator.compare(lhsp.getDisplayLabel(), rhsp.getDisplayLabel()); } } @@ -1955,12 +1936,12 @@ public class ChooserActivity extends ResolverActivity { } @Override - public ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents, - Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid, - boolean filterLastUsed) { - final ChooserListAdapter adapter = new ChooserListAdapter(context, payloadIntents, - initialIntents, rList, launchedFromUid, filterLastUsed, createListController()); - return adapter; + public ResolverListAdapter createAdapter(Context context, List<Intent> payloadIntents, + Intent[] initialIntents, List<ResolveInfo> rList, + boolean filterLastUsed, boolean useLayoutForBrowsables) { + return new ChooserListAdapter(context, payloadIntents, + initialIntents, rList, filterLastUsed, createListController(), + useLayoutForBrowsables, this, this); } @VisibleForTesting @@ -1999,345 +1980,19 @@ public class ChooserActivity extends ResolverActivity { return null; } - interface ChooserTargetInfo extends TargetInfo { - float getModifiedScore(); - - ChooserTarget getChooserTarget(); - - /** - * Do not label as 'equals', since this doesn't quite work - * as intended with java 8. - */ - default boolean isSimilar(ChooserTargetInfo other) { - if (other == null) return false; - - ChooserTarget ct1 = getChooserTarget(); - ChooserTarget ct2 = other.getChooserTarget(); - - // If either is null, there is not enough info to make an informed decision - // about equality, so just exit - if (ct1 == null || ct2 == null) return false; - - if (ct1.getComponentName().equals(ct2.getComponentName()) - && TextUtils.equals(getDisplayLabel(), other.getDisplayLabel()) - && TextUtils.equals(getExtendedInfo(), other.getExtendedInfo())) { - return true; - } - - return false; - } - } - - /** - * Distinguish between targets that selectable by the user, vs those that are - * placeholders for the system while information is loading in an async manner. - */ - abstract class NotSelectableTargetInfo implements ChooserTargetInfo { - - public Intent getResolvedIntent() { - return null; - } - - public ComponentName getResolvedComponentName() { - return null; - } - - public boolean start(Activity activity, Bundle options) { - return false; - } - - public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) { - return false; - } - - public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { - return false; - } - - public ResolveInfo getResolveInfo() { - return null; - } - - public CharSequence getDisplayLabel() { - return null; - } - - public CharSequence getExtendedInfo() { - return null; - } - - public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { - return null; - } - - public List<Intent> getAllSourceIntents() { - return null; - } - - public float getModifiedScore() { - return -0.1f; - } - - public ChooserTarget getChooserTarget() { - return null; - } - - public boolean isSuspended() { - return false; - } - } - - final class PlaceHolderTargetInfo extends NotSelectableTargetInfo { - public Drawable getDisplayIcon() { + static final class PlaceHolderTargetInfo extends NotSelectableTargetInfo { + public Drawable getDisplayIcon(Context context) { AnimatedVectorDrawable avd = (AnimatedVectorDrawable) - getDrawable(R.drawable.chooser_direct_share_icon_placeholder); + context.getDrawable(R.drawable.chooser_direct_share_icon_placeholder); avd.start(); // Start animation after generation return avd; } } - - final class EmptyTargetInfo extends NotSelectableTargetInfo { - public Drawable getDisplayIcon() { - return null; - } - } - - final class SelectableTargetInfo implements ChooserTargetInfo { - private final DisplayResolveInfo mSourceInfo; - private final ResolveInfo mBackupResolveInfo; - private final ChooserTarget mChooserTarget; - private final String mDisplayLabel; - private Drawable mBadgeIcon = null; - private CharSequence mBadgeContentDescription; - private Drawable mDisplayIcon; - private final Intent mFillInIntent; - private final int mFillInFlags; - private final float mModifiedScore; - private boolean mIsSuspended = false; - - SelectableTargetInfo(DisplayResolveInfo sourceInfo, ChooserTarget chooserTarget, - float modifiedScore) { - mSourceInfo = sourceInfo; - mChooserTarget = chooserTarget; - mModifiedScore = modifiedScore; - if (sourceInfo != null) { - final ResolveInfo ri = sourceInfo.getResolveInfo(); - if (ri != null) { - final ActivityInfo ai = ri.activityInfo; - if (ai != null && ai.applicationInfo != null) { - final PackageManager pm = getPackageManager(); - mBadgeIcon = pm.getApplicationIcon(ai.applicationInfo); - mBadgeContentDescription = pm.getApplicationLabel(ai.applicationInfo); - mIsSuspended = - (ai.applicationInfo.flags & ApplicationInfo.FLAG_SUSPENDED) != 0; - } - } - } - // TODO(b/121287224): do this in the background thread, and only for selected targets - mDisplayIcon = getChooserTargetIconDrawable(chooserTarget); - - if (sourceInfo != null) { - mBackupResolveInfo = null; - } else { - mBackupResolveInfo = getPackageManager().resolveActivity(getResolvedIntent(), 0); - } - - mFillInIntent = null; - mFillInFlags = 0; - - mDisplayLabel = sanitizeDisplayLabel(chooserTarget.getTitle()); - } - - private SelectableTargetInfo(SelectableTargetInfo other, Intent fillInIntent, int flags) { - mSourceInfo = other.mSourceInfo; - mBackupResolveInfo = other.mBackupResolveInfo; - mChooserTarget = other.mChooserTarget; - mBadgeIcon = other.mBadgeIcon; - mBadgeContentDescription = other.mBadgeContentDescription; - mDisplayIcon = other.mDisplayIcon; - mFillInIntent = fillInIntent; - mFillInFlags = flags; - mModifiedScore = other.mModifiedScore; - - mDisplayLabel = sanitizeDisplayLabel(mChooserTarget.getTitle()); - } - - private String sanitizeDisplayLabel(CharSequence label) { - SpannableStringBuilder sb = new SpannableStringBuilder(label); - sb.clearSpans(); - return sb.toString(); - } - - public boolean isSuspended() { - return mIsSuspended; - } - - /** - * Since ShortcutInfos are returned by ShortcutManager, we can cache the shortcuts and skip - * the call to LauncherApps#getShortcuts(ShortcutQuery). - */ - // TODO(121287224): Refactor code to apply the suggestion above - private Drawable getChooserTargetIconDrawable(ChooserTarget target) { - Drawable directShareIcon = null; - - // First get the target drawable and associated activity info - final Icon icon = target.getIcon(); - if (icon != null) { - directShareIcon = icon.loadDrawable(ChooserActivity.this); - } else if (USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS) { - Bundle extras = target.getIntentExtras(); - if (extras != null && extras.containsKey(Intent.EXTRA_SHORTCUT_ID)) { - CharSequence shortcutId = extras.getCharSequence(Intent.EXTRA_SHORTCUT_ID); - LauncherApps launcherApps = (LauncherApps) getSystemService( - Context.LAUNCHER_APPS_SERVICE); - final LauncherApps.ShortcutQuery q = new LauncherApps.ShortcutQuery(); - q.setPackage(target.getComponentName().getPackageName()); - q.setShortcutIds(Arrays.asList(shortcutId.toString())); - q.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC); - final List<ShortcutInfo> shortcuts = launcherApps.getShortcuts(q, getUser()); - if (shortcuts != null && shortcuts.size() > 0) { - directShareIcon = launcherApps.getShortcutIconDrawable(shortcuts.get(0), 0); - } - } - } - - if (directShareIcon == null) return null; - - ActivityInfo info = null; - try { - info = mPm.getActivityInfo(target.getComponentName(), 0); - } catch (NameNotFoundException error) { - Log.e(TAG, "Could not find activity associated with ChooserTarget"); - } - - if (info == null) return null; - - // Now fetch app icon and raster with no badging even in work profile - Bitmap appIcon = makePresentationGetter(info).getIconBitmap( - UserHandle.getUserHandleForUid(UserHandle.myUserId())); - - // Raster target drawable with appIcon as a badge - SimpleIconFactory sif = SimpleIconFactory.obtain(ChooserActivity.this); - Bitmap directShareBadgedIcon = sif.createAppBadgedIconBitmap(directShareIcon, appIcon); - sif.recycle(); - - return new BitmapDrawable(getResources(), directShareBadgedIcon); - } - - public float getModifiedScore() { - return mModifiedScore; - } - - @Override - public Intent getResolvedIntent() { - if (mSourceInfo != null) { - return mSourceInfo.getResolvedIntent(); - } - - final Intent targetIntent = new Intent(getTargetIntent()); - targetIntent.setComponent(mChooserTarget.getComponentName()); - targetIntent.putExtras(mChooserTarget.getIntentExtras()); - return targetIntent; - } - - @Override - public ComponentName getResolvedComponentName() { - if (mSourceInfo != null) { - return mSourceInfo.getResolvedComponentName(); - } else if (mBackupResolveInfo != null) { - return new ComponentName(mBackupResolveInfo.activityInfo.packageName, - mBackupResolveInfo.activityInfo.name); - } - return null; - } - - private Intent getBaseIntentToSend() { - Intent result = getResolvedIntent(); - if (result == null) { - Log.e(TAG, "ChooserTargetInfo: no base intent available to send"); - } else { - result = new Intent(result); - if (mFillInIntent != null) { - result.fillIn(mFillInIntent, mFillInFlags); - } - result.fillIn(mReferrerFillInIntent, 0); - } - return result; - } - - @Override - public boolean start(Activity activity, Bundle options) { - throw new RuntimeException("ChooserTargets should be started as caller."); - } - - @Override - public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) { - final Intent intent = getBaseIntentToSend(); - if (intent == null) { - return false; - } - intent.setComponent(mChooserTarget.getComponentName()); - intent.putExtras(mChooserTarget.getIntentExtras()); - - // Important: we will ignore the target security checks in ActivityManager - // if and only if the ChooserTarget's target package is the same package - // where we got the ChooserTargetService that provided it. This lets a - // ChooserTargetService provide a non-exported or permission-guarded target - // to the chooser for the user to pick. - // - // If mSourceInfo is null, we got this ChooserTarget from the caller or elsewhere - // so we'll obey the caller's normal security checks. - final boolean ignoreTargetSecurity = mSourceInfo != null - && mSourceInfo.getResolvedComponentName().getPackageName() - .equals(mChooserTarget.getComponentName().getPackageName()); - return activity.startAsCallerImpl(intent, options, ignoreTargetSecurity, userId); - } - - @Override - public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { - throw new RuntimeException("ChooserTargets should be started as caller."); - } - - @Override - public ResolveInfo getResolveInfo() { - return mSourceInfo != null ? mSourceInfo.getResolveInfo() : mBackupResolveInfo; - } - - @Override - public CharSequence getDisplayLabel() { - return mDisplayLabel; - } - - @Override - public CharSequence getExtendedInfo() { - // ChooserTargets have badge icons, so we won't show the extended info to disambiguate. + static final class EmptyTargetInfo extends NotSelectableTargetInfo { + public Drawable getDisplayIcon(Context context) { return null; } - - @Override - public Drawable getDisplayIcon() { - return mDisplayIcon; - } - - public ChooserTarget getChooserTarget() { - return mChooserTarget; - } - - @Override - public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { - return new SelectableTargetInfo(this, fillInIntent, flags); - } - - @Override - public List<Intent> getAllSourceIntents() { - final List<Intent> results = new ArrayList<>(); - if (mSourceInfo != null) { - // We only queried the service for the first one in our sourceinfo. - results.add(mSourceInfo.getAllSourceIntents().get(0)); - } - return results; - } } private void handleScroll(View view, int x, int y, int oldx, int oldy) { @@ -2408,7 +2063,8 @@ public class ChooserActivity extends ResolverActivity { boolean isExpandable = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT && !isInMultiWindowMode(); - if (directShareHeight != 0 && isSendAction(getTargetIntent()) && isExpandable) { + if (directShareHeight != 0 && isSendAction(getTargetIntent()) + && isExpandable) { // make sure to leave room for direct share 4->8 expansion int requiredExpansionHeight = (int) (directShareHeight / DIRECT_SHARE_EXPANSION_RATE); @@ -2424,508 +2080,85 @@ public class ChooserActivity extends ResolverActivity { } } - public class ChooserListAdapter extends ResolveListAdapter { - public static final int TARGET_BAD = -1; - public static final int TARGET_CALLER = 0; - public static final int TARGET_SERVICE = 1; - public static final int TARGET_STANDARD = 2; - public static final int TARGET_STANDARD_AZ = 3; - - private static final int MAX_SUGGESTED_APP_TARGETS = 4; - private static final int MAX_CHOOSER_TARGETS_PER_APP = 2; - - private static final int MAX_SERVICE_TARGETS = 8; - - private final int mMaxShortcutTargetsPerApp = - getResources().getInteger(R.integer.config_maxShortcutTargetsPerApp); - - private int mNumShortcutResults = 0; - - // Reserve spots for incoming direct share targets by adding placeholders - private ChooserTargetInfo mPlaceHolderTargetInfo = new PlaceHolderTargetInfo(); - private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>(); - private final List<TargetInfo> mCallerTargets = new ArrayList<>(); - - private final BaseChooserTargetComparator mBaseTargetComparator - = new BaseChooserTargetComparator(); - - public ChooserListAdapter(Context context, List<Intent> payloadIntents, - Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid, - boolean filterLastUsed, ResolverListController resolverListController) { - // Don't send the initial intents through the shared ResolverActivity path, - // we want to separate them into a different section. - super(context, payloadIntents, null, rList, launchedFromUid, filterLastUsed, - resolverListController); - - createPlaceHolders(); - - if (initialIntents != null) { - final PackageManager pm = getPackageManager(); - for (int i = 0; i < initialIntents.length; i++) { - final Intent ii = initialIntents[i]; - if (ii == null) { - continue; - } - - // We reimplement Intent#resolveActivityInfo here because if we have an - // implicit intent, we want the ResolveInfo returned by PackageManager - // instead of one we reconstruct ourselves. The ResolveInfo returned might - // have extra metadata and resolvePackageName set and we want to respect that. - ResolveInfo ri = null; - ActivityInfo ai = null; - final ComponentName cn = ii.getComponent(); - if (cn != null) { - try { - ai = pm.getActivityInfo(ii.getComponent(), 0); - ri = new ResolveInfo(); - ri.activityInfo = ai; - } catch (PackageManager.NameNotFoundException ignored) { - // ai will == null below - } - } - if (ai == null) { - ri = pm.resolveActivity(ii, PackageManager.MATCH_DEFAULT_ONLY); - ai = ri != null ? ri.activityInfo : null; - } - if (ai == null) { - Log.w(TAG, "No activity found for " + ii); - continue; - } - UserManager userManager = - (UserManager) getSystemService(Context.USER_SERVICE); - if (ii instanceof LabeledIntent) { - LabeledIntent li = (LabeledIntent) ii; - ri.resolvePackageName = li.getSourcePackage(); - ri.labelRes = li.getLabelResource(); - ri.nonLocalizedLabel = li.getNonLocalizedLabel(); - ri.icon = li.getIconResource(); - ri.iconResourceId = ri.icon; - } - if (userManager.isManagedProfile()) { - ri.noResourceId = true; - ri.icon = 0; - } - mCallerTargets.add(new DisplayResolveInfo(ii, ri, ii)); - } - } - } - - @Override - public void handlePackagesChanged() { - if (DEBUG) { - Log.d(TAG, "clearing queryTargets on package change"); - } - createPlaceHolders(); - mServicesRequested.clear(); - notifyDataSetChanged(); - - super.handlePackagesChanged(); - } - + static class BaseChooserTargetComparator implements Comparator<ChooserTarget> { @Override - public void notifyDataSetChanged() { - if (!mListViewDataChanged) { - mChooserHandler.sendEmptyMessageDelayed(ChooserHandler.LIST_VIEW_UPDATE_MESSAGE, - LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS); - mListViewDataChanged = true; - } + public int compare(ChooserTarget lhs, ChooserTarget rhs) { + // Descending order + return (int) Math.signum(rhs.getScore() - lhs.getScore()); } + } - private void refreshListView() { - if (mListViewDataChanged) { - super.notifyDataSetChanged(); - } - mListViewDataChanged = false; - } + @Override // ResolverListCommunicator + public void onHandlePackagesChanged() { + mServicesRequested.clear(); + mAdapter.notifyDataSetChanged(); + super.onHandlePackagesChanged(); + } + @Override // SelectableTargetInfoCommunicator + public ActivityInfoPresentationGetter makePresentationGetter(ActivityInfo info) { + return mChooserListAdapter.makePresentationGetter(info); + } - private void createPlaceHolders() { - mNumShortcutResults = 0; - mServiceTargets.clear(); - for (int i = 0; i < MAX_SERVICE_TARGETS; i++) { - mServiceTargets.add(mPlaceHolderTargetInfo); - } - } + @Override // SelectableTargetInfoCommunicator + public Intent getReferrerFillInIntent() { + return mReferrerFillInIntent; + } - @Override - public View onCreateView(ViewGroup parent) { - return mInflater.inflate( - com.android.internal.R.layout.resolve_grid_item, parent, false); - } + @Override // ChooserListCommunicator + public int getMaxRankedTargets() { + return mChooserRowAdapter == null ? 4 : mChooserRowAdapter.getMaxTargetsPerRow(); + } - @Override - protected void onBindView(View view, TargetInfo info) { - super.onBindView(view, info); - - // If target is loading, show a special placeholder shape in the label, make unclickable - final ViewHolder holder = (ViewHolder) view.getTag(); - if (info instanceof PlaceHolderTargetInfo) { - final int maxWidth = getResources().getDimensionPixelSize( - R.dimen.chooser_direct_share_label_placeholder_max_width); - holder.text.setMaxWidth(maxWidth); - holder.text.setBackground(getResources().getDrawable( - R.drawable.chooser_direct_share_label_placeholder, getTheme())); - // Prevent rippling by removing background containing ripple - holder.itemView.setBackground(null); - } else { - holder.text.setMaxWidth(Integer.MAX_VALUE); - holder.text.setBackground(null); - holder.itemView.setBackground(holder.defaultItemViewBackground); - } - } + @Override // ChooserListCommunicator + public void sendListViewUpdateMessage() { + mChooserHandler.sendEmptyMessageDelayed(ChooserHandler.LIST_VIEW_UPDATE_MESSAGE, + LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS); + } - /** - * Rather than fully sorting the input list, this sorting task will put the top k elements - * in the head of input list and fill the tail with other elements in undetermined order. - */ - @Override - AsyncTask<List<ResolvedComponentInfo>, - Void, - List<ResolvedComponentInfo>> createSortingTask() { - return new AsyncTask<List<ResolvedComponentInfo>, - Void, - List<ResolvedComponentInfo>>() { + @Override + public void onListRebuilt() { + if (mChooserListAdapter.mDisplayList == null + || mChooserListAdapter.mDisplayList.isEmpty()) { + mChooserListAdapter.notifyDataSetChanged(); + } else { + new AsyncTask<Void, Void, Void>() { @Override - protected List<ResolvedComponentInfo> doInBackground( - List<ResolvedComponentInfo>... params) { - mResolverListController.topK(params[0], - getMaxRankedTargets()); - return params[0]; + protected Void doInBackground(Void... voids) { + mChooserListAdapter.updateAlphabeticalList(); + return null; } - @Override - protected void onPostExecute(List<ResolvedComponentInfo> sortedComponents) { - processSortedList(sortedComponents); - bindProfileView(); - notifyDataSetChanged(); - } - }; - } - - @Override - public void onListRebuilt() { - if (getDisplayList() == null || getDisplayList().isEmpty()) { - notifyDataSetChanged(); - } else { - new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... voids) { - updateAlphabeticalList(); - return null; - } - - @Override - protected void onPostExecute(Void aVoid) { - notifyDataSetChanged(); - } - }.execute(); - } - - // don't support direct share on low ram devices - if (ActivityManager.isLowRamDeviceStatic()) { - return; - } - - if (USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS - || USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) { - if (DEBUG) { - Log.d(TAG, "querying direct share targets from ShortcutManager"); - } - - queryDirectShareTargets(this, false); - } - if (USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS) { - if (DEBUG) { - Log.d(TAG, "List built querying services"); - } - - queryTargetServices(this); - } - } - - @Override - public boolean shouldGetResolvedFilter() { - return true; - } - - @Override - public int getCount() { - return getRankedTargetCount() + getAlphaTargetCount() - + getSelectableServiceTargetCount() + getCallerTargetCount(); - } - - @Override - public int getUnfilteredCount() { - int appTargets = super.getUnfilteredCount(); - if (appTargets > getMaxRankedTargets()) { - appTargets = appTargets + getMaxRankedTargets(); - } - return appTargets + getSelectableServiceTargetCount() + getCallerTargetCount(); - } - - - public int getCallerTargetCount() { - return Math.min(mCallerTargets.size(), MAX_SUGGESTED_APP_TARGETS); - } - - /** - * Filter out placeholders and non-selectable service targets - */ - public int getSelectableServiceTargetCount() { - int count = 0; - for (ChooserTargetInfo info : mServiceTargets) { - if (info instanceof SelectableTargetInfo) { - count++; + protected void onPostExecute(Void aVoid) { + mChooserListAdapter.notifyDataSetChanged(); } - } - return count; - } - - public int getServiceTargetCount() { - if (isSendAction(getTargetIntent()) && !ActivityManager.isLowRamDeviceStatic()) { - return Math.min(mServiceTargets.size(), MAX_SERVICE_TARGETS); - } - - return 0; + }.execute(); } - int getAlphaTargetCount() { - int standardCount = super.getCount(); - return standardCount > getMaxRankedTargets() ? standardCount : 0; - } - - int getRankedTargetCount() { - int spacesAvailable = getMaxRankedTargets() - getCallerTargetCount(); - return Math.min(spacesAvailable, super.getCount()); - } - - private int getMaxRankedTargets() { - return mChooserRowAdapter == null ? 4 : mChooserRowAdapter.getMaxTargetsPerRow(); - } - - public int getPositionTargetType(int position) { - int offset = 0; - - final int serviceTargetCount = getServiceTargetCount(); - if (position < serviceTargetCount) { - return TARGET_SERVICE; - } - offset += serviceTargetCount; - - final int callerTargetCount = getCallerTargetCount(); - if (position - offset < callerTargetCount) { - return TARGET_CALLER; - } - offset += callerTargetCount; - - final int rankedTargetCount = getRankedTargetCount(); - if (position - offset < rankedTargetCount) { - return TARGET_STANDARD; - } - offset += rankedTargetCount; - - final int standardTargetCount = getAlphaTargetCount(); - if (position - offset < standardTargetCount) { - return TARGET_STANDARD_AZ; - } - - return TARGET_BAD; - } - - @Override - public TargetInfo getItem(int position) { - return targetInfoForPosition(position, true); - } - - - /** - * Find target info for a given position. - * Since ChooserActivity displays several sections of content, determine which - * section provides this item. - */ - @Override - public TargetInfo targetInfoForPosition(int position, boolean filtered) { - int offset = 0; - - // Direct share targets - final int serviceTargetCount = filtered ? getServiceTargetCount() : - getSelectableServiceTargetCount(); - if (position < serviceTargetCount) { - return mServiceTargets.get(position); - } - offset += serviceTargetCount; - - // Targets provided by calling app - final int callerTargetCount = getCallerTargetCount(); - if (position - offset < callerTargetCount) { - return mCallerTargets.get(position - offset); - } - offset += callerTargetCount; - - // Ranked standard app targets - final int rankedTargetCount = getRankedTargetCount(); - if (position - offset < rankedTargetCount) { - return filtered ? super.getItem(position - offset) - : getDisplayResolveInfo(position - offset); - } - offset += rankedTargetCount; - - // Alphabetical complete app target list. - if (position - offset < getAlphaTargetCount() && !mSortedList.isEmpty()) { - return mSortedList.get(position - offset); - } - - return null; + // don't support direct share on low ram devices + if (ActivityManager.isLowRamDeviceStatic()) { + return; } - - /** - * Evaluate targets for inclusion in the direct share area. May not be included - * if score is too low. - */ - public void addServiceResults(DisplayResolveInfo origTarget, List<ChooserTarget> targets, - @ShareTargetType int targetType) { + if (ChooserFlags.USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS + || ChooserFlags.USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) { if (DEBUG) { - Log.d(TAG, "addServiceResults " + origTarget + ", " + targets.size() - + " targets"); - } - - if (targets.size() == 0) { - return; - } - - final float baseScore = getBaseScore(origTarget, targetType); - Collections.sort(targets, mBaseTargetComparator); - - final boolean isShortcutResult = - (targetType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER - || targetType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE); - final int maxTargets = isShortcutResult ? mMaxShortcutTargetsPerApp - : MAX_CHOOSER_TARGETS_PER_APP; - float lastScore = 0; - boolean shouldNotify = false; - for (int i = 0, count = Math.min(targets.size(), maxTargets); i < count; i++) { - final ChooserTarget target = targets.get(i); - float targetScore = target.getScore(); - targetScore *= baseScore; - if (i > 0 && targetScore >= lastScore) { - // Apply a decay so that the top app can't crowd out everything else. - // This incents ChooserTargetServices to define what's truly better. - targetScore = lastScore * 0.95f; - } - boolean isInserted = insertServiceTarget( - new SelectableTargetInfo(origTarget, target, targetScore)); - - if (isInserted && isShortcutResult) { - mNumShortcutResults++; - } - - shouldNotify |= isInserted; - - if (DEBUG) { - Log.d(TAG, " => " + target.toString() + " score=" + targetScore - + " base=" + target.getScore() - + " lastScore=" + lastScore - + " baseScore=" + baseScore); - } - - lastScore = targetScore; + Log.d(TAG, "querying direct share targets from ShortcutManager"); } - if (shouldNotify) { - notifyDataSetChanged(); - } - } - - private int getNumShortcutResults() { - return mNumShortcutResults; + queryDirectShareTargets(mChooserListAdapter, false); } - - /** - * Use the scoring system along with artificial boosts to create up to 4 distinct buckets: - * <ol> - * <li>App-supplied targets - * <li>Shortcuts ranked via App Prediction Manager - * <li>Shortcuts ranked via legacy heuristics - * <li>Legacy direct share targets - * </ol> - */ - public float getBaseScore(DisplayResolveInfo target, @ShareTargetType int targetType) { - if (target == null) { - return CALLER_TARGET_SCORE_BOOST; - } - - if (targetType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE) { - return SHORTCUT_TARGET_SCORE_BOOST; - } - - float score = super.getScore(target); - if (targetType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER) { - return score * SHORTCUT_TARGET_SCORE_BOOST; - } - - return score; - } - - /** - * Calling this marks service target loading complete, and will attempt to no longer - * update the direct share area. - */ - public void completeServiceTargetLoading() { - mServiceTargets.removeIf(o -> o instanceof PlaceHolderTargetInfo); - - if (mServiceTargets.isEmpty()) { - mServiceTargets.add(new EmptyTargetInfo()); - } - notifyDataSetChanged(); - } - - private boolean insertServiceTarget(ChooserTargetInfo chooserTargetInfo) { - // Avoid inserting any potentially late results - if (mServiceTargets.size() == 1 - && mServiceTargets.get(0) instanceof EmptyTargetInfo) { - return false; - } - - // Check for duplicates and abort if found - for (ChooserTargetInfo otherTargetInfo : mServiceTargets) { - if (chooserTargetInfo.isSimilar(otherTargetInfo)) { - return false; - } - } - - int currentSize = mServiceTargets.size(); - final float newScore = chooserTargetInfo.getModifiedScore(); - for (int i = 0; i < Math.min(currentSize, MAX_SERVICE_TARGETS); i++) { - final ChooserTargetInfo serviceTarget = mServiceTargets.get(i); - if (serviceTarget == null) { - mServiceTargets.set(i, chooserTargetInfo); - return true; - } else if (newScore > serviceTarget.getModifiedScore()) { - mServiceTargets.add(i, chooserTargetInfo); - return true; - } - } - - if (currentSize < MAX_SERVICE_TARGETS) { - mServiceTargets.add(chooserTargetInfo); - return true; + if (USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS) { + if (DEBUG) { + Log.d(TAG, "List built querying services"); } - return false; + queryTargetServices(mChooserListAdapter); } } - static class BaseChooserTargetComparator implements Comparator<ChooserTarget> { - @Override - public int compare(ChooserTarget lhs, ChooserTarget rhs) { - // Descending order - return (int) Math.signum(rhs.getScore() - lhs.getScore()); - } - } - - - private boolean isSendAction(Intent targetIntent) { + @Override // ChooserListCommunicator + public boolean isSendAction(Intent targetIntent) { if (targetIntent == null) { return false; } @@ -3080,7 +2313,8 @@ public class ChooserActivity extends ResolverActivity { // There can be at most one row in the listview, that is internally // a ViewGroup with 2 rows public int getServiceTargetRowCount() { - if (isSendAction(getTargetIntent()) && !ActivityManager.isLowRamDeviceStatic()) { + if (isSendAction(getTargetIntent()) + && !ActivityManager.isLowRamDeviceStatic()) { return 1; } return 0; @@ -3177,7 +2411,7 @@ public class ChooserActivity extends ResolverActivity { getResources().getDrawable(R.drawable.chooser_row_layer_list, null)); mProfileView = profileRow.findViewById(R.id.profile_button); mProfileView.setOnClickListener(ChooserActivity.this::onProfileClick); - bindProfileView(); + updateProfileViewButton(); return profileRow; } diff --git a/core/java/com/android/internal/app/ChooserFlags.java b/core/java/com/android/internal/app/ChooserFlags.java new file mode 100644 index 000000000000..f1f1dbf49b8b --- /dev/null +++ b/core/java/com/android/internal/app/ChooserFlags.java @@ -0,0 +1,38 @@ +/* + * 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.internal.app; + +import android.app.prediction.AppPredictionManager; + +/** + * Common flags for {@link ChooserListAdapter} and {@link ChooserActivity}. + */ +public class ChooserFlags { + /** + * If set to true, use ShortcutManager to retrieve the matching direct share targets, instead of + * binding to every ChooserTargetService implementation. + */ + // TODO(b/121287573): Replace with a system flag (setprop?) + public static final boolean USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS = true; + + /** + * If {@link ChooserFlags#USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS} and this is set to true, + * {@link AppPredictionManager} will be queried for direct share targets. + */ + // TODO(b/123089490): Replace with system flag + static final boolean USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS = true; +} diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java new file mode 100644 index 000000000000..6eb470fef2bc --- /dev/null +++ b/core/java/com/android/internal/app/ChooserListAdapter.java @@ -0,0 +1,539 @@ +/* + * 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.internal.app; + +import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE; +import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER; + +import android.app.ActivityManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.LabeledIntent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.AsyncTask; +import android.os.UserManager; +import android.service.chooser.ChooserTarget; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + +import com.android.internal.R; +import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; +import com.android.internal.app.chooser.ChooserTargetInfo; +import com.android.internal.app.chooser.DisplayResolveInfo; +import com.android.internal.app.chooser.SelectableTargetInfo; +import com.android.internal.app.chooser.TargetInfo; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ChooserListAdapter extends ResolverListAdapter { + private static final String TAG = "ChooserListAdapter"; + private static final boolean DEBUG = false; + + public static final int TARGET_BAD = -1; + public static final int TARGET_CALLER = 0; + public static final int TARGET_SERVICE = 1; + public static final int TARGET_STANDARD = 2; + public static final int TARGET_STANDARD_AZ = 3; + + private static final int MAX_SUGGESTED_APP_TARGETS = 4; + private static final int MAX_CHOOSER_TARGETS_PER_APP = 2; + + static final int MAX_SERVICE_TARGETS = 8; + + /** {@link #getBaseScore} */ + public static final float CALLER_TARGET_SCORE_BOOST = 900.f; + /** {@link #getBaseScore} */ + public static final float SHORTCUT_TARGET_SCORE_BOOST = 90.f; + + private final int mMaxShortcutTargetsPerApp; + private final ChooserListCommunicator mChooserListCommunicator; + private final SelectableTargetInfo.SelectableTargetInfoCommunicator + mSelectableTargetInfoComunicator; + + private int mNumShortcutResults = 0; + + // Reserve spots for incoming direct share targets by adding placeholders + private ChooserTargetInfo + mPlaceHolderTargetInfo = new ChooserActivity.PlaceHolderTargetInfo(); + private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>(); + private final List<TargetInfo> mCallerTargets = new ArrayList<>(); + + private final ChooserActivity.BaseChooserTargetComparator mBaseTargetComparator = + new ChooserActivity.BaseChooserTargetComparator(); + private boolean mListViewDataChanged = false; + + // Sorted list of DisplayResolveInfos for the alphabetical app section. + private List<DisplayResolveInfo> mSortedList = new ArrayList<>(); + + public ChooserListAdapter(Context context, List<Intent> payloadIntents, + Intent[] initialIntents, List<ResolveInfo> rList, + boolean filterLastUsed, ResolverListController resolverListController, + boolean useLayoutForBrowsables, + ChooserListCommunicator chooserListCommunicator, + SelectableTargetInfo.SelectableTargetInfoCommunicator selectableTargetInfoComunicator) { + // Don't send the initial intents through the shared ResolverActivity path, + // we want to separate them into a different section. + super(context, payloadIntents, null, rList, filterLastUsed, + resolverListController, useLayoutForBrowsables, + chooserListCommunicator); + + createPlaceHolders(); + mMaxShortcutTargetsPerApp = + context.getResources().getInteger(R.integer.config_maxShortcutTargetsPerApp); + mChooserListCommunicator = chooserListCommunicator; + mSelectableTargetInfoComunicator = selectableTargetInfoComunicator; + + if (initialIntents != null) { + final PackageManager pm = context.getPackageManager(); + for (int i = 0; i < initialIntents.length; i++) { + final Intent ii = initialIntents[i]; + if (ii == null) { + continue; + } + + // We reimplement Intent#resolveActivityInfo here because if we have an + // implicit intent, we want the ResolveInfo returned by PackageManager + // instead of one we reconstruct ourselves. The ResolveInfo returned might + // have extra metadata and resolvePackageName set and we want to respect that. + ResolveInfo ri = null; + ActivityInfo ai = null; + final ComponentName cn = ii.getComponent(); + if (cn != null) { + try { + ai = pm.getActivityInfo(ii.getComponent(), 0); + ri = new ResolveInfo(); + ri.activityInfo = ai; + } catch (PackageManager.NameNotFoundException ignored) { + // ai will == null below + } + } + if (ai == null) { + ri = pm.resolveActivity(ii, PackageManager.MATCH_DEFAULT_ONLY); + ai = ri != null ? ri.activityInfo : null; + } + if (ai == null) { + Log.w(TAG, "No activity found for " + ii); + continue; + } + UserManager userManager = + (UserManager) context.getSystemService(Context.USER_SERVICE); + if (ii instanceof LabeledIntent) { + LabeledIntent li = (LabeledIntent) ii; + ri.resolvePackageName = li.getSourcePackage(); + ri.labelRes = li.getLabelResource(); + ri.nonLocalizedLabel = li.getNonLocalizedLabel(); + ri.icon = li.getIconResource(); + ri.iconResourceId = ri.icon; + } + if (userManager.isManagedProfile()) { + ri.noResourceId = true; + ri.icon = 0; + } + mCallerTargets.add(new DisplayResolveInfo(ii, ri, ii, makePresentationGetter(ri))); + } + } + } + + @Override + public void handlePackagesChanged() { + if (DEBUG) { + Log.d(TAG, "clearing queryTargets on package change"); + } + createPlaceHolders(); + mChooserListCommunicator.onHandlePackagesChanged(); + + } + + @Override + public void notifyDataSetChanged() { + if (!mListViewDataChanged) { + mChooserListCommunicator.sendListViewUpdateMessage(); + mListViewDataChanged = true; + } + } + + void refreshListView() { + if (mListViewDataChanged) { + super.notifyDataSetChanged(); + } + mListViewDataChanged = false; + } + + + private void createPlaceHolders() { + mNumShortcutResults = 0; + mServiceTargets.clear(); + for (int i = 0; i < MAX_SERVICE_TARGETS; i++) { + mServiceTargets.add(mPlaceHolderTargetInfo); + } + } + + @Override + public View onCreateView(ViewGroup parent) { + return mInflater.inflate( + com.android.internal.R.layout.resolve_grid_item, parent, false); + } + + @Override + protected void onBindView(View view, TargetInfo info) { + super.onBindView(view, info); + + // If target is loading, show a special placeholder shape in the label, make unclickable + final ViewHolder holder = (ViewHolder) view.getTag(); + if (info instanceof ChooserActivity.PlaceHolderTargetInfo) { + final int maxWidth = mContext.getResources().getDimensionPixelSize( + R.dimen.chooser_direct_share_label_placeholder_max_width); + holder.text.setMaxWidth(maxWidth); + holder.text.setBackground(mContext.getResources().getDrawable( + R.drawable.chooser_direct_share_label_placeholder, mContext.getTheme())); + // Prevent rippling by removing background containing ripple + holder.itemView.setBackground(null); + } else { + holder.text.setMaxWidth(Integer.MAX_VALUE); + holder.text.setBackground(null); + holder.itemView.setBackground(holder.defaultItemViewBackground); + } + } + + void updateAlphabeticalList() { + mSortedList.clear(); + mSortedList.addAll(mDisplayList); + Collections.sort(mSortedList, new ChooserActivity.AzInfoComparator(mContext)); + } + + @Override + public boolean shouldGetResolvedFilter() { + return true; + } + + @Override + public int getCount() { + return getRankedTargetCount() + getAlphaTargetCount() + + getSelectableServiceTargetCount() + getCallerTargetCount(); + } + + @Override + public int getUnfilteredCount() { + int appTargets = super.getUnfilteredCount(); + if (appTargets > mChooserListCommunicator.getMaxRankedTargets()) { + appTargets = appTargets + mChooserListCommunicator.getMaxRankedTargets(); + } + return appTargets + getSelectableServiceTargetCount() + getCallerTargetCount(); + } + + + public int getCallerTargetCount() { + return Math.min(mCallerTargets.size(), MAX_SUGGESTED_APP_TARGETS); + } + + /** + * Filter out placeholders and non-selectable service targets + */ + public int getSelectableServiceTargetCount() { + int count = 0; + for (ChooserTargetInfo info : mServiceTargets) { + if (info instanceof SelectableTargetInfo) { + count++; + } + } + return count; + } + + public int getServiceTargetCount() { + if (mChooserListCommunicator.isSendAction(mChooserListCommunicator.getTargetIntent()) + && !ActivityManager.isLowRamDeviceStatic()) { + return Math.min(mServiceTargets.size(), MAX_SERVICE_TARGETS); + } + + return 0; + } + + int getAlphaTargetCount() { + int standardCount = super.getCount(); + return standardCount > mChooserListCommunicator.getMaxRankedTargets() ? standardCount : 0; + } + + int getRankedTargetCount() { + int spacesAvailable = + mChooserListCommunicator.getMaxRankedTargets() - getCallerTargetCount(); + return Math.min(spacesAvailable, super.getCount()); + } + + public int getPositionTargetType(int position) { + int offset = 0; + + final int serviceTargetCount = getServiceTargetCount(); + if (position < serviceTargetCount) { + return TARGET_SERVICE; + } + offset += serviceTargetCount; + + final int callerTargetCount = getCallerTargetCount(); + if (position - offset < callerTargetCount) { + return TARGET_CALLER; + } + offset += callerTargetCount; + + final int rankedTargetCount = getRankedTargetCount(); + if (position - offset < rankedTargetCount) { + return TARGET_STANDARD; + } + offset += rankedTargetCount; + + final int standardTargetCount = getAlphaTargetCount(); + if (position - offset < standardTargetCount) { + return TARGET_STANDARD_AZ; + } + + return TARGET_BAD; + } + + @Override + public TargetInfo getItem(int position) { + return targetInfoForPosition(position, true); + } + + + /** + * Find target info for a given position. + * Since ChooserActivity displays several sections of content, determine which + * section provides this item. + */ + @Override + public TargetInfo targetInfoForPosition(int position, boolean filtered) { + int offset = 0; + + // Direct share targets + final int serviceTargetCount = filtered ? getServiceTargetCount() : + getSelectableServiceTargetCount(); + if (position < serviceTargetCount) { + return mServiceTargets.get(position); + } + offset += serviceTargetCount; + + // Targets provided by calling app + final int callerTargetCount = getCallerTargetCount(); + if (position - offset < callerTargetCount) { + return mCallerTargets.get(position - offset); + } + offset += callerTargetCount; + + // Ranked standard app targets + final int rankedTargetCount = getRankedTargetCount(); + if (position - offset < rankedTargetCount) { + return filtered ? super.getItem(position - offset) + : getDisplayResolveInfo(position - offset); + } + offset += rankedTargetCount; + + // Alphabetical complete app target list. + if (position - offset < getAlphaTargetCount() && !mSortedList.isEmpty()) { + return mSortedList.get(position - offset); + } + + return null; + } + + + /** + * Evaluate targets for inclusion in the direct share area. May not be included + * if score is too low. + */ + public void addServiceResults(DisplayResolveInfo origTarget, List<ChooserTarget> targets, + @ChooserActivity.ShareTargetType int targetType) { + if (DEBUG) { + Log.d(TAG, "addServiceResults " + origTarget + ", " + targets.size() + + " targets"); + } + + if (targets.size() == 0) { + return; + } + + final float baseScore = getBaseScore(origTarget, targetType); + Collections.sort(targets, mBaseTargetComparator); + + final boolean isShortcutResult = + (targetType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER + || targetType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE); + final int maxTargets = isShortcutResult ? mMaxShortcutTargetsPerApp + : MAX_CHOOSER_TARGETS_PER_APP; + float lastScore = 0; + boolean shouldNotify = false; + for (int i = 0, count = Math.min(targets.size(), maxTargets); i < count; i++) { + final ChooserTarget target = targets.get(i); + float targetScore = target.getScore(); + targetScore *= baseScore; + if (i > 0 && targetScore >= lastScore) { + // Apply a decay so that the top app can't crowd out everything else. + // This incents ChooserTargetServices to define what's truly better. + targetScore = lastScore * 0.95f; + } + boolean isInserted = insertServiceTarget(new SelectableTargetInfo( + mContext, origTarget, target, targetScore, mSelectableTargetInfoComunicator)); + + if (isInserted && isShortcutResult) { + mNumShortcutResults++; + } + + shouldNotify |= isInserted; + + if (DEBUG) { + Log.d(TAG, " => " + target.toString() + " score=" + targetScore + + " base=" + target.getScore() + + " lastScore=" + lastScore + + " baseScore=" + baseScore); + } + + lastScore = targetScore; + } + + if (shouldNotify) { + notifyDataSetChanged(); + } + } + + int getNumShortcutResults() { + return mNumShortcutResults; + } + + /** + * Use the scoring system along with artificial boosts to create up to 4 distinct buckets: + * <ol> + * <li>App-supplied targets + * <li>Shortcuts ranked via App Prediction Manager + * <li>Shortcuts ranked via legacy heuristics + * <li>Legacy direct share targets + * </ol> + */ + public float getBaseScore( + DisplayResolveInfo target, + @ChooserActivity.ShareTargetType int targetType) { + if (target == null) { + return CALLER_TARGET_SCORE_BOOST; + } + + if (targetType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE) { + return SHORTCUT_TARGET_SCORE_BOOST; + } + + float score = super.getScore(target); + if (targetType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER) { + return score * SHORTCUT_TARGET_SCORE_BOOST; + } + + return score; + } + + /** + * Calling this marks service target loading complete, and will attempt to no longer + * update the direct share area. + */ + public void completeServiceTargetLoading() { + mServiceTargets.removeIf(o -> o instanceof ChooserActivity.PlaceHolderTargetInfo); + + if (mServiceTargets.isEmpty()) { + mServiceTargets.add(new ChooserActivity.EmptyTargetInfo()); + } + notifyDataSetChanged(); + } + + private boolean insertServiceTarget(ChooserTargetInfo chooserTargetInfo) { + // Avoid inserting any potentially late results + if (mServiceTargets.size() == 1 + && mServiceTargets.get(0) instanceof ChooserActivity.EmptyTargetInfo) { + return false; + } + + // Check for duplicates and abort if found + for (ChooserTargetInfo otherTargetInfo : mServiceTargets) { + if (chooserTargetInfo.isSimilar(otherTargetInfo)) { + return false; + } + } + + int currentSize = mServiceTargets.size(); + final float newScore = chooserTargetInfo.getModifiedScore(); + for (int i = 0; i < Math.min(currentSize, MAX_SERVICE_TARGETS); i++) { + final ChooserTargetInfo serviceTarget = mServiceTargets.get(i); + if (serviceTarget == null) { + mServiceTargets.set(i, chooserTargetInfo); + return true; + } else if (newScore > serviceTarget.getModifiedScore()) { + mServiceTargets.add(i, chooserTargetInfo); + return true; + } + } + + if (currentSize < MAX_SERVICE_TARGETS) { + mServiceTargets.add(chooserTargetInfo); + return true; + } + + return false; + } + + public ChooserTarget getChooserTargetForValue(int value) { + return mServiceTargets.get(value).getChooserTarget(); + } + + /** + * Rather than fully sorting the input list, this sorting task will put the top k elements + * in the head of input list and fill the tail with other elements in undetermined order. + */ + @Override + AsyncTask<List<ResolvedComponentInfo>, + Void, + List<ResolvedComponentInfo>> createSortingTask() { + return new AsyncTask<List<ResolvedComponentInfo>, + Void, + List<ResolvedComponentInfo>>() { + @Override + protected List<ResolvedComponentInfo> doInBackground( + List<ResolvedComponentInfo>... params) { + mResolverListController.topK(params[0], + mChooserListCommunicator.getMaxRankedTargets()); + return params[0]; + } + @Override + protected void onPostExecute(List<ResolvedComponentInfo> sortedComponents) { + processSortedList(sortedComponents); + mChooserListCommunicator.updateProfileViewButton(); + notifyDataSetChanged(); + } + }; + } + + /** + * Necessary methods to communicate between {@link ChooserListAdapter} + * and {@link ChooserActivity}. + */ + interface ChooserListCommunicator extends ResolverListCommunicator { + + int getMaxRankedTargets(); + + void sendListViewUpdateMessage(); + + boolean isSendAction(Intent targetIntent); + } +} diff --git a/core/java/com/android/internal/app/DumpHeapActivity.java b/core/java/com/android/internal/app/DumpHeapActivity.java deleted file mode 100644 index e04e870a8ec5..000000000000 --- a/core/java/com/android/internal/app/DumpHeapActivity.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2015 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.internal.app; - -import android.app.Activity; -import android.app.ActivityManager; -import android.app.AlertDialog; -import android.content.ActivityNotFoundException; -import android.content.ClipData; -import android.content.DialogInterface; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.util.DebugUtils; -import android.util.Slog; - -/** - * This activity is displayed when the system has collected a heap dump from - * a large process and the user has selected to share it. - */ -public class DumpHeapActivity extends Activity { - /** The process we are reporting */ - public static final String KEY_PROCESS = "process"; - /** The size limit the process reached */ - public static final String KEY_SIZE = "size"; - /** Whether the user initiated the dump or not. */ - public static final String KEY_IS_USER_INITIATED = "is_user_initiated"; - /** Whether the process is a system process (eg: Android System) or not. */ - public static final String KEY_IS_SYSTEM_PROCESS = "is_system_process"; - /** Optional name of package to directly launch */ - public static final String KEY_DIRECT_LAUNCH = "direct_launch"; - - // Broadcast action to determine when to delete the current dump heap data. - public static final String ACTION_DELETE_DUMPHEAP = "com.android.server.am.DELETE_DUMPHEAP"; - - // Extra for above: delay delete of data, since the user is in the process of sharing it. - public static final String EXTRA_DELAY_DELETE = "delay_delete"; - - static final public Uri JAVA_URI = Uri.parse("content://com.android.server.heapdump/java"); - - String mProcess; - long mSize; - AlertDialog mDialog; - boolean mHandled = false; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mProcess = getIntent().getStringExtra(KEY_PROCESS); - mSize = getIntent().getLongExtra(KEY_SIZE, 0); - final boolean isUserInitiated = getIntent().getBooleanExtra(KEY_IS_USER_INITIATED, false); - final boolean isSystemProcess = getIntent().getBooleanExtra(KEY_IS_SYSTEM_PROCESS, false); - - String directLaunch = getIntent().getStringExtra(KEY_DIRECT_LAUNCH); - if (directLaunch != null) { - Intent intent = new Intent(ActivityManager.ACTION_REPORT_HEAP_LIMIT); - intent.setPackage(directLaunch); - ClipData clip = ClipData.newUri(getContentResolver(), "Heap Dump", JAVA_URI); - intent.setClipData(clip); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.setType(clip.getDescription().getMimeType(0)); - intent.putExtra(Intent.EXTRA_STREAM, JAVA_URI); - try { - startActivity(intent); - scheduleDelete(); - mHandled = true; - finish(); - return; - } catch (ActivityNotFoundException e) { - Slog.i("DumpHeapActivity", "Unable to direct launch to " + directLaunch - + ": " + e.getMessage()); - } - } - - final int messageId; - if (isUserInitiated) { - messageId = com.android.internal.R.string.dump_heap_ready_text; - } else if (isSystemProcess) { - messageId = com.android.internal.R.string.dump_heap_system_text; - } else { - messageId = com.android.internal.R.string.dump_heap_text; - } - AlertDialog.Builder b = new AlertDialog.Builder(this, - android.R.style.Theme_Material_Light_Dialog_Alert); - b.setTitle(com.android.internal.R.string.dump_heap_title); - b.setMessage(getString( - messageId, mProcess, DebugUtils.sizeValueToString(mSize, null))); - b.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - mHandled = true; - sendBroadcast(new Intent(ACTION_DELETE_DUMPHEAP)); - finish(); - } - }); - b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - mHandled = true; - scheduleDelete(); - Intent intent = new Intent(Intent.ACTION_SEND); - ClipData clip = ClipData.newUri(getContentResolver(), "Heap Dump", JAVA_URI); - intent.setClipData(clip); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.setType(clip.getDescription().getMimeType(0)); - intent.putExtra(Intent.EXTRA_STREAM, JAVA_URI); - startActivity(Intent.createChooser(intent, - getText(com.android.internal.R.string.dump_heap_title))); - finish(); - } - }); - mDialog = b.show(); - } - - void scheduleDelete() { - Intent broadcast = new Intent(ACTION_DELETE_DUMPHEAP); - broadcast.putExtra(EXTRA_DELAY_DELETE, true); - sendBroadcast(broadcast); - } - - @Override - protected void onStop() { - super.onStop(); - if (!isChangingConfigurations()) { - if (!mHandled) { - sendBroadcast(new Intent(ACTION_DELETE_DUMPHEAP)); - } - } - } - - @Override - protected void onDestroy() { - super.onDestroy(); - mDialog.dismiss(); - } -} diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 74996e9fc212..c1c6ac9dfa89 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -23,7 +23,6 @@ import android.annotation.StringRes; import android.annotation.UiThread; import android.annotation.UnsupportedAppUsage; import android.app.Activity; -import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.ActivityThread; import android.app.VoiceInteractor.PickOptionRequest; @@ -35,26 +34,18 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; -import android.content.pm.LabeledIntent; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.ColorMatrix; -import android.graphics.ColorMatrixColorFilter; import android.graphics.Insets; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; import android.net.Uri; -import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.PatternMatcher; -import android.os.Process; import android.os.RemoteException; import android.os.StrictMode; import android.os.UserHandle; @@ -71,7 +62,6 @@ import android.view.ViewGroup.LayoutParams; import android.view.WindowInsets; import android.widget.AbsListView; import android.widget.AdapterView; -import android.widget.BaseAdapter; import android.widget.Button; import android.widget.ImageView; import android.widget.ListView; @@ -81,6 +71,8 @@ import android.widget.Toast; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.chooser.DisplayResolveInfo; +import com.android.internal.app.chooser.TargetInfo; import com.android.internal.content.PackageMonitor; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; @@ -99,32 +91,33 @@ import java.util.Set; * which to go to. It is not normally used directly by application developers. */ @UiThread -public class ResolverActivity extends Activity { - - // Temporary flag for new chooser delegate behavior. - boolean mEnableChooserDelegate = true; +public class ResolverActivity extends Activity implements + ResolverListAdapter.ResolverListCommunicator { @UnsupportedAppUsage - protected ResolveListAdapter mAdapter; + protected ResolverListAdapter mAdapter; private boolean mSafeForwardingMode; protected AbsListView mAdapterView; private Button mAlwaysButton; private Button mOnceButton; protected View mProfileView; - private int mIconDpi; private int mLastSelected = AbsListView.INVALID_POSITION; private boolean mResolvingHome = false; private int mProfileSwitchMessageId = -1; private int mLayoutId; - private final ArrayList<Intent> mIntents = new ArrayList<>(); + @VisibleForTesting + protected final ArrayList<Intent> mIntents = new ArrayList<>(); private PickTargetOptionRequest mPickOptionRequest; private String mReferrerPackage; private CharSequence mTitle; private int mDefaultTitleResId; - private boolean mUseLayoutForBrowsables; + + @VisibleForTesting + protected boolean mUseLayoutForBrowsables; // Whether or not this activity supports choosing a default handler for the intent. - private boolean mSupportsAlwaysUseOption; + @VisibleForTesting + protected boolean mSupportsAlwaysUseOption; protected ResolverDrawerLayout mResolverDrawerLayout; @UnsupportedAppUsage protected PackageManager mPm; @@ -132,12 +125,9 @@ public class ResolverActivity extends Activity { private static final String TAG = "ResolverActivity"; private static final boolean DEBUG = false; - private Runnable mPostListReadyRunnable; private boolean mRegistered; - private ColorMatrixColorFilter mSuspendedMatrixColorFilter; - protected Insets mSystemWindowInsets = null; private Space mFooterSpacer = null; @@ -233,7 +223,7 @@ public class ResolverActivity extends Activity { @Override public void onSomePackagesChanged() { mAdapter.handlePackagesChanged(); - bindProfileView(); + updateProfileViewButton(); } @Override @@ -316,9 +306,6 @@ public class ResolverActivity extends Activity { mRegistered = true; mReferrerPackage = getReferrerPackageName(); - final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE); - mIconDpi = am.getLauncherLargeIconDensity(); - // Add our initial intent as the first item, regardless of what else has already been added. mIntents.add(0, new Intent(intent)); mTitle = title; @@ -330,7 +317,17 @@ public class ResolverActivity extends Activity { mSupportsAlwaysUseOption = supportsAlwaysUseOption; - if (configureContentView(mIntents, initialIntents, rList)) { + // The last argument of createAdapter is whether to do special handling + // of the last used choice to highlight it in the list. We need to always + // turn this off when running under voice interaction, since it results in + // a more complicated UI that the current voice interaction flow is not able + // to handle. + boolean filterLastUsed = mSupportsAlwaysUseOption && !isVoiceInteraction(); + mAdapter = createAdapter(this, mIntents, initialIntents, rList, + filterLastUsed, mUseLayoutForBrowsables); + configureContentView(); + + if (rebuildList()) { return; } @@ -356,11 +353,9 @@ public class ResolverActivity extends Activity { mProfileView = findViewById(R.id.profile_button); if (mProfileView != null) { mProfileView.setOnClickListener(this::onProfileClick); - bindProfileView(); + updateProfileViewButton(); } - initSuspendedColorMatrix(); - final Set<String> categories = intent.getCategories(); MetricsLogger.action(this, mAdapter.hasFilteredItem() ? MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_APP_FEATURED @@ -423,25 +418,7 @@ public class ResolverActivity extends Activity { } } - private void initSuspendedColorMatrix() { - int grayValue = 127; - float scale = 0.5f; // half bright - - ColorMatrix tempBrightnessMatrix = new ColorMatrix(); - float[] mat = tempBrightnessMatrix.getArray(); - mat[0] = scale; - mat[6] = scale; - mat[12] = scale; - mat[4] = grayValue; - mat[9] = grayValue; - mat[14] = grayValue; - - ColorMatrix matrix = new ColorMatrix(); - matrix.setSaturation(0.0f); - matrix.preConcat(tempBrightnessMatrix); - mSuspendedMatrixColorFilter = new ColorMatrixColorFilter(matrix); - } - + @Override // ResolverListCommunicator public void sendVoiceChoicesIfNeeded() { if (!isVoiceInteraction()) { // Clearly not needed. @@ -476,6 +453,7 @@ public class ResolverActivity extends Activity { } } + @Override // SelectableTargetInfoCommunicator ResolverListCommunicator public Intent getTargetIntent() { return mIntents.isEmpty() ? null : mIntents.get(0); } @@ -492,7 +470,8 @@ public class ResolverActivity extends Activity { return R.layout.resolver_list; } - protected void bindProfileView() { + @Override // ResolverListCommunicator + public void updateProfileViewButton() { if (mProfileView == null) { return; } @@ -583,187 +562,6 @@ public class ResolverActivity extends Activity { } } - - /** - * Loads the icon and label for the provided ApplicationInfo. Defaults to using the application - * icon and label over any IntentFilter or Activity icon to increase user understanding, with an - * exception for applications that hold the right permission. Always attempts to use available - * resources over PackageManager loading mechanisms so badging can be done by iconloader. Uses - * Strings to strip creative formatting. - */ - private abstract static class TargetPresentationGetter { - @Nullable abstract Drawable getIconSubstituteInternal(); - @Nullable abstract String getAppSubLabelInternal(); - - private Context mCtx; - private final int mIconDpi; - private final boolean mHasSubstitutePermission; - private final ApplicationInfo mAi; - - protected PackageManager mPm; - - TargetPresentationGetter(Context ctx, int iconDpi, ApplicationInfo ai) { - mCtx = ctx; - mPm = ctx.getPackageManager(); - mAi = ai; - mIconDpi = iconDpi; - mHasSubstitutePermission = PackageManager.PERMISSION_GRANTED == mPm.checkPermission( - android.Manifest.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON, - mAi.packageName); - } - - public Drawable getIcon(UserHandle userHandle) { - return new BitmapDrawable(mCtx.getResources(), getIconBitmap(userHandle)); - } - - public Bitmap getIconBitmap(UserHandle userHandle) { - Drawable dr = null; - if (mHasSubstitutePermission) { - dr = getIconSubstituteInternal(); - } - - if (dr == null) { - try { - if (mAi.icon != 0) { - dr = loadIconFromResource(mPm.getResourcesForApplication(mAi), mAi.icon); - } - } catch (NameNotFoundException ignore) { - } - } - - // Fall back to ApplicationInfo#loadIcon if nothing has been loaded - if (dr == null) { - dr = mAi.loadIcon(mPm); - } - - SimpleIconFactory sif = SimpleIconFactory.obtain(mCtx); - Bitmap icon = sif.createUserBadgedIconBitmap(dr, userHandle); - sif.recycle(); - - return icon; - } - - public String getLabel() { - String label = null; - // Apps with the substitute permission will always show the sublabel as their label - if (mHasSubstitutePermission) { - label = getAppSubLabelInternal(); - } - - if (label == null) { - label = (String) mAi.loadLabel(mPm); - } - - return label; - } - - public String getSubLabel() { - // Apps with the substitute permission will never have a sublabel - if (mHasSubstitutePermission) return null; - return getAppSubLabelInternal(); - } - - protected String loadLabelFromResource(Resources res, int resId) { - return res.getString(resId); - } - - @Nullable - protected Drawable loadIconFromResource(Resources res, int resId) { - return res.getDrawableForDensity(resId, mIconDpi); - } - - } - - /** - * Loads the icon and label for the provided ResolveInfo. - */ - @VisibleForTesting - public static class ResolveInfoPresentationGetter extends ActivityInfoPresentationGetter { - private final ResolveInfo mRi; - public ResolveInfoPresentationGetter(Context ctx, int iconDpi, ResolveInfo ri) { - super(ctx, iconDpi, ri.activityInfo); - mRi = ri; - } - - @Override - Drawable getIconSubstituteInternal() { - Drawable dr = null; - try { - // Do not use ResolveInfo#getIconResource() as it defaults to the app - if (mRi.resolvePackageName != null && mRi.icon != 0) { - dr = loadIconFromResource( - mPm.getResourcesForApplication(mRi.resolvePackageName), mRi.icon); - } - } catch (NameNotFoundException e) { - Log.e(TAG, "SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON permission granted but " - + "couldn't find resources for package", e); - } - - // Fall back to ActivityInfo if no icon is found via ResolveInfo - if (dr == null) dr = super.getIconSubstituteInternal(); - - return dr; - } - - @Override - String getAppSubLabelInternal() { - // Will default to app name if no intent filter or activity label set, make sure to - // check if subLabel matches label before final display - return (String) mRi.loadLabel(mPm); - } - } - - ResolveInfoPresentationGetter makePresentationGetter(ResolveInfo ri) { - return new ResolveInfoPresentationGetter(this, mIconDpi, ri); - } - - /** - * Loads the icon and label for the provided ActivityInfo. - */ - @VisibleForTesting - public static class ActivityInfoPresentationGetter extends TargetPresentationGetter { - private final ActivityInfo mActivityInfo; - public ActivityInfoPresentationGetter(Context ctx, int iconDpi, - ActivityInfo activityInfo) { - super(ctx, iconDpi, activityInfo.applicationInfo); - mActivityInfo = activityInfo; - } - - @Override - Drawable getIconSubstituteInternal() { - Drawable dr = null; - try { - // Do not use ActivityInfo#getIconResource() as it defaults to the app - if (mActivityInfo.icon != 0) { - dr = loadIconFromResource( - mPm.getResourcesForApplication(mActivityInfo.applicationInfo), - mActivityInfo.icon); - } - } catch (NameNotFoundException e) { - Log.e(TAG, "SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON permission granted but " - + "couldn't find resources for package", e); - } - - return dr; - } - - @Override - String getAppSubLabelInternal() { - // Will default to app name if no activity label set, make sure to check if subLabel - // matches label before final display - return (String) mActivityInfo.loadLabel(mPm); - } - } - - protected ActivityInfoPresentationGetter makePresentationGetter(ActivityInfo ai) { - return new ActivityInfoPresentationGetter(this, mIconDpi, ai); - } - - Drawable loadIconForResolveInfo(ResolveInfo ri) { - // Load icons based on the current process. If in work profile icons should be badged. - return makePresentationGetter(ri).getIcon(Process.myUserHandle()); - } - @Override protected void onRestart() { super.onRestart(); @@ -772,7 +570,7 @@ public class ResolverActivity extends Activity { mRegistered = true; } mAdapter.handlePackagesChanged(); - bindProfileView(); + updateProfileViewButton(); } @Override @@ -804,12 +602,8 @@ public class ResolverActivity extends Activity { if (!isChangingConfigurations() && mPickOptionRequest != null) { mPickOptionRequest.cancel(); } - if (mPostListReadyRunnable != null) { - getMainThreadHandler().removeCallbacks(mPostListReadyRunnable); - mPostListReadyRunnable = null; - } - if (mAdapter != null && mAdapter.mResolverListController != null) { - mAdapter.mResolverListController.destroy(); + if (mAdapter != null) { + mAdapter.onDestroy(); } } @@ -950,10 +744,30 @@ public class ResolverActivity extends Activity { /** * Replace me in subclasses! */ + @Override // ResolverListCommunicator public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) { return defIntent; } + @Override // ResolverListCommunicator + public void onPostListReady() { + setHeader(); + resetButtonBar(); + onListRebuilt(); + } + + protected void onListRebuilt() { + int count = mAdapter.getUnfilteredCount(); + if (count == 1 && mAdapter.getOtherProfile() == null) { + // Only one target, so we're a candidate to auto-launch! + final TargetInfo target = mAdapter.targetInfoForPosition(0, false); + if (shouldAutoLaunchSingleChoice(target)) { + safelyStartActivity(target); + finish(); + } + } + } + protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) { final ResolveInfo ri = target.getResolveInfo(); final Intent intent = target != null ? target.getResolvedIntent() : null; @@ -1049,8 +863,8 @@ public class ResolverActivity extends Activity { // If we don't add back in the component for forwarding the intent to a managed // profile, the preferred activity may not be updated correctly (as the set of // components we tell it we knew about will have changed). - final boolean needToAddBackProfileForwardingComponent - = mAdapter.mOtherProfile != null; + final boolean needToAddBackProfileForwardingComponent = + mAdapter.getOtherProfile() != null; if (!needToAddBackProfileForwardingComponent) { set = new ComponentName[N]; } else { @@ -1066,8 +880,8 @@ public class ResolverActivity extends Activity { } if (needToAddBackProfileForwardingComponent) { - set[N] = mAdapter.mOtherProfile.getResolvedComponentName(); - final int otherProfileMatch = mAdapter.mOtherProfile.getResolveInfo().match; + set[N] = mAdapter.getOtherProfile().getResolvedComponentName(); + final int otherProfileMatch = mAdapter.getOtherProfile().getResolveInfo().match; if (otherProfileMatch > bestMatch) bestMatch = otherProfileMatch; } @@ -1169,7 +983,7 @@ public class ResolverActivity extends Activity { } - boolean startAsCallerImpl(Intent intent, Bundle options, boolean ignoreTargetSecurity, + public boolean startAsCallerImpl(Intent intent, Bundle options, boolean ignoreTargetSecurity, int userId) { // Pass intent to delegate chooser activity with permission token. // TODO: This should move to a trampoline Activity in the system when the ChooserActivity @@ -1205,6 +1019,7 @@ public class ResolverActivity extends Activity { // Do nothing } + @Override // ResolverListCommunicator public boolean shouldGetActivityMetadata() { return false; } @@ -1220,11 +1035,11 @@ public class ResolverActivity extends Activity { startActivity(in); } - public ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents, - Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid, - boolean filterLastUsed) { - return new ResolveListAdapter(context, payloadIntents, initialIntents, rList, - launchedFromUid, filterLastUsed, createListController()); + public ResolverListAdapter createAdapter(Context context, List<Intent> payloadIntents, + Intent[] initialIntents, List<ResolveInfo> rList, + boolean filterLastUsed, boolean useLayoutForBrowsables) { + return new ResolverListAdapter(context, payloadIntents, initialIntents, rList, + filterLastUsed, createListController(), useLayoutForBrowsables, this); } @VisibleForTesting @@ -1238,26 +1053,34 @@ public class ResolverActivity extends Activity { } /** - * Returns true if the activity is finishing and creation should halt + * Sets up the content view. */ - public boolean configureContentView(List<Intent> payloadIntents, Intent[] initialIntents, - List<ResolveInfo> rList) { - // The last argument of createAdapter is whether to do special handling - // of the last used choice to highlight it in the list. We need to always - // turn this off when running under voice interaction, since it results in - // a more complicated UI that the current voice interaction flow is not able - // to handle. - mAdapter = createAdapter(this, payloadIntents, initialIntents, rList, - mLaunchedFromUid, mSupportsAlwaysUseOption && !isVoiceInteraction()); - boolean rebuildCompleted = mAdapter.rebuildList(); - + private void configureContentView() { + if (mAdapter == null) { + throw new IllegalStateException("mAdapter cannot be null."); + } if (useLayoutWithDefault()) { mLayoutId = R.layout.resolver_list_with_default; } else { mLayoutId = getLayoutResource(); } setContentView(mLayoutId); + mAdapterView = findViewById(R.id.resolver_list); + } + + /** + * Returns true if the activity is finishing and creation should halt. + * </p>Subclasses must call rebuildListInternal at the end of rebuildList. + */ + protected boolean rebuildList() { + return rebuildListInternal(); + } + /** + * Returns true if the activity is finishing and creation should halt. + */ + final boolean rebuildListInternal() { + boolean rebuildCompleted = mAdapter.rebuildList(); int count = mAdapter.getUnfilteredCount(); // We only rebuild asynchronously when we have multiple elements to sort. In the case where @@ -1276,10 +1099,7 @@ public class ResolverActivity extends Activity { } } - - mAdapterView = findViewById(R.id.resolver_list); - - if (count == 0 && mAdapter.mPlaceholderCount == 0) { + if (count == 0 && mAdapter.getPlaceholderCount() == 0) { final TextView emptyView = findViewById(R.id.empty); emptyView.setVisibility(View.VISIBLE); mAdapterView.setVisibility(View.GONE); @@ -1290,7 +1110,7 @@ public class ResolverActivity extends Activity { return false; } - public void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter) { + public void onPrepareAdapterView(AbsListView adapterView, ResolverListAdapter adapter) { final boolean useHeader = adapter.hasFilteredItem(); final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null; @@ -1317,7 +1137,7 @@ public class ResolverActivity extends Activity { * Configure the area above the app selection list (title, content preview, etc). */ public void setHeader() { - if (mAdapter.getCount() == 0 && mAdapter.mPlaceholderCount == 0) { + if (mAdapter.getCount() == 0 && mAdapter.getPlaceholderCount() == 0) { final TextView titleView = findViewById(R.id.title); if (titleView != null) { titleView.setVisibility(View.GONE); @@ -1337,9 +1157,8 @@ public class ResolverActivity extends Activity { } final ImageView iconView = findViewById(R.id.icon); - final DisplayResolveInfo iconInfo = mAdapter.getFilteredItem(); - if (iconView != null && iconInfo != null) { - new LoadIconTask(iconInfo, iconView).execute(); + if (iconView != null) { + mAdapter.loadFilteredItemIconTaskAsync(iconView); } } @@ -1382,7 +1201,8 @@ public class ResolverActivity extends Activity { } } - private boolean useLayoutWithDefault() { + @Override // ResolverListCommunicator + public boolean useLayoutWithDefault() { return mSupportsAlwaysUseOption && mAdapter.hasFilteredItem(); } @@ -1397,706 +1217,22 @@ public class ResolverActivity extends Activity { /** * Check a simple match for the component of two ResolveInfos. */ - static boolean resolveInfoMatch(ResolveInfo lhs, ResolveInfo rhs) { + @Override // ResolverListCommunicator + public boolean resolveInfoMatch(ResolveInfo lhs, ResolveInfo rhs) { return lhs == null ? rhs == null : lhs.activityInfo == null ? rhs.activityInfo == null : Objects.equals(lhs.activityInfo.name, rhs.activityInfo.name) && Objects.equals(lhs.activityInfo.packageName, rhs.activityInfo.packageName); } - public final class DisplayResolveInfo implements TargetInfo { - private final ResolveInfo mResolveInfo; - private CharSequence mDisplayLabel; - private Drawable mDisplayIcon; - private Drawable mBadge; - private CharSequence mExtendedInfo; - private final Intent mResolvedIntent; - private final List<Intent> mSourceIntents = new ArrayList<>(); - private boolean mIsSuspended; - - public DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, Intent pOrigIntent) { - this(originalIntent, pri, null /*mDisplayLabel*/, null /*mExtendedInfo*/, pOrigIntent); - } - - public DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, CharSequence pLabel, - CharSequence pInfo, Intent pOrigIntent) { - mSourceIntents.add(originalIntent); - mResolveInfo = pri; - mDisplayLabel = pLabel; - mExtendedInfo = pInfo; - - final Intent intent = new Intent(pOrigIntent != null ? pOrigIntent : - getReplacementIntent(pri.activityInfo, getTargetIntent())); - intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT - | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); - final ActivityInfo ai = mResolveInfo.activityInfo; - intent.setComponent(new ComponentName(ai.applicationInfo.packageName, ai.name)); - - mIsSuspended = (ai.applicationInfo.flags & ApplicationInfo.FLAG_SUSPENDED) != 0; - - mResolvedIntent = intent; - } - - private DisplayResolveInfo(DisplayResolveInfo other, Intent fillInIntent, int flags) { - mSourceIntents.addAll(other.getAllSourceIntents()); - mResolveInfo = other.mResolveInfo; - mDisplayLabel = other.mDisplayLabel; - mDisplayIcon = other.mDisplayIcon; - mExtendedInfo = other.mExtendedInfo; - mResolvedIntent = new Intent(other.mResolvedIntent); - mResolvedIntent.fillIn(fillInIntent, flags); - } - - public ResolveInfo getResolveInfo() { - return mResolveInfo; - } - - public CharSequence getDisplayLabel() { - if (mDisplayLabel == null) { - ResolveInfoPresentationGetter pg = makePresentationGetter(mResolveInfo); - mDisplayLabel = pg.getLabel(); - mExtendedInfo = pg.getSubLabel(); - } - return mDisplayLabel; - } - - public boolean hasDisplayLabel() { - return mDisplayLabel != null; - } - - public void setDisplayLabel(CharSequence displayLabel) { - mDisplayLabel = displayLabel; - } - - public void setExtendedInfo(CharSequence extendedInfo) { - mExtendedInfo = extendedInfo; - } - - public Drawable getDisplayIcon() { - return mDisplayIcon; - } - - @Override - public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { - return new DisplayResolveInfo(this, fillInIntent, flags); - } - - @Override - public List<Intent> getAllSourceIntents() { - return mSourceIntents; - } - - public void addAlternateSourceIntent(Intent alt) { - mSourceIntents.add(alt); - } - - public void setDisplayIcon(Drawable icon) { - mDisplayIcon = icon; - } - - public boolean hasDisplayIcon() { - return mDisplayIcon != null; - } - - public CharSequence getExtendedInfo() { - return mExtendedInfo; - } - - public Intent getResolvedIntent() { - return mResolvedIntent; - } - - @Override - public ComponentName getResolvedComponentName() { - return new ComponentName(mResolveInfo.activityInfo.packageName, - mResolveInfo.activityInfo.name); - } - - @Override - public boolean start(Activity activity, Bundle options) { - activity.startActivity(mResolvedIntent, options); - return true; - } - - @Override - public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) { - if (mEnableChooserDelegate) { - return activity.startAsCallerImpl(mResolvedIntent, options, false, userId); - } else { - activity.startActivityAsCaller(mResolvedIntent, options, null, false, userId); - return true; - } - } - - @Override - public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { - activity.startActivityAsUser(mResolvedIntent, options, user); - return false; - } - - public boolean isSuspended() { - return mIsSuspended; - } - } - - List<DisplayResolveInfo> getDisplayList() { - return mAdapter.mDisplayList; - } - - /** - * A single target as represented in the chooser. - */ - public interface TargetInfo { - /** - * Get the resolved intent that represents this target. Note that this may not be the - * intent that will be launched by calling one of the <code>start</code> methods provided; - * this is the intent that will be credited with the launch. - * - * @return the resolved intent for this target - */ - Intent getResolvedIntent(); - - /** - * Get the resolved component name that represents this target. Note that this may not - * be the component that will be directly launched by calling one of the <code>start</code> - * methods provided; this is the component that will be credited with the launch. - * - * @return the resolved ComponentName for this target - */ - ComponentName getResolvedComponentName(); - - /** - * Start the activity referenced by this target. - * - * @param activity calling Activity performing the launch - * @param options ActivityOptions bundle - * @return true if the start completed successfully - */ - boolean start(Activity activity, Bundle options); - - /** - * Start the activity referenced by this target as if the ResolverActivity's caller - * was performing the start operation. - * - * @param activity calling Activity (actually) performing the launch - * @param options ActivityOptions bundle - * @param userId userId to start as or {@link UserHandle#USER_NULL} for activity's caller - * @return true if the start completed successfully - */ - boolean startAsCaller(ResolverActivity activity, Bundle options, int userId); - - /** - * Start the activity referenced by this target as a given user. - * - * @param activity calling activity performing the launch - * @param options ActivityOptions bundle - * @param user handle for the user to start the activity as - * @return true if the start completed successfully - */ - boolean startAsUser(Activity activity, Bundle options, UserHandle user); - - /** - * Return the ResolveInfo about how and why this target matched the original query - * for available targets. - * - * @return ResolveInfo representing this target's match - */ - ResolveInfo getResolveInfo(); - - /** - * Return the human-readable text label for this target. - * - * @return user-visible target label - */ - CharSequence getDisplayLabel(); - - /** - * Return any extended info for this target. This may be used to disambiguate - * otherwise identical targets. - * - * @return human-readable disambig string or null if none present - */ - CharSequence getExtendedInfo(); - - /** - * @return The drawable that should be used to represent this target including badge - */ - Drawable getDisplayIcon(); - - /** - * Clone this target with the given fill-in information. - */ - TargetInfo cloneFilledIn(Intent fillInIntent, int flags); - - /** - * @return the list of supported source intents deduped against this single target - */ - List<Intent> getAllSourceIntents(); - - /** - * @return true if this target can be selected by the user - */ - boolean isSuspended(); - } - - public class ResolveListAdapter extends BaseAdapter { - private final List<Intent> mIntents; - private final Intent[] mInitialIntents; - private final List<ResolveInfo> mBaseResolveList; - protected ResolveInfo mLastChosen; - private DisplayResolveInfo mOtherProfile; - ResolverListController mResolverListController; - private int mPlaceholderCount; - private boolean mAllTargetsAreBrowsers = false; - - protected final LayoutInflater mInflater; - - // This one is the list that the Adapter will actually present. - List<DisplayResolveInfo> mDisplayList; - List<ResolvedComponentInfo> mUnfilteredResolveList; - - private int mLastChosenPosition = -1; - private boolean mFilterLastUsed; - - public ResolveListAdapter(Context context, List<Intent> payloadIntents, - Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid, - boolean filterLastUsed, - ResolverListController resolverListController) { - mIntents = payloadIntents; - mInitialIntents = initialIntents; - mBaseResolveList = rList; - mLaunchedFromUid = launchedFromUid; - mInflater = LayoutInflater.from(context); - mDisplayList = new ArrayList<>(); - mFilterLastUsed = filterLastUsed; - mResolverListController = resolverListController; - } - - public void handlePackagesChanged() { - rebuildList(); - if (getCount() == 0) { - // We no longer have any items... just finish the activity. - finish(); - } - } - - public void setPlaceholderCount(int count) { - mPlaceholderCount = count; - } - - public int getPlaceholderCount() { return mPlaceholderCount; } - - @Nullable - public DisplayResolveInfo getFilteredItem() { - if (mFilterLastUsed && mLastChosenPosition >= 0) { - // Not using getItem since it offsets to dodge this position for the list - return mDisplayList.get(mLastChosenPosition); - } - return null; - } - - public DisplayResolveInfo getOtherProfile() { - return mOtherProfile; - } - - public int getFilteredPosition() { - if (mFilterLastUsed && mLastChosenPosition >= 0) { - return mLastChosenPosition; - } - return AbsListView.INVALID_POSITION; - } - - public boolean hasFilteredItem() { - return mFilterLastUsed && mLastChosen != null; - } - - public float getScore(DisplayResolveInfo target) { - return mResolverListController.getScore(target); - } - - public void updateModel(ComponentName componentName) { - mResolverListController.updateModel(componentName); - } - - public void updateChooserCounts(String packageName, int userId, String action) { - mResolverListController.updateChooserCounts(packageName, userId, action); - } - - /** - * @return true if all items in the display list are defined as browsers by - * ResolveInfo.handleAllWebDataURI - */ - public boolean areAllTargetsBrowsers() { - return mAllTargetsAreBrowsers; - } - - /** - * Rebuild the list of resolvers. In some cases some parts will need some asynchronous work - * to complete. - * - * @return Whether or not the list building is completed. - */ - protected boolean rebuildList() { - List<ResolvedComponentInfo> currentResolveList = null; - // Clear the value of mOtherProfile from previous call. - mOtherProfile = null; - mLastChosen = null; - mLastChosenPosition = -1; - mAllTargetsAreBrowsers = false; - mDisplayList.clear(); - if (mBaseResolveList != null) { - currentResolveList = mUnfilteredResolveList = new ArrayList<>(); - mResolverListController.addResolveListDedupe(currentResolveList, - getTargetIntent(), - mBaseResolveList); - } else { - currentResolveList = mUnfilteredResolveList = - mResolverListController.getResolversForIntent(shouldGetResolvedFilter(), - shouldGetActivityMetadata(), - mIntents); - if (currentResolveList == null) { - processSortedList(currentResolveList); - return true; - } - List<ResolvedComponentInfo> originalList = - mResolverListController.filterIneligibleActivities(currentResolveList, - true); - if (originalList != null) { - mUnfilteredResolveList = originalList; - } - } - - // So far we only support a single other profile at a time. - // The first one we see gets special treatment. - for (ResolvedComponentInfo info : currentResolveList) { - if (info.getResolveInfoAt(0).targetUserId != UserHandle.USER_CURRENT) { - mOtherProfile = new DisplayResolveInfo(info.getIntentAt(0), - info.getResolveInfoAt(0), - info.getResolveInfoAt(0).loadLabel(mPm), - info.getResolveInfoAt(0).loadLabel(mPm), - getReplacementIntent(info.getResolveInfoAt(0).activityInfo, - info.getIntentAt(0))); - currentResolveList.remove(info); - break; - } - } - - if (mOtherProfile == null) { - try { - mLastChosen = mResolverListController.getLastChosen(); - } catch (RemoteException re) { - Log.d(TAG, "Error calling getLastChosenActivity\n" + re); - } - } - - int N; - if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) { - // We only care about fixing the unfilteredList if the current resolve list and - // current resolve list are currently the same. - List<ResolvedComponentInfo> originalList = - mResolverListController.filterLowPriority(currentResolveList, - mUnfilteredResolveList == currentResolveList); - if (originalList != null) { - mUnfilteredResolveList = originalList; - } - - if (currentResolveList.size() > 1) { - int placeholderCount = currentResolveList.size(); - if (useLayoutWithDefault()) { - --placeholderCount; - } - setPlaceholderCount(placeholderCount); - createSortingTask().execute(currentResolveList); - postListReadyRunnable(); - return false; - } else { - processSortedList(currentResolveList); - return true; - } - } else { - processSortedList(currentResolveList); - return true; - } - } - - AsyncTask<List<ResolvedComponentInfo>, - Void, - List<ResolvedComponentInfo>> createSortingTask() { - return new AsyncTask<List<ResolvedComponentInfo>, - Void, - List<ResolvedComponentInfo>>() { - @Override - protected List<ResolvedComponentInfo> doInBackground( - List<ResolvedComponentInfo>... params) { - mResolverListController.sort(params[0]); - return params[0]; - } - - @Override - protected void onPostExecute(List<ResolvedComponentInfo> sortedComponents) { - processSortedList(sortedComponents); - bindProfileView(); - notifyDataSetChanged(); - } - }; - } - - void processSortedList(List<ResolvedComponentInfo> sortedComponents) { - int N; - if (sortedComponents != null && (N = sortedComponents.size()) != 0) { - mAllTargetsAreBrowsers = mUseLayoutForBrowsables; - - // First put the initial items at the top. - if (mInitialIntents != null) { - for (int i = 0; i < mInitialIntents.length; i++) { - Intent ii = mInitialIntents[i]; - if (ii == null) { - continue; - } - ActivityInfo ai = ii.resolveActivityInfo( - getPackageManager(), 0); - if (ai == null) { - Log.w(TAG, "No activity found for " + ii); - continue; - } - ResolveInfo ri = new ResolveInfo(); - ri.activityInfo = ai; - UserManager userManager = - (UserManager) getSystemService(Context.USER_SERVICE); - if (ii instanceof LabeledIntent) { - LabeledIntent li = (LabeledIntent) ii; - ri.resolvePackageName = li.getSourcePackage(); - ri.labelRes = li.getLabelResource(); - ri.nonLocalizedLabel = li.getNonLocalizedLabel(); - ri.icon = li.getIconResource(); - ri.iconResourceId = ri.icon; - } - if (userManager.isManagedProfile()) { - ri.noResourceId = true; - ri.icon = 0; - } - mAllTargetsAreBrowsers &= ri.handleAllWebDataURI; - - addResolveInfo(new DisplayResolveInfo(ii, ri, - ri.loadLabel(getPackageManager()), null, ii)); - } - } - - - for (ResolvedComponentInfo rci : sortedComponents) { - final ResolveInfo ri = rci.getResolveInfoAt(0); - if (ri != null) { - mAllTargetsAreBrowsers &= ri.handleAllWebDataURI; - addResolveInfoWithAlternates(rci); - } - } - } - - sendVoiceChoicesIfNeeded(); - postListReadyRunnable(); - } - - - - /** - * Some necessary methods for creating the list are initiated in onCreate and will also - * determine the layout known. We therefore can't update the UI inline and post to the - * handler thread to update after the current task is finished. - */ - private void postListReadyRunnable() { - if (mPostListReadyRunnable == null) { - mPostListReadyRunnable = new Runnable() { - @Override - public void run() { - setHeader(); - resetButtonBar(); - onListRebuilt(); - mPostListReadyRunnable = null; - } - }; - getMainThreadHandler().post(mPostListReadyRunnable); - } - } - - public void onListRebuilt() { - int count = getUnfilteredCount(); - if (count == 1 && getOtherProfile() == null) { - // Only one target, so we're a candidate to auto-launch! - final TargetInfo target = targetInfoForPosition(0, false); - if (shouldAutoLaunchSingleChoice(target)) { - safelyStartActivity(target); - finish(); - } - } - } - - public boolean shouldGetResolvedFilter() { - return mFilterLastUsed; - } - - private void addResolveInfoWithAlternates(ResolvedComponentInfo rci) { - final int count = rci.getCount(); - final Intent intent = rci.getIntentAt(0); - final ResolveInfo add = rci.getResolveInfoAt(0); - final Intent replaceIntent = getReplacementIntent(add.activityInfo, intent); - final DisplayResolveInfo dri = new DisplayResolveInfo(intent, add, replaceIntent); - addResolveInfo(dri); - if (replaceIntent == intent) { - // Only add alternates if we didn't get a specific replacement from - // the caller. If we have one it trumps potential alternates. - for (int i = 1, N = count; i < N; i++) { - final Intent altIntent = rci.getIntentAt(i); - dri.addAlternateSourceIntent(altIntent); - } - } - updateLastChosenPosition(add); - } - - private void updateLastChosenPosition(ResolveInfo info) { - // If another profile is present, ignore the last chosen entry. - if (mOtherProfile != null) { - mLastChosenPosition = -1; - return; - } - if (mLastChosen != null - && mLastChosen.activityInfo.packageName.equals(info.activityInfo.packageName) - && mLastChosen.activityInfo.name.equals(info.activityInfo.name)) { - mLastChosenPosition = mDisplayList.size() - 1; - } - } - - // We assume that at this point we've already filtered out the only intent for a different - // targetUserId which we're going to use. - private void addResolveInfo(DisplayResolveInfo dri) { - if (dri != null && dri.mResolveInfo != null - && dri.mResolveInfo.targetUserId == UserHandle.USER_CURRENT) { - // Checks if this info is already listed in display. - for (DisplayResolveInfo existingInfo : mDisplayList) { - if (resolveInfoMatch(dri.mResolveInfo, existingInfo.mResolveInfo)) { - return; - } - } - mDisplayList.add(dri); - } - } - - @Nullable - public ResolveInfo resolveInfoForPosition(int position, boolean filtered) { - TargetInfo target = targetInfoForPosition(position, filtered); - if (target != null) { - return target.getResolveInfo(); - } - return null; - } - - @Nullable - public TargetInfo targetInfoForPosition(int position, boolean filtered) { - if (filtered) { - return getItem(position); - } - if (mDisplayList.size() > position) { - return mDisplayList.get(position); - } - return null; - } - - public int getCount() { - int totalSize = mDisplayList == null || mDisplayList.isEmpty() ? mPlaceholderCount : - mDisplayList.size(); - if (mFilterLastUsed && mLastChosenPosition >= 0) { - totalSize--; - } - return totalSize; - } - - public int getUnfilteredCount() { - return mDisplayList.size(); - } - - @Nullable - public TargetInfo getItem(int position) { - if (mFilterLastUsed && mLastChosenPosition >= 0 && position >= mLastChosenPosition) { - position++; - } - if (mDisplayList.size() > position) { - return mDisplayList.get(position); - } else { - return null; - } - } - - public long getItemId(int position) { - return position; - } - - public int getDisplayResolveInfoCount() { - return mDisplayList.size(); - } - - public DisplayResolveInfo getDisplayResolveInfo(int index) { - // Used to query services. We only query services for primary targets, not alternates. - return mDisplayList.get(index); - } - - public final View getView(int position, View convertView, ViewGroup parent) { - View view = convertView; - if (view == null) { - view = createView(parent); - } - onBindView(view, getItem(position)); - return view; - } - - public final View createView(ViewGroup parent) { - final View view = onCreateView(parent); - final ViewHolder holder = new ViewHolder(view); - view.setTag(holder); - return view; - } - - public View onCreateView(ViewGroup parent) { - return mInflater.inflate( - com.android.internal.R.layout.resolve_list_item, parent, false); - } - - public final void bindView(int position, View view) { - onBindView(view, getItem(position)); - } - - protected void onBindView(View view, TargetInfo info) { - final ViewHolder holder = (ViewHolder) view.getTag(); - if (info == null) { - holder.icon.setImageDrawable( - getDrawable(R.drawable.resolver_icon_placeholder)); - return; - } - - if (info instanceof DisplayResolveInfo - && !((DisplayResolveInfo) info).hasDisplayLabel()) { - getLoadLabelTask((DisplayResolveInfo) info, holder).execute(); - } else { - holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo()); - } - - if (info.isSuspended()) { - holder.icon.setColorFilter(mSuspendedMatrixColorFilter); - } else { - holder.icon.setColorFilter(null); - } - - if (info instanceof DisplayResolveInfo - && !((DisplayResolveInfo) info).hasDisplayIcon()) { - new LoadIconTask((DisplayResolveInfo) info, holder.icon).execute(); - } else { - holder.icon.setImageDrawable(info.getDisplayIcon()); - } + @Override // ResolverListCommunicator + public void onHandlePackagesChanged() { + if (mAdapter.getCount() == 0) { + // We no longer have any items... just finish the activity. + finish(); } } - protected LoadLabelTask getLoadLabelTask(DisplayResolveInfo info, ViewHolder holder) { - return new LoadLabelTask(info, holder); - } - @VisibleForTesting public static final class ResolvedComponentInfo { public final ComponentName name; @@ -2144,41 +1280,6 @@ public class ResolverActivity extends Activity { } } - static class ViewHolder { - public View itemView; - public Drawable defaultItemViewBackground; - - public TextView text; - public TextView text2; - public ImageView icon; - - public ViewHolder(View view) { - itemView = view; - defaultItemViewBackground = view.getBackground(); - text = (TextView) view.findViewById(com.android.internal.R.id.text1); - text2 = (TextView) view.findViewById(com.android.internal.R.id.text2); - icon = (ImageView) view.findViewById(R.id.icon); - } - - public void bindLabel(CharSequence label, CharSequence subLabel) { - if (!TextUtils.equals(text.getText(), label)) { - text.setText(label); - } - - // Always show a subLabel for visual consistency across list items. Show an empty - // subLabel if the subLabel is the same as the label - if (TextUtils.equals(label, subLabel)) { - subLabel = null; - } - - if (!TextUtils.equals(text2.getText(), subLabel) - && !TextUtils.isEmpty(subLabel)) { - text2.setVisibility(View.VISIBLE); - text2.setText(subLabel); - } - } - } - class ItemClickListener implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener { @Override @@ -2229,61 +1330,6 @@ public class ResolverActivity extends Activity { } - protected class LoadLabelTask extends AsyncTask<Void, Void, CharSequence[]> { - private final DisplayResolveInfo mDisplayResolveInfo; - private final ViewHolder mHolder; - - protected LoadLabelTask(DisplayResolveInfo dri, ViewHolder holder) { - mDisplayResolveInfo = dri; - mHolder = holder; - } - - - @Override - protected CharSequence[] doInBackground(Void... voids) { - ResolveInfoPresentationGetter pg = - makePresentationGetter(mDisplayResolveInfo.mResolveInfo); - return new CharSequence[] { - pg.getLabel(), - pg.getSubLabel() - }; - } - - @Override - protected void onPostExecute(CharSequence[] result) { - mDisplayResolveInfo.setDisplayLabel(result[0]); - mDisplayResolveInfo.setExtendedInfo(result[1]); - mHolder.bindLabel(result[0], result[1]); - } - } - - class LoadIconTask extends AsyncTask<Void, Void, Drawable> { - protected final DisplayResolveInfo mDisplayResolveInfo; - private final ResolveInfo mResolveInfo; - private final ImageView mTargetView; - - LoadIconTask(DisplayResolveInfo dri, ImageView target) { - mDisplayResolveInfo = dri; - mResolveInfo = dri.getResolveInfo(); - mTargetView = target; - } - - @Override - protected Drawable doInBackground(Void... params) { - return loadIconForResolveInfo(mResolveInfo); - } - - @Override - protected void onPostExecute(Drawable d) { - if (mAdapter.getOtherProfile() == mDisplayResolveInfo) { - bindProfileView(); - } else { - mDisplayResolveInfo.setDisplayIcon(d); - mTargetView.setImageDrawable(d); - } - } - } - static final boolean isSpecificUriMatch(int match) { match = match&IntentFilter.MATCH_CATEGORY_MASK; return match >= IntentFilter.MATCH_CATEGORY_HOST diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java new file mode 100644 index 000000000000..4076ddaa71b0 --- /dev/null +++ b/core/java/com/android/internal/app/ResolverListAdapter.java @@ -0,0 +1,862 @@ +/* + * 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.internal.app; + +import static android.content.Context.ACTIVITY_SERVICE; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.LabeledIntent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.os.Process; +import android.os.RemoteException; +import android.os.UserHandle; +import android.os.UserManager; +import android.text.TextUtils; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; +import com.android.internal.app.chooser.DisplayResolveInfo; +import com.android.internal.app.chooser.TargetInfo; + +import java.util.ArrayList; +import java.util.List; + +public class ResolverListAdapter extends BaseAdapter { + private static final String TAG = "ResolverListAdapter"; + + private final List<Intent> mIntents; + private final Intent[] mInitialIntents; + private final List<ResolveInfo> mBaseResolveList; + private final PackageManager mPm; + protected final Context mContext; + private final ColorMatrixColorFilter mSuspendedMatrixColorFilter; + private final boolean mUseLayoutForBrowsables; + private final int mIconDpi; + protected ResolveInfo mLastChosen; + private DisplayResolveInfo mOtherProfile; + ResolverListController mResolverListController; + private int mPlaceholderCount; + private boolean mAllTargetsAreBrowsers = false; + + protected final LayoutInflater mInflater; + + // This one is the list that the Adapter will actually present. + List<DisplayResolveInfo> mDisplayList; + List<ResolvedComponentInfo> mUnfilteredResolveList; + + private int mLastChosenPosition = -1; + private boolean mFilterLastUsed; + private final ResolverListCommunicator mResolverListCommunicator; + private Runnable mPostListReadyRunnable; + + public ResolverListAdapter(Context context, List<Intent> payloadIntents, + Intent[] initialIntents, List<ResolveInfo> rList, + boolean filterLastUsed, + ResolverListController resolverListController, + boolean useLayoutForBrowsables, + ResolverListCommunicator resolverListCommunicator) { + mContext = context; + mIntents = payloadIntents; + mInitialIntents = initialIntents; + mBaseResolveList = rList; + mInflater = LayoutInflater.from(context); + mPm = context.getPackageManager(); + mDisplayList = new ArrayList<>(); + mFilterLastUsed = filterLastUsed; + mResolverListController = resolverListController; + mSuspendedMatrixColorFilter = createSuspendedColorMatrix(); + mUseLayoutForBrowsables = useLayoutForBrowsables; + mResolverListCommunicator = resolverListCommunicator; + final ActivityManager am = (ActivityManager) mContext.getSystemService(ACTIVITY_SERVICE); + mIconDpi = am.getLauncherLargeIconDensity(); + } + + public void handlePackagesChanged() { + rebuildList(); + mResolverListCommunicator.onHandlePackagesChanged(); + } + + public void setPlaceholderCount(int count) { + mPlaceholderCount = count; + } + + public int getPlaceholderCount() { + return mPlaceholderCount; + } + + @Nullable + public DisplayResolveInfo getFilteredItem() { + if (mFilterLastUsed && mLastChosenPosition >= 0) { + // Not using getItem since it offsets to dodge this position for the list + return mDisplayList.get(mLastChosenPosition); + } + return null; + } + + public DisplayResolveInfo getOtherProfile() { + return mOtherProfile; + } + + public int getFilteredPosition() { + if (mFilterLastUsed && mLastChosenPosition >= 0) { + return mLastChosenPosition; + } + return AbsListView.INVALID_POSITION; + } + + public boolean hasFilteredItem() { + return mFilterLastUsed && mLastChosen != null; + } + + public float getScore(DisplayResolveInfo target) { + return mResolverListController.getScore(target); + } + + public void updateModel(ComponentName componentName) { + mResolverListController.updateModel(componentName); + } + + public void updateChooserCounts(String packageName, int userId, String action) { + mResolverListController.updateChooserCounts(packageName, userId, action); + } + + /** + * @return true if all items in the display list are defined as browsers by + * ResolveInfo.handleAllWebDataURI + */ + public boolean areAllTargetsBrowsers() { + return mAllTargetsAreBrowsers; + } + + /** + * Rebuild the list of resolvers. In some cases some parts will need some asynchronous work + * to complete. + * + * @return Whether or not the list building is completed. + */ + protected boolean rebuildList() { + List<ResolvedComponentInfo> currentResolveList = null; + // Clear the value of mOtherProfile from previous call. + mOtherProfile = null; + mLastChosen = null; + mLastChosenPosition = -1; + mAllTargetsAreBrowsers = false; + mDisplayList.clear(); + if (mBaseResolveList != null) { + currentResolveList = mUnfilteredResolveList = new ArrayList<>(); + mResolverListController.addResolveListDedupe(currentResolveList, + mResolverListCommunicator.getTargetIntent(), + mBaseResolveList); + } else { + currentResolveList = mUnfilteredResolveList = + mResolverListController.getResolversForIntent(shouldGetResolvedFilter(), + mResolverListCommunicator.shouldGetActivityMetadata(), + mIntents); + if (currentResolveList == null) { + processSortedList(currentResolveList); + return true; + } + List<ResolvedComponentInfo> originalList = + mResolverListController.filterIneligibleActivities(currentResolveList, + true); + if (originalList != null) { + mUnfilteredResolveList = originalList; + } + } + + // So far we only support a single other profile at a time. + // The first one we see gets special treatment. + for (ResolvedComponentInfo info : currentResolveList) { + ResolveInfo resolveInfo = info.getResolveInfoAt(0); + if (resolveInfo.targetUserId != UserHandle.USER_CURRENT) { + Intent pOrigIntent = mResolverListCommunicator.getReplacementIntent( + resolveInfo.activityInfo, + info.getIntentAt(0)); + Intent replacementIntent = mResolverListCommunicator.getReplacementIntent( + resolveInfo.activityInfo, + mResolverListCommunicator.getTargetIntent()); + mOtherProfile = new DisplayResolveInfo(info.getIntentAt(0), + resolveInfo, + resolveInfo.loadLabel(mPm), + resolveInfo.loadLabel(mPm), + pOrigIntent != null ? pOrigIntent : replacementIntent, + makePresentationGetter(resolveInfo)); + currentResolveList.remove(info); + break; + } + } + + if (mOtherProfile == null) { + try { + mLastChosen = mResolverListController.getLastChosen(); + } catch (RemoteException re) { + Log.d(TAG, "Error calling getLastChosenActivity\n" + re); + } + } + + int n; + if ((currentResolveList != null) && ((n = currentResolveList.size()) > 0)) { + // We only care about fixing the unfilteredList if the current resolve list and + // current resolve list are currently the same. + List<ResolvedComponentInfo> originalList = + mResolverListController.filterLowPriority(currentResolveList, + mUnfilteredResolveList == currentResolveList); + if (originalList != null) { + mUnfilteredResolveList = originalList; + } + + if (currentResolveList.size() > 1) { + int placeholderCount = currentResolveList.size(); + if (mResolverListCommunicator.useLayoutWithDefault()) { + --placeholderCount; + } + setPlaceholderCount(placeholderCount); + createSortingTask().execute(currentResolveList); + postListReadyRunnable(); + return false; + } else { + processSortedList(currentResolveList); + return true; + } + } else { + processSortedList(currentResolveList); + return true; + } + } + + AsyncTask<List<ResolvedComponentInfo>, + Void, + List<ResolvedComponentInfo>> createSortingTask() { + return new AsyncTask<List<ResolvedComponentInfo>, + Void, + List<ResolvedComponentInfo>>() { + @Override + protected List<ResolvedComponentInfo> doInBackground( + List<ResolvedComponentInfo>... params) { + mResolverListController.sort(params[0]); + return params[0]; + } + @Override + protected void onPostExecute(List<ResolvedComponentInfo> sortedComponents) { + processSortedList(sortedComponents); + mResolverListCommunicator.updateProfileViewButton(); + notifyDataSetChanged(); + } + }; + } + + + protected void processSortedList(List<ResolvedComponentInfo> sortedComponents) { + int n; + if (sortedComponents != null && (n = sortedComponents.size()) != 0) { + mAllTargetsAreBrowsers = mUseLayoutForBrowsables; + + // First put the initial items at the top. + if (mInitialIntents != null) { + for (int i = 0; i < mInitialIntents.length; i++) { + Intent ii = mInitialIntents[i]; + if (ii == null) { + continue; + } + ActivityInfo ai = ii.resolveActivityInfo( + mPm, 0); + if (ai == null) { + Log.w(TAG, "No activity found for " + ii); + continue; + } + ResolveInfo ri = new ResolveInfo(); + ri.activityInfo = ai; + UserManager userManager = + (UserManager) mContext.getSystemService(Context.USER_SERVICE); + if (ii instanceof LabeledIntent) { + LabeledIntent li = (LabeledIntent) ii; + ri.resolvePackageName = li.getSourcePackage(); + ri.labelRes = li.getLabelResource(); + ri.nonLocalizedLabel = li.getNonLocalizedLabel(); + ri.icon = li.getIconResource(); + ri.iconResourceId = ri.icon; + } + if (userManager.isManagedProfile()) { + ri.noResourceId = true; + ri.icon = 0; + } + mAllTargetsAreBrowsers &= ri.handleAllWebDataURI; + + addResolveInfo(new DisplayResolveInfo(ii, ri, + ri.loadLabel(mPm), null, ii, makePresentationGetter(ri))); + } + } + + + for (ResolvedComponentInfo rci : sortedComponents) { + final ResolveInfo ri = rci.getResolveInfoAt(0); + if (ri != null) { + mAllTargetsAreBrowsers &= ri.handleAllWebDataURI; + addResolveInfoWithAlternates(rci); + } + } + } + + mResolverListCommunicator.sendVoiceChoicesIfNeeded(); + postListReadyRunnable(); + } + + /** + * Some necessary methods for creating the list are initiated in onCreate and will also + * determine the layout known. We therefore can't update the UI inline and post to the + * handler thread to update after the current task is finished. + */ + private void postListReadyRunnable() { + if (mPostListReadyRunnable == null) { + mPostListReadyRunnable = new Runnable() { + @Override + public void run() { + mResolverListCommunicator.onPostListReady(); + mPostListReadyRunnable = null; + } + }; + mContext.getMainThreadHandler().post(mPostListReadyRunnable); + } + } + + public boolean shouldGetResolvedFilter() { + return mFilterLastUsed; + } + + private void addResolveInfoWithAlternates(ResolvedComponentInfo rci) { + final int count = rci.getCount(); + final Intent intent = rci.getIntentAt(0); + final ResolveInfo add = rci.getResolveInfoAt(0); + final Intent replaceIntent = + mResolverListCommunicator.getReplacementIntent(add.activityInfo, intent); + final Intent defaultIntent = mResolverListCommunicator.getReplacementIntent( + add.activityInfo, mResolverListCommunicator.getTargetIntent()); + final DisplayResolveInfo + dri = new DisplayResolveInfo(intent, add, + replaceIntent != null ? replaceIntent : defaultIntent, makePresentationGetter(add)); + addResolveInfo(dri); + if (replaceIntent == intent) { + // Only add alternates if we didn't get a specific replacement from + // the caller. If we have one it trumps potential alternates. + for (int i = 1, n = count; i < n; i++) { + final Intent altIntent = rci.getIntentAt(i); + dri.addAlternateSourceIntent(altIntent); + } + } + updateLastChosenPosition(add); + } + + private void updateLastChosenPosition(ResolveInfo info) { + // If another profile is present, ignore the last chosen entry. + if (mOtherProfile != null) { + mLastChosenPosition = -1; + return; + } + if (mLastChosen != null + && mLastChosen.activityInfo.packageName.equals(info.activityInfo.packageName) + && mLastChosen.activityInfo.name.equals(info.activityInfo.name)) { + mLastChosenPosition = mDisplayList.size() - 1; + } + } + + // We assume that at this point we've already filtered out the only intent for a different + // targetUserId which we're going to use. + private void addResolveInfo(DisplayResolveInfo dri) { + if (dri != null && dri.getResolveInfo() != null + && dri.getResolveInfo().targetUserId == UserHandle.USER_CURRENT) { + // Checks if this info is already listed in display. + for (DisplayResolveInfo existingInfo : mDisplayList) { + if (mResolverListCommunicator + .resolveInfoMatch(dri.getResolveInfo(), existingInfo.getResolveInfo())) { + return; + } + } + mDisplayList.add(dri); + } + } + + @Nullable + public ResolveInfo resolveInfoForPosition(int position, boolean filtered) { + TargetInfo target = targetInfoForPosition(position, filtered); + if (target != null) { + return target.getResolveInfo(); + } + return null; + } + + @Nullable + public TargetInfo targetInfoForPosition(int position, boolean filtered) { + if (filtered) { + return getItem(position); + } + if (mDisplayList.size() > position) { + return mDisplayList.get(position); + } + return null; + } + + public int getCount() { + int totalSize = mDisplayList == null || mDisplayList.isEmpty() ? mPlaceholderCount : + mDisplayList.size(); + if (mFilterLastUsed && mLastChosenPosition >= 0) { + totalSize--; + } + return totalSize; + } + + public int getUnfilteredCount() { + return mDisplayList.size(); + } + + @Nullable + public TargetInfo getItem(int position) { + if (mFilterLastUsed && mLastChosenPosition >= 0 && position >= mLastChosenPosition) { + position++; + } + if (mDisplayList.size() > position) { + return mDisplayList.get(position); + } else { + return null; + } + } + + public long getItemId(int position) { + return position; + } + + public int getDisplayResolveInfoCount() { + return mDisplayList.size(); + } + + public DisplayResolveInfo getDisplayResolveInfo(int index) { + // Used to query services. We only query services for primary targets, not alternates. + return mDisplayList.get(index); + } + + public final View getView(int position, View convertView, ViewGroup parent) { + View view = convertView; + if (view == null) { + view = createView(parent); + } + onBindView(view, getItem(position)); + return view; + } + + public final View createView(ViewGroup parent) { + final View view = onCreateView(parent); + final ViewHolder holder = new ViewHolder(view); + view.setTag(holder); + return view; + } + + public View onCreateView(ViewGroup parent) { + return mInflater.inflate( + com.android.internal.R.layout.resolve_list_item, parent, false); + } + + public final void bindView(int position, View view) { + onBindView(view, getItem(position)); + } + + protected void onBindView(View view, TargetInfo info) { + final ViewHolder holder = (ViewHolder) view.getTag(); + if (info == null) { + holder.icon.setImageDrawable( + mContext.getDrawable(R.drawable.resolver_icon_placeholder)); + return; + } + + if (info instanceof DisplayResolveInfo + && !((DisplayResolveInfo) info).hasDisplayLabel()) { + getLoadLabelTask((DisplayResolveInfo) info, holder).execute(); + } else { + holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo()); + } + + if (info.isSuspended()) { + holder.icon.setColorFilter(mSuspendedMatrixColorFilter); + } else { + holder.icon.setColorFilter(null); + } + + if (info instanceof DisplayResolveInfo + && !((DisplayResolveInfo) info).hasDisplayIcon()) { + new ResolverListAdapter.LoadIconTask((DisplayResolveInfo) info, holder.icon).execute(); + } else { + holder.icon.setImageDrawable(info.getDisplayIcon(mContext)); + } + } + + protected LoadLabelTask getLoadLabelTask(DisplayResolveInfo info, ViewHolder holder) { + return new LoadLabelTask(info, holder); + } + + public void onDestroy() { + if (mPostListReadyRunnable != null) { + mContext.getMainThreadHandler().removeCallbacks(mPostListReadyRunnable); + mPostListReadyRunnable = null; + } + if (mResolverListController != null) { + mResolverListController.destroy(); + } + } + + private ColorMatrixColorFilter createSuspendedColorMatrix() { + int grayValue = 127; + float scale = 0.5f; // half bright + + ColorMatrix tempBrightnessMatrix = new ColorMatrix(); + float[] mat = tempBrightnessMatrix.getArray(); + mat[0] = scale; + mat[6] = scale; + mat[12] = scale; + mat[4] = grayValue; + mat[9] = grayValue; + mat[14] = grayValue; + + ColorMatrix matrix = new ColorMatrix(); + matrix.setSaturation(0.0f); + matrix.preConcat(tempBrightnessMatrix); + return new ColorMatrixColorFilter(matrix); + } + + ActivityInfoPresentationGetter makePresentationGetter(ActivityInfo ai) { + return new ActivityInfoPresentationGetter(mContext, mIconDpi, ai); + } + + ResolveInfoPresentationGetter makePresentationGetter(ResolveInfo ri) { + return new ResolveInfoPresentationGetter(mContext, mIconDpi, ri); + } + + Drawable loadIconForResolveInfo(ResolveInfo ri) { + // Load icons based on the current process. If in work profile icons should be badged. + return makePresentationGetter(ri).getIcon(Process.myUserHandle()); + } + + void loadFilteredItemIconTaskAsync(@NonNull ImageView iconView) { + final DisplayResolveInfo iconInfo = getFilteredItem(); + if (iconView != null && iconInfo != null) { + new LoadIconTask(iconInfo, iconView).execute(); + } + } + + /** + * Necessary methods to communicate between {@link ResolverListAdapter} + * and {@link ResolverActivity}. + */ + interface ResolverListCommunicator { + + boolean resolveInfoMatch(ResolveInfo lhs, ResolveInfo rhs); + + Intent getReplacementIntent(ActivityInfo activityInfo, Intent defIntent); + + void onPostListReady(); + + void sendVoiceChoicesIfNeeded(); + + void updateProfileViewButton(); + + boolean useLayoutWithDefault(); + + boolean shouldGetActivityMetadata(); + + Intent getTargetIntent(); + + void onHandlePackagesChanged(); + } + + static class ViewHolder { + public View itemView; + public Drawable defaultItemViewBackground; + + public TextView text; + public TextView text2; + public ImageView icon; + + ViewHolder(View view) { + itemView = view; + defaultItemViewBackground = view.getBackground(); + text = (TextView) view.findViewById(com.android.internal.R.id.text1); + text2 = (TextView) view.findViewById(com.android.internal.R.id.text2); + icon = (ImageView) view.findViewById(R.id.icon); + } + + public void bindLabel(CharSequence label, CharSequence subLabel) { + if (!TextUtils.equals(text.getText(), label)) { + text.setText(label); + } + + // Always show a subLabel for visual consistency across list items. Show an empty + // subLabel if the subLabel is the same as the label + if (TextUtils.equals(label, subLabel)) { + subLabel = null; + } + + if (!TextUtils.equals(text2.getText(), subLabel) + && !TextUtils.isEmpty(subLabel)) { + text2.setVisibility(View.VISIBLE); + text2.setText(subLabel); + } + } + } + + protected class LoadLabelTask extends AsyncTask<Void, Void, CharSequence[]> { + private final DisplayResolveInfo mDisplayResolveInfo; + private final ViewHolder mHolder; + + protected LoadLabelTask(DisplayResolveInfo dri, ViewHolder holder) { + mDisplayResolveInfo = dri; + mHolder = holder; + } + + @Override + protected CharSequence[] doInBackground(Void... voids) { + ResolveInfoPresentationGetter pg = + makePresentationGetter(mDisplayResolveInfo.getResolveInfo()); + return new CharSequence[] { + pg.getLabel(), + pg.getSubLabel() + }; + } + + @Override + protected void onPostExecute(CharSequence[] result) { + mDisplayResolveInfo.setDisplayLabel(result[0]); + mDisplayResolveInfo.setExtendedInfo(result[1]); + mHolder.bindLabel(result[0], result[1]); + } + } + + class LoadIconTask extends AsyncTask<Void, Void, Drawable> { + protected final com.android.internal.app.chooser.DisplayResolveInfo mDisplayResolveInfo; + private final ResolveInfo mResolveInfo; + private final ImageView mTargetView; + + LoadIconTask(DisplayResolveInfo dri, ImageView target) { + mDisplayResolveInfo = dri; + mResolveInfo = dri.getResolveInfo(); + mTargetView = target; + } + + @Override + protected Drawable doInBackground(Void... params) { + return loadIconForResolveInfo(mResolveInfo); + } + + @Override + protected void onPostExecute(Drawable d) { + if (getOtherProfile() == mDisplayResolveInfo) { + mResolverListCommunicator.updateProfileViewButton(); + } else { + mDisplayResolveInfo.setDisplayIcon(d); + mTargetView.setImageDrawable(d); + } + } + } + + /** + * Loads the icon and label for the provided ResolveInfo. + */ + @VisibleForTesting + public static class ResolveInfoPresentationGetter extends ActivityInfoPresentationGetter { + private final ResolveInfo mRi; + public ResolveInfoPresentationGetter(Context ctx, int iconDpi, ResolveInfo ri) { + super(ctx, iconDpi, ri.activityInfo); + mRi = ri; + } + + @Override + Drawable getIconSubstituteInternal() { + Drawable dr = null; + try { + // Do not use ResolveInfo#getIconResource() as it defaults to the app + if (mRi.resolvePackageName != null && mRi.icon != 0) { + dr = loadIconFromResource( + mPm.getResourcesForApplication(mRi.resolvePackageName), mRi.icon); + } + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON permission granted but " + + "couldn't find resources for package", e); + } + + // Fall back to ActivityInfo if no icon is found via ResolveInfo + if (dr == null) dr = super.getIconSubstituteInternal(); + + return dr; + } + + @Override + String getAppSubLabelInternal() { + // Will default to app name if no intent filter or activity label set, make sure to + // check if subLabel matches label before final display + return (String) mRi.loadLabel(mPm); + } + } + + /** + * Loads the icon and label for the provided ActivityInfo. + */ + @VisibleForTesting + public static class ActivityInfoPresentationGetter extends + TargetPresentationGetter { + private final ActivityInfo mActivityInfo; + public ActivityInfoPresentationGetter(Context ctx, int iconDpi, + ActivityInfo activityInfo) { + super(ctx, iconDpi, activityInfo.applicationInfo); + mActivityInfo = activityInfo; + } + + @Override + Drawable getIconSubstituteInternal() { + Drawable dr = null; + try { + // Do not use ActivityInfo#getIconResource() as it defaults to the app + if (mActivityInfo.icon != 0) { + dr = loadIconFromResource( + mPm.getResourcesForApplication(mActivityInfo.applicationInfo), + mActivityInfo.icon); + } + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON permission granted but " + + "couldn't find resources for package", e); + } + + return dr; + } + + @Override + String getAppSubLabelInternal() { + // Will default to app name if no activity label set, make sure to check if subLabel + // matches label before final display + return (String) mActivityInfo.loadLabel(mPm); + } + } + + /** + * Loads the icon and label for the provided ApplicationInfo. Defaults to using the application + * icon and label over any IntentFilter or Activity icon to increase user understanding, with an + * exception for applications that hold the right permission. Always attempts to use available + * resources over PackageManager loading mechanisms so badging can be done by iconloader. Uses + * Strings to strip creative formatting. + */ + private abstract static class TargetPresentationGetter { + @Nullable abstract Drawable getIconSubstituteInternal(); + @Nullable abstract String getAppSubLabelInternal(); + + private Context mCtx; + private final int mIconDpi; + private final boolean mHasSubstitutePermission; + private final ApplicationInfo mAi; + + protected PackageManager mPm; + + TargetPresentationGetter(Context ctx, int iconDpi, ApplicationInfo ai) { + mCtx = ctx; + mPm = ctx.getPackageManager(); + mAi = ai; + mIconDpi = iconDpi; + mHasSubstitutePermission = PackageManager.PERMISSION_GRANTED == mPm.checkPermission( + android.Manifest.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON, + mAi.packageName); + } + + public Drawable getIcon(UserHandle userHandle) { + return new BitmapDrawable(mCtx.getResources(), getIconBitmap(userHandle)); + } + + public Bitmap getIconBitmap(UserHandle userHandle) { + Drawable dr = null; + if (mHasSubstitutePermission) { + dr = getIconSubstituteInternal(); + } + + if (dr == null) { + try { + if (mAi.icon != 0) { + dr = loadIconFromResource(mPm.getResourcesForApplication(mAi), mAi.icon); + } + } catch (PackageManager.NameNotFoundException ignore) { + } + } + + // Fall back to ApplicationInfo#loadIcon if nothing has been loaded + if (dr == null) { + dr = mAi.loadIcon(mPm); + } + + SimpleIconFactory sif = SimpleIconFactory.obtain(mCtx); + Bitmap icon = sif.createUserBadgedIconBitmap(dr, userHandle); + sif.recycle(); + + return icon; + } + + public String getLabel() { + String label = null; + // Apps with the substitute permission will always show the sublabel as their label + if (mHasSubstitutePermission) { + label = getAppSubLabelInternal(); + } + + if (label == null) { + label = (String) mAi.loadLabel(mPm); + } + + return label; + } + + public String getSubLabel() { + // Apps with the substitute permission will never have a sublabel + if (mHasSubstitutePermission) return null; + return getAppSubLabelInternal(); + } + + protected String loadLabelFromResource(Resources res, int resId) { + return res.getString(resId); + } + + @Nullable + protected Drawable loadIconFromResource(Resources res, int resId) { + return res.getDrawableForDensity(resId, mIconDpi); + } + + } +} diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java index 28a8a8631372..6cc60b786e55 100644 --- a/core/java/com/android/internal/app/ResolverListController.java +++ b/core/java/com/android/internal/app/ResolverListController.java @@ -31,6 +31,7 @@ import android.os.RemoteException; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.chooser.DisplayResolveInfo; import java.util.ArrayList; import java.util.Collections; @@ -332,7 +333,7 @@ public class ResolverListController { } @VisibleForTesting - public float getScore(ResolverActivity.DisplayResolveInfo target) { + public float getScore(DisplayResolveInfo target) { return mResolverComparator.getScore(target.getResolvedComponentName()); } diff --git a/core/java/com/android/internal/app/SimpleIconFactory.java b/core/java/com/android/internal/app/SimpleIconFactory.java index 7a4e76fa8c17..d618cdf86865 100644 --- a/core/java/com/android/internal/app/SimpleIconFactory.java +++ b/core/java/com/android/internal/app/SimpleIconFactory.java @@ -214,7 +214,7 @@ public class SimpleIconFactory { * @deprecated Do not use, functionality will be replaced by iconloader lib eventually. */ @Deprecated - Bitmap createAppBadgedIconBitmap(@Nullable Drawable icon, Bitmap renderedAppIcon) { + public Bitmap createAppBadgedIconBitmap(@Nullable Drawable icon, Bitmap renderedAppIcon) { // If no icon is provided use the system default if (icon == null) { icon = getFullResDefaultActivityIcon(mFillResIconDpi); diff --git a/core/java/com/android/internal/app/chooser/ChooserTargetInfo.java b/core/java/com/android/internal/app/chooser/ChooserTargetInfo.java new file mode 100644 index 000000000000..a2d0953b5620 --- /dev/null +++ b/core/java/com/android/internal/app/chooser/ChooserTargetInfo.java @@ -0,0 +1,53 @@ +/* + * 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.internal.app.chooser; + +import android.service.chooser.ChooserTarget; +import android.text.TextUtils; + +/** + * A TargetInfo for Direct Share. Includes a {@link ChooserTarget} representing the + * Direct Share deep link into an application. + */ +public interface ChooserTargetInfo extends TargetInfo { + float getModifiedScore(); + + ChooserTarget getChooserTarget(); + + /** + * Do not label as 'equals', since this doesn't quite work + * as intended with java 8. + */ + default boolean isSimilar(ChooserTargetInfo other) { + if (other == null) return false; + + ChooserTarget ct1 = getChooserTarget(); + ChooserTarget ct2 = other.getChooserTarget(); + + // If either is null, there is not enough info to make an informed decision + // about equality, so just exit + if (ct1 == null || ct2 == null) return false; + + if (ct1.getComponentName().equals(ct2.getComponentName()) + && TextUtils.equals(getDisplayLabel(), other.getDisplayLabel()) + && TextUtils.equals(getExtendedInfo(), other.getExtendedInfo())) { + return true; + } + + return false; + } +} diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java new file mode 100644 index 000000000000..c77444e949ed --- /dev/null +++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java @@ -0,0 +1,182 @@ +/* + * 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.internal.app.chooser; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ResolveInfo; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.UserHandle; + +import com.android.internal.app.ResolverActivity; +import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter; + +import java.util.ArrayList; +import java.util.List; + +/** + * A TargetInfo plus additional information needed to render it (such as icon and label) and + * resolve it to an activity. + */ +public class DisplayResolveInfo implements TargetInfo { + // Temporary flag for new chooser delegate behavior. + private static final boolean ENABLE_CHOOSER_DELEGATE = true; + + private final ResolveInfo mResolveInfo; + private CharSequence mDisplayLabel; + private Drawable mDisplayIcon; + private CharSequence mExtendedInfo; + private final Intent mResolvedIntent; + private final List<Intent> mSourceIntents = new ArrayList<>(); + private boolean mIsSuspended; + private ResolveInfoPresentationGetter mResolveInfoPresentationGetter; + + public DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, Intent pOrigIntent, + ResolveInfoPresentationGetter resolveInfoPresentationGetter) { + this(originalIntent, pri, null /*mDisplayLabel*/, null /*mExtendedInfo*/, pOrigIntent, + resolveInfoPresentationGetter); + } + + public DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, CharSequence pLabel, + CharSequence pInfo, @NonNull Intent resolvedIntent, + @Nullable ResolveInfoPresentationGetter resolveInfoPresentationGetter) { + mSourceIntents.add(originalIntent); + mResolveInfo = pri; + mDisplayLabel = pLabel; + mExtendedInfo = pInfo; + mResolveInfoPresentationGetter = resolveInfoPresentationGetter; + + final Intent intent = new Intent(resolvedIntent); + intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT + | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); + final ActivityInfo ai = mResolveInfo.activityInfo; + intent.setComponent(new ComponentName(ai.applicationInfo.packageName, ai.name)); + + mIsSuspended = (ai.applicationInfo.flags & ApplicationInfo.FLAG_SUSPENDED) != 0; + + mResolvedIntent = intent; + } + + private DisplayResolveInfo(DisplayResolveInfo other, Intent fillInIntent, int flags, + ResolveInfoPresentationGetter resolveInfoPresentationGetter) { + mSourceIntents.addAll(other.getAllSourceIntents()); + mResolveInfo = other.mResolveInfo; + mDisplayLabel = other.mDisplayLabel; + mDisplayIcon = other.mDisplayIcon; + mExtendedInfo = other.mExtendedInfo; + mResolvedIntent = new Intent(other.mResolvedIntent); + mResolvedIntent.fillIn(fillInIntent, flags); + mResolveInfoPresentationGetter = resolveInfoPresentationGetter; + } + + public ResolveInfo getResolveInfo() { + return mResolveInfo; + } + + public CharSequence getDisplayLabel() { + if (mDisplayLabel == null && mResolveInfoPresentationGetter != null) { + mDisplayLabel = mResolveInfoPresentationGetter.getLabel(); + mExtendedInfo = mResolveInfoPresentationGetter.getSubLabel(); + } + return mDisplayLabel; + } + + public boolean hasDisplayLabel() { + return mDisplayLabel != null; + } + + public void setDisplayLabel(CharSequence displayLabel) { + mDisplayLabel = displayLabel; + } + + public void setExtendedInfo(CharSequence extendedInfo) { + mExtendedInfo = extendedInfo; + } + + public Drawable getDisplayIcon(Context context) { + return mDisplayIcon; + } + + @Override + public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { + return new DisplayResolveInfo(this, fillInIntent, flags, mResolveInfoPresentationGetter); + } + + @Override + public List<Intent> getAllSourceIntents() { + return mSourceIntents; + } + + public void addAlternateSourceIntent(Intent alt) { + mSourceIntents.add(alt); + } + + public void setDisplayIcon(Drawable icon) { + mDisplayIcon = icon; + } + + public boolean hasDisplayIcon() { + return mDisplayIcon != null; + } + + public CharSequence getExtendedInfo() { + return mExtendedInfo; + } + + public Intent getResolvedIntent() { + return mResolvedIntent; + } + + @Override + public ComponentName getResolvedComponentName() { + return new ComponentName(mResolveInfo.activityInfo.packageName, + mResolveInfo.activityInfo.name); + } + + @Override + public boolean start(Activity activity, Bundle options) { + activity.startActivity(mResolvedIntent, options); + return true; + } + + @Override + public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) { + if (ENABLE_CHOOSER_DELEGATE) { + return activity.startAsCallerImpl(mResolvedIntent, options, false, userId); + } else { + activity.startActivityAsCaller(mResolvedIntent, options, null, false, userId); + return true; + } + } + + @Override + public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { + activity.startActivityAsUser(mResolvedIntent, options, user); + return false; + } + + public boolean isSuspended() { + return mIsSuspended; + } +} diff --git a/core/java/com/android/internal/app/chooser/NotSelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/NotSelectableTargetInfo.java new file mode 100644 index 000000000000..22cbdaa66267 --- /dev/null +++ b/core/java/com/android/internal/app/chooser/NotSelectableTargetInfo.java @@ -0,0 +1,88 @@ +/* + * 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.internal.app.chooser; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.os.Bundle; +import android.os.UserHandle; +import android.service.chooser.ChooserTarget; + +import com.android.internal.app.ResolverActivity; + +import java.util.List; + +/** + * Distinguish between targets that selectable by the user, vs those that are + * placeholders for the system while information is loading in an async manner. + */ +public abstract class NotSelectableTargetInfo implements ChooserTargetInfo { + + public Intent getResolvedIntent() { + return null; + } + + public ComponentName getResolvedComponentName() { + return null; + } + + public boolean start(Activity activity, Bundle options) { + return false; + } + + public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) { + return false; + } + + public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { + return false; + } + + public ResolveInfo getResolveInfo() { + return null; + } + + public CharSequence getDisplayLabel() { + return null; + } + + public CharSequence getExtendedInfo() { + return null; + } + + public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { + return null; + } + + public List<Intent> getAllSourceIntents() { + return null; + } + + public float getModifiedScore() { + return -0.1f; + } + + public ChooserTarget getChooserTarget() { + return null; + } + + public boolean isSuspended() { + return false; + } +} diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java new file mode 100644 index 000000000000..1cc4857b39fe --- /dev/null +++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java @@ -0,0 +1,316 @@ +/* + * 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.internal.app.chooser; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.LauncherApps; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ShortcutInfo; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.os.Bundle; +import android.os.UserHandle; +import android.service.chooser.ChooserTarget; +import android.text.SpannableStringBuilder; +import android.util.Log; + +import com.android.internal.app.ChooserActivity; +import com.android.internal.app.ChooserFlags; +import com.android.internal.app.ResolverActivity; +import com.android.internal.app.ResolverListAdapter.ActivityInfoPresentationGetter; +import com.android.internal.app.SimpleIconFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Live target, currently selectable by the user. + * @see NotSelectableTargetInfo + */ +public final class SelectableTargetInfo implements ChooserTargetInfo { + private static final String TAG = "SelectableTargetInfo"; + + private final Context mContext; + private final DisplayResolveInfo mSourceInfo; + private final ResolveInfo mBackupResolveInfo; + private final ChooserTarget mChooserTarget; + private final String mDisplayLabel; + private final PackageManager mPm; + private final SelectableTargetInfoCommunicator mSelectableTargetInfoCommunicator; + private Drawable mBadgeIcon = null; + private CharSequence mBadgeContentDescription; + private Drawable mDisplayIcon; + private final Intent mFillInIntent; + private final int mFillInFlags; + private final float mModifiedScore; + private boolean mIsSuspended = false; + + public SelectableTargetInfo(Context context, DisplayResolveInfo sourceInfo, + ChooserTarget chooserTarget, + float modifiedScore, SelectableTargetInfoCommunicator selectableTargetInfoComunicator) { + mContext = context; + mSourceInfo = sourceInfo; + mChooserTarget = chooserTarget; + mModifiedScore = modifiedScore; + mPm = mContext.getPackageManager(); + mSelectableTargetInfoCommunicator = selectableTargetInfoComunicator; + if (sourceInfo != null) { + final ResolveInfo ri = sourceInfo.getResolveInfo(); + if (ri != null) { + final ActivityInfo ai = ri.activityInfo; + if (ai != null && ai.applicationInfo != null) { + final PackageManager pm = mContext.getPackageManager(); + mBadgeIcon = pm.getApplicationIcon(ai.applicationInfo); + mBadgeContentDescription = pm.getApplicationLabel(ai.applicationInfo); + mIsSuspended = + (ai.applicationInfo.flags & ApplicationInfo.FLAG_SUSPENDED) != 0; + } + } + } + // TODO(b/121287224): do this in the background thread, and only for selected targets + mDisplayIcon = getChooserTargetIconDrawable(chooserTarget); + + if (sourceInfo != null) { + mBackupResolveInfo = null; + } else { + mBackupResolveInfo = + mContext.getPackageManager().resolveActivity(getResolvedIntent(), 0); + } + + mFillInIntent = null; + mFillInFlags = 0; + + mDisplayLabel = sanitizeDisplayLabel(chooserTarget.getTitle()); + } + + private SelectableTargetInfo(SelectableTargetInfo other, + Intent fillInIntent, int flags) { + mContext = other.mContext; + mPm = other.mPm; + mSelectableTargetInfoCommunicator = other.mSelectableTargetInfoCommunicator; + mSourceInfo = other.mSourceInfo; + mBackupResolveInfo = other.mBackupResolveInfo; + mChooserTarget = other.mChooserTarget; + mBadgeIcon = other.mBadgeIcon; + mBadgeContentDescription = other.mBadgeContentDescription; + mDisplayIcon = other.mDisplayIcon; + mFillInIntent = fillInIntent; + mFillInFlags = flags; + mModifiedScore = other.mModifiedScore; + + mDisplayLabel = sanitizeDisplayLabel(mChooserTarget.getTitle()); + } + + private String sanitizeDisplayLabel(CharSequence label) { + SpannableStringBuilder sb = new SpannableStringBuilder(label); + sb.clearSpans(); + return sb.toString(); + } + + public boolean isSuspended() { + return mIsSuspended; + } + + /** + * Since ShortcutInfos are returned by ShortcutManager, we can cache the shortcuts and skip + * the call to LauncherApps#getShortcuts(ShortcutQuery). + */ + // TODO(121287224): Refactor code to apply the suggestion above + private Drawable getChooserTargetIconDrawable(ChooserTarget target) { + Drawable directShareIcon = null; + + // First get the target drawable and associated activity info + final Icon icon = target.getIcon(); + if (icon != null) { + directShareIcon = icon.loadDrawable(mContext); + } else if (ChooserFlags.USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS) { + Bundle extras = target.getIntentExtras(); + if (extras != null && extras.containsKey(Intent.EXTRA_SHORTCUT_ID)) { + CharSequence shortcutId = extras.getCharSequence(Intent.EXTRA_SHORTCUT_ID); + LauncherApps launcherApps = (LauncherApps) mContext.getSystemService( + Context.LAUNCHER_APPS_SERVICE); + final LauncherApps.ShortcutQuery q = new LauncherApps.ShortcutQuery(); + q.setPackage(target.getComponentName().getPackageName()); + q.setShortcutIds(Arrays.asList(shortcutId.toString())); + q.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC); + final List<ShortcutInfo> shortcuts = + launcherApps.getShortcuts(q, mContext.getUser()); + if (shortcuts != null && shortcuts.size() > 0) { + directShareIcon = launcherApps.getShortcutIconDrawable(shortcuts.get(0), 0); + } + } + } + + if (directShareIcon == null) return null; + + ActivityInfo info = null; + try { + info = mPm.getActivityInfo(target.getComponentName(), 0); + } catch (PackageManager.NameNotFoundException error) { + Log.e(TAG, "Could not find activity associated with ChooserTarget"); + } + + if (info == null) return null; + + // Now fetch app icon and raster with no badging even in work profile + Bitmap appIcon = mSelectableTargetInfoCommunicator.makePresentationGetter(info) + .getIconBitmap(UserHandle.getUserHandleForUid(UserHandle.myUserId())); + + // Raster target drawable with appIcon as a badge + SimpleIconFactory sif = SimpleIconFactory.obtain(mContext); + Bitmap directShareBadgedIcon = sif.createAppBadgedIconBitmap(directShareIcon, appIcon); + sif.recycle(); + + return new BitmapDrawable(mContext.getResources(), directShareBadgedIcon); + } + + public float getModifiedScore() { + return mModifiedScore; + } + + @Override + public Intent getResolvedIntent() { + if (mSourceInfo != null) { + return mSourceInfo.getResolvedIntent(); + } + + final Intent targetIntent = new Intent(mSelectableTargetInfoCommunicator.getTargetIntent()); + targetIntent.setComponent(mChooserTarget.getComponentName()); + targetIntent.putExtras(mChooserTarget.getIntentExtras()); + return targetIntent; + } + + @Override + public ComponentName getResolvedComponentName() { + if (mSourceInfo != null) { + return mSourceInfo.getResolvedComponentName(); + } else if (mBackupResolveInfo != null) { + return new ComponentName(mBackupResolveInfo.activityInfo.packageName, + mBackupResolveInfo.activityInfo.name); + } + return null; + } + + private Intent getBaseIntentToSend() { + Intent result = getResolvedIntent(); + if (result == null) { + Log.e(TAG, "ChooserTargetInfo: no base intent available to send"); + } else { + result = new Intent(result); + if (mFillInIntent != null) { + result.fillIn(mFillInIntent, mFillInFlags); + } + result.fillIn(mSelectableTargetInfoCommunicator.getReferrerFillInIntent(), 0); + } + return result; + } + + @Override + public boolean start(Activity activity, Bundle options) { + throw new RuntimeException("ChooserTargets should be started as caller."); + } + + @Override + public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) { + final Intent intent = getBaseIntentToSend(); + if (intent == null) { + return false; + } + intent.setComponent(mChooserTarget.getComponentName()); + intent.putExtras(mChooserTarget.getIntentExtras()); + + // Important: we will ignore the target security checks in ActivityManager + // if and only if the ChooserTarget's target package is the same package + // where we got the ChooserTargetService that provided it. This lets a + // ChooserTargetService provide a non-exported or permission-guarded target + // to the chooser for the user to pick. + // + // If mSourceInfo is null, we got this ChooserTarget from the caller or elsewhere + // so we'll obey the caller's normal security checks. + final boolean ignoreTargetSecurity = mSourceInfo != null + && mSourceInfo.getResolvedComponentName().getPackageName() + .equals(mChooserTarget.getComponentName().getPackageName()); + return activity.startAsCallerImpl(intent, options, ignoreTargetSecurity, userId); + } + + @Override + public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { + throw new RuntimeException("ChooserTargets should be started as caller."); + } + + @Override + public ResolveInfo getResolveInfo() { + return mSourceInfo != null ? mSourceInfo.getResolveInfo() : mBackupResolveInfo; + } + + @Override + public CharSequence getDisplayLabel() { + return mDisplayLabel; + } + + @Override + public CharSequence getExtendedInfo() { + // ChooserTargets have badge icons, so we won't show the extended info to disambiguate. + return null; + } + + @Override + public Drawable getDisplayIcon(Context context) { + return mDisplayIcon; + } + + public ChooserTarget getChooserTarget() { + return mChooserTarget; + } + + @Override + public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { + return new SelectableTargetInfo(this, fillInIntent, flags); + } + + @Override + public List<Intent> getAllSourceIntents() { + final List<Intent> results = new ArrayList<>(); + if (mSourceInfo != null) { + // We only queried the service for the first one in our sourceinfo. + results.add(mSourceInfo.getAllSourceIntents().get(0)); + } + return results; + } + + /** + * Necessary methods to communicate between {@link SelectableTargetInfo} + * and {@link ResolverActivity} or {@link ChooserActivity}. + */ + public interface SelectableTargetInfoCommunicator { + + ActivityInfoPresentationGetter makePresentationGetter(ActivityInfo info); + + Intent getTargetIntent(); + + Intent getReferrerFillInIntent(); + } +} diff --git a/core/java/com/android/internal/app/chooser/TargetInfo.java b/core/java/com/android/internal/app/chooser/TargetInfo.java new file mode 100644 index 000000000000..b59def174828 --- /dev/null +++ b/core/java/com/android/internal/app/chooser/TargetInfo.java @@ -0,0 +1,128 @@ +/* + * 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.internal.app.chooser; + + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.UserHandle; + +import com.android.internal.app.ResolverActivity; + +import java.util.List; + +/** + * A single target as represented in the chooser. + */ +public interface TargetInfo { + /** + * Get the resolved intent that represents this target. Note that this may not be the + * intent that will be launched by calling one of the <code>start</code> methods provided; + * this is the intent that will be credited with the launch. + * + * @return the resolved intent for this target + */ + Intent getResolvedIntent(); + + /** + * Get the resolved component name that represents this target. Note that this may not + * be the component that will be directly launched by calling one of the <code>start</code> + * methods provided; this is the component that will be credited with the launch. + * + * @return the resolved ComponentName for this target + */ + ComponentName getResolvedComponentName(); + + /** + * Start the activity referenced by this target. + * + * @param activity calling Activity performing the launch + * @param options ActivityOptions bundle + * @return true if the start completed successfully + */ + boolean start(Activity activity, Bundle options); + + /** + * Start the activity referenced by this target as if the ResolverActivity's caller + * was performing the start operation. + * + * @param activity calling Activity (actually) performing the launch + * @param options ActivityOptions bundle + * @param userId userId to start as or {@link UserHandle#USER_NULL} for activity's caller + * @return true if the start completed successfully + */ + boolean startAsCaller(ResolverActivity activity, Bundle options, int userId); + + /** + * Start the activity referenced by this target as a given user. + * + * @param activity calling activity performing the launch + * @param options ActivityOptions bundle + * @param user handle for the user to start the activity as + * @return true if the start completed successfully + */ + boolean startAsUser(Activity activity, Bundle options, UserHandle user); + + /** + * Return the ResolveInfo about how and why this target matched the original query + * for available targets. + * + * @return ResolveInfo representing this target's match + */ + ResolveInfo getResolveInfo(); + + /** + * Return the human-readable text label for this target. + * + * @return user-visible target label + */ + CharSequence getDisplayLabel(); + + /** + * Return any extended info for this target. This may be used to disambiguate + * otherwise identical targets. + * + * @return human-readable disambig string or null if none present + */ + CharSequence getExtendedInfo(); + + /** + * @return The drawable that should be used to represent this target including badge + * @param context + */ + Drawable getDisplayIcon(Context context); + + /** + * Clone this target with the given fill-in information. + */ + TargetInfo cloneFilledIn(Intent fillInIntent, int flags); + + /** + * @return the list of supported source intents deduped against this single target + */ + List<Intent> getAllSourceIntents(); + + /** + * @return true if this target can be selected by the user + */ + boolean isSuspended(); +} diff --git a/core/java/com/android/internal/compat/CompatibilityChangeConfig.java b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java index fd2ada08edc1..36bc22906695 100644 --- a/core/java/com/android/internal/compat/CompatibilityChangeConfig.java +++ b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java @@ -49,6 +49,18 @@ public final class CompatibilityChangeConfig implements Parcelable { return mChangeConfig.forceDisabledSet(); } + /** + * Returns if a change is enabled or disabled in this config. + */ + public boolean isChangeEnabled(long changeId) { + if (mChangeConfig.isForceEnabled(changeId)) { + return true; + } else if (mChangeConfig.isForceDisabled(changeId)) { + return false; + } + throw new IllegalStateException("Change " + changeId + " is not defined."); + } + private CompatibilityChangeConfig(Parcel in) { long[] enabledArray = in.createLongArray(); long[] disabledArray = in.createLongArray(); diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.aidl b/core/java/com/android/internal/compat/CompatibilityChangeInfo.aidl new file mode 100644 index 000000000000..3bc72771f99c --- /dev/null +++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.aidl @@ -0,0 +1,19 @@ +/* + * 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.internal.compat; + +parcelable CompatibilityChangeInfo; diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java new file mode 100644 index 000000000000..e48e2df4043c --- /dev/null +++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java @@ -0,0 +1,92 @@ +/* + * 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.internal.compat; + +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * This class is a parcelable version of {@link com.android.server.compat.Change}. + * + * @hide + */ +public class CompatibilityChangeInfo implements Parcelable { + private final long mChangeId; + private final @Nullable String mName; + private final int mEnableAfterTargetSdk; + private final boolean mDisabled; + + public long getId() { + return mChangeId; + } + + @Nullable + public String getName() { + return mName; + } + + public int getEnableAfterTargetSdk() { + return mEnableAfterTargetSdk; + } + + public boolean getDisabled() { + return mDisabled; + } + + public CompatibilityChangeInfo( + Long changeId, String name, int enableAfterTargetSdk, boolean disabled) { + this.mChangeId = changeId; + this.mName = name; + this.mEnableAfterTargetSdk = enableAfterTargetSdk; + this.mDisabled = disabled; + } + + private CompatibilityChangeInfo(Parcel in) { + mChangeId = in.readLong(); + mName = in.readString(); + mEnableAfterTargetSdk = in.readInt(); + mDisabled = in.readBoolean(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(mChangeId); + dest.writeString(mName); + dest.writeInt(mEnableAfterTargetSdk); + dest.writeBoolean(mDisabled); + } + + public static final Parcelable.Creator<CompatibilityChangeInfo> CREATOR = + new Parcelable.Creator<CompatibilityChangeInfo>() { + + @Override + public CompatibilityChangeInfo createFromParcel(Parcel in) { + return new CompatibilityChangeInfo(in); + } + + @Override + public CompatibilityChangeInfo[] newArray(int size) { + return new CompatibilityChangeInfo[size]; + } + }; +} diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl index 8391ad2f12c2..5857642cbd4e 100644 --- a/core/java/com/android/internal/compat/IPlatformCompat.aidl +++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl @@ -17,8 +17,10 @@ package com.android.internal.compat; import android.content.pm.ApplicationInfo; +import java.util.Map; parcelable CompatibilityChangeConfig; +parcelable CompatibilityChangeInfo; /** * Platform private API for talking with the PlatformCompat service. @@ -146,4 +148,21 @@ interface IPlatformCompat * */ void clearOverrides(in String packageName); + + /** + * Get configs for an application. + * + * @param appInfo The application whose config will be returned. + * + * @return A {@link CompatibilityChangeConfig}, representing whether a change is enabled for + * the given app or not. + */ + CompatibilityChangeConfig getAppConfig(in ApplicationInfo appInfo); + + /** + * List all compatibility changes. + * + * @return An array of {@link CompatChangeInfo} known to the service. + */ + CompatibilityChangeInfo[] listAllChanges(); } diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java index 025e27bc45c9..382a254b67a6 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java +++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java @@ -168,9 +168,6 @@ public final class InputMethodDebug { if ((startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) { joiner.add("IS_TEXT_EDITOR"); } - if ((startInputFlags & StartInputFlags.FIRST_WINDOW_FOCUS_GAIN) != 0) { - joiner.add("FIRST_WINDOW_FOCUS_GAIN"); - } if ((startInputFlags & StartInputFlags.INITIAL_CONNECTION) != 0) { joiner.add("INITIAL_CONNECTION"); } diff --git a/core/java/com/android/internal/inputmethod/StartInputFlags.java b/core/java/com/android/internal/inputmethod/StartInputFlags.java index ba26d8db533c..5a8d2c227256 100644 --- a/core/java/com/android/internal/inputmethod/StartInputFlags.java +++ b/core/java/com/android/internal/inputmethod/StartInputFlags.java @@ -30,7 +30,6 @@ import java.lang.annotation.Retention; @IntDef(flag = true, value = { StartInputFlags.VIEW_HAS_FOCUS, StartInputFlags.IS_TEXT_EDITOR, - StartInputFlags.FIRST_WINDOW_FOCUS_GAIN, StartInputFlags.INITIAL_CONNECTION}) public @interface StartInputFlags { /** @@ -44,13 +43,8 @@ public @interface StartInputFlags { int IS_TEXT_EDITOR = 2; /** - * This is the first time the window has gotten focus. - */ - int FIRST_WINDOW_FOCUS_GAIN = 4; - - /** * An internal concept to distinguish "start" and "restart". This concept doesn't look well * documented hence we probably need to revisit this though. */ - int INITIAL_CONNECTION = 8; + int INITIAL_CONNECTION = 4; } diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp index bb5780558bdf..3704ccdfb8ea 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -59,6 +59,10 @@ FileDescriptorWhitelist* FileDescriptorWhitelist::Get() { return instance_; } +static bool IsArtMemfd(const std::string& path) { + return android::base::StartsWith(path, "/memfd:/boot-image-methods.art"); +} + bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const { // Check the static whitelist path. for (const auto& whitelist_path : kPathWhitelist) { @@ -87,6 +91,11 @@ bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const { return true; } + // the in-memory file created by ART through memfd_create is allowed. + if (IsArtMemfd(path)) { + return true; + } + // Whitelist files needed for Runtime Resource Overlay, like these: // /system/vendor/overlay/framework-res.apk // /system/vendor/overlay-subdir/pg/framework-res.apk @@ -312,6 +321,11 @@ void FileDescriptorInfo::ReopenOrDetach(fail_fn_t fail_fn) const { return DetachSocket(fail_fn); } + // Children can directly use the in-memory file created by ART through memfd_create. + if (IsArtMemfd(file_path)) { + return; + } + // NOTE: This might happen if the file was unlinked after being opened. // It's a common pattern in the case of temporary files and the like but // we should not allow such usage from the zygote. @@ -531,6 +545,10 @@ FileDescriptorTable::FileDescriptorTable( } void FileDescriptorTable::RestatInternal(std::set<int>& open_fds, fail_fn_t fail_fn) { + // ART creates a file through memfd for optimization purposes. We make sure + // there is at most one being created. + bool art_memfd_seen = false; + // Iterate through the list of file descriptors we've already recorded // and check whether : // @@ -563,6 +581,14 @@ void FileDescriptorTable::RestatInternal(std::set<int>& open_fds, fail_fn_t fail // FD. } + if (IsArtMemfd(it->second->file_path)) { + if (art_memfd_seen) { + fail_fn("ART fd already seen: " + it->second->file_path); + } else { + art_memfd_seen = true; + } + } + ++it; // Finally, remove the FD from the set of open_fds. We do this last because diff --git a/core/proto/OWNERS b/core/proto/OWNERS index 6ab0fc91d744..74ced8921799 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -3,8 +3,10 @@ # Metrics joeo@google.com singhtejinder@google.com +yanmin@google.com yaochen@google.com yro@google.com +zhouwenjie@google.com # Settings UI per-file settings_enums.proto=tmfang@google.com diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index 7d0629ee6fba..047c095339b0 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -42,6 +42,7 @@ import "frameworks/base/core/proto/android/service/appwidget.proto"; import "frameworks/base/core/proto/android/service/battery.proto"; import "frameworks/base/core/proto/android/service/batterystats.proto"; import "frameworks/base/core/proto/android/service/diskstats.proto"; +import "frameworks/base/core/proto/android/service/dropbox.proto"; import "frameworks/base/core/proto/android/service/graphicsstats.proto"; import "frameworks/base/core/proto/android/service/netstats.proto"; import "frameworks/base/core/proto/android/service/notification.proto"; @@ -329,6 +330,22 @@ message IncidentProto { (section).userdebug_and_eng_only = true ]; + // Dropbox entries split by tags. + optional android.service.dropbox.DropBoxManagerServiceDumpProto dropbox_data_app_crashes = 3027 [ + (section).type = SECTION_DUMPSYS, + (section).args = "dropbox --proto data_app_crash" + ]; + + optional android.service.dropbox.DropBoxManagerServiceDumpProto dropbox_data_app_anr = 3028 [ + (section).type = SECTION_DUMPSYS, + (section).args = "dropbox --proto data_app_anr" + ]; + + optional android.service.dropbox.DropBoxManagerServiceDumpProto dropbox_data_app_native_crash = 3029 [ + (section).type = SECTION_DUMPSYS, + (section).args = "dropbox --proto data_app_native_crash" + ]; + // Reserved for OEMs. extensions 50000 to 100000; } diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto index d01a45c2002a..2f87debce978 100644 --- a/core/proto/android/server/activitymanagerservice.proto +++ b/core/proto/android/server/activitymanagerservice.proto @@ -771,10 +771,11 @@ message ActivityManagerServiceDumpProcessesProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; optional string proc_name = 1; - optional string file = 2 [ (.android.privacy).dest = DEST_EXPLICIT ]; + reserved 2; // file, DEST_EXPLICIT optional int32 pid = 3; optional int32 uid = 4; optional bool is_user_initiated = 5; + optional string uri = 6 [ (.android.privacy).dest = DEST_EXPLICIT ]; } optional Dump dump = 2; } diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index a346a63841fe..69e67d154d27 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -231,7 +231,7 @@ message AppWindowTokenProto { optional WindowTokenProto window_token = 2; optional bool last_surface_showing = 3; optional bool is_waiting_for_transition_start = 4; - optional bool is_really_animating = 5; + optional bool is_animating = 5; optional AppWindowThumbnailProto thumbnail = 6; optional bool fills_parent = 7; optional bool app_stopped = 8; diff --git a/core/proto/android/service/dropbox.proto b/core/proto/android/service/dropbox.proto new file mode 100644 index 000000000000..29fe62b06c95 --- /dev/null +++ b/core/proto/android/service/dropbox.proto @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +package android.service.dropbox; + +import "frameworks/base/core/proto/android/privacy.proto"; + +option java_multiple_files = true; + +// Dump from com.android.server.DropboxManagerService.java. +message DropBoxManagerServiceDumpProto { + option (android.msg_privacy).dest = DEST_EXPLICIT; + + message Entry { + // Time when entry was originally created. + optional int64 time_ms = 1 [ (.android.privacy).dest = DEST_AUTOMATIC ] ; + optional bytes data = 2; + } + repeated Entry entries = 1; +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index c365aaecb6f9..329e8de7dc08 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4511,9 +4511,9 @@ android:protectionLevel="signature" /> <!-- @SystemApi Allows an application to turn on / off quiet mode. - @hide <p>Not for use by third-party applications. --> + @hide --> <permission android:name="android.permission.MODIFY_QUIET_MODE" - android:protectionLevel="signature|privileged" /> + android:protectionLevel="signature|privileged|wellbeing" /> <!-- Allows internal management of the camera framework @hide --> @@ -4647,6 +4647,13 @@ android:protectionLevel="normal" /> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> + <!-- @hide Allow the caller to collect debugging data from processes that otherwise + would require USAGE_STATS. Before sharing this data with other apps, holders + of this permission are REQUIRED to themselves check that the caller has + PACKAGE_USAGE_STATS and OP_GET_USAGE_STATS. --> + <permission android:name="android.permission.PEEK_DROPBOX_DATA" + android:protectionLevel="signature|privileged" /> + <application android:process="system" android:persistent="true" android:hasCode="false" @@ -4728,19 +4735,6 @@ android:excludeFromRecents="true" android:process=":ui"> </activity> - <activity android:name="com.android.internal.app.DumpHeapActivity" - android:theme="@style/Theme.Translucent.NoTitleBar" - android:label="@string/dump_heap_title" - android:finishOnCloseSystemDialogs="true" - android:noHistory="true" - android:excludeFromRecents="true" - android:process=":ui"> - </activity> - <provider android:name="com.android.server.am.DumpHeapProvider" - android:authorities="com.android.server.heapdump" - android:grantUriPermissions="true" - android:multiprocess="false" - android:singleUser="true" /> <activity android:name="android.accounts.ChooseAccountActivity" android:excludeFromRecents="true" diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 4ca0df4a4a0e..7f72a13ff2c7 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> het geen internettoegang nie"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tik vir opsies"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Selnetwerk het nie internettoegang nie"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Netwerk het nie internettoegang nie"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Daar kan nie by private DNS-bediener ingegaan word nie"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Gekoppel"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> het beperkte konnektiwiteit"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Tik om in elk geval te koppel"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Stoor"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Nee, dankie"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Dateer op"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Gaan voort"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"wagwoord"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"adres"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"kredietkaart"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debietkaart"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"betaalkaart"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kaart"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"gebruikernaam"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"e-posadres"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Bly kalm en soek skuiling naby."</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index eb5265ed0e61..f576d04f8f89 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ምንም የበይነ መረብ መዳረሻ የለም"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ለአማራጮች መታ ያድርጉ"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"የተንቀሳቃሽ ስልክ አውታረ መረብ የበይነመረብ መዳረሻ የለውም"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"አውታረ መረብ የበይነመረብ መዳረሻ የለውም"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"የግል ዲኤንኤስ አገልጋይ ሊደረስበት አይችልም"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"ተገናኝቷል"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> የተገደበ ግንኙነት አለው"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"ለማንኛውም ለማገናኘት መታ ያድርጉ"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"አስቀምጥ"</string> <string name="autofill_save_no" msgid="2625132258725581787">"አይ፣ አመሰግናለሁ"</string> <string name="autofill_update_yes" msgid="310358413273276958">"አዘምን"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"ቀጥል"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"የይለፍ ቃል"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"አድራሻ"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"ክሬዲት ካርድ"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"ዴቢት ካርድ"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"የክፍያ ካርድ"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"ካርድ"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"የተጠቃሚ ስም"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"የኢሜይል አድራሻ"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"ረጋ ይበሉና በአቅራቢያ ያለ መጠለያ ይፈልጉ።"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index c9067e5d26cb..7974a08b3809 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -1362,6 +1362,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"لا يتوفّر في <xliff:g id="NETWORK_SSID">%1$s</xliff:g> إمكانية الاتصال بالإنترنت."</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"انقر للحصول على الخيارات."</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"شبكة الجوّال هذه غير متصلة بالإنترنت"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"الشبكة غير متصلة بالإنترنت"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"لا يمكن الوصول إلى خادم أسماء نظام نطاقات خاص"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"تمّ الاتصال."</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"إمكانية اتصال <xliff:g id="NETWORK_SSID">%1$s</xliff:g> محدودة."</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"يمكنك النقر للاتصال على أي حال."</string> @@ -2101,9 +2104,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"حفظ"</string> <string name="autofill_save_no" msgid="2625132258725581787">"لا، شكرًا"</string> <string name="autofill_update_yes" msgid="310358413273276958">"تعديل"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"متابعة"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"كلمة مرور"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"عنوان"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"بطاقة ائتمان"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"بطاقة السحب الآلي"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"بطاقة الدفع"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"بطاقة"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"اسم المستخدم"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"عنوان البريد الإلكتروني"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"يُرجى الثبات والبحث عن ملاذ بالجوار."</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 7a89b654da19..018ca7e9c8ff 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -1274,6 +1274,12 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ৰ ইণ্টাৰনেটৰ এক্সেছ নাই"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"অধিক বিকল্পৰ বাবে টিপক"</string> + <!-- no translation found for mobile_no_internet (1445208572588803493) --> + <skip /> + <!-- no translation found for other_networks_no_internet (1553338015597653827) --> + <skip /> + <!-- no translation found for private_dns_broken_detailed (4293356177543535578) --> + <skip /> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"সংযোগ কৰা হ’ল"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ৰ সকলো সেৱাৰ এক্সেছ নাই"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"যিকোনো প্ৰকাৰে সংযোগ কৰিবলৈ টিপক"</string> @@ -1961,9 +1967,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"ছেভ কৰক"</string> <string name="autofill_save_no" msgid="2625132258725581787">"নালাগে, ধন্যবাদ"</string> <string name="autofill_update_yes" msgid="310358413273276958">"আপডে’ট কৰক"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"অব্যাহত ৰাখক"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"পাছৱৰ্ড"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"ঠিকনা"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"ক্ৰেডিট কাৰ্ড"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"ডেবিট কাৰ্ড"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"পৰিশোধৰ বাবে ব্যৱহাৰ কৰা কার্ড"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"কাৰ্ড"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"ব্যৱহাৰকাৰীৰ নাম"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"ইমেইল ঠিকনা"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"শান্ত হৈ থাকক আৰু ওচৰৰ ক\'ৰবাত আশ্ৰয় বিচাৰক।"</string> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index 21dbf03eaaff..5b135cffbeae 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> üçün internet girişi əlçatan deyil"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Seçimlər üçün tıklayın"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobil şəbəkənin internetə girişi yoxdur"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Şəbəkənin internetə girişi yoxdur"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Özəl DNS serverinə giriş mümkün deyil"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Qoşuldu"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> bağlantını məhdudlaşdırdı"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"İstənilən halda klikləyin"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Yadda saxlayın"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Xeyr, çox sağ olun"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Yeniləyin"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Davam edin"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"parol"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"ünvan"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"kredit kartı"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debet kart"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"ödəniş kartı"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kart"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"istifadəçi adı"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"e-poçt ünvanı"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Sakit qalın və yaxınlıqda sığınacaq axtarın."</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index dbb788dd2513..02b098fce63a 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -1296,6 +1296,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Dodirnite za opcije"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobilna mreža nema pristup internetu"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Mreža nema pristup internetu"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Pristup privatnom DNS serveru nije uspeo"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Povezano je"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu vezu"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Dodirnite da biste se ipak povezali"</string> @@ -1996,9 +1999,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Sačuvaj"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Ne, hvala"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Ažuriraj"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Nastavi"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"lozinka"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"adresa"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"kreditna kartica"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debitna kartica"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"platna kartica"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kartica"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"korisničko ime"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"imejl adresa"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Ostanite mirni i potražite sklonište u okolini."</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index bae2640bb893..4060ed7ae813 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -1318,6 +1318,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> не мае доступу ў інтэрнэт"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Дакраніцеся, каб убачыць параметры"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Мабільная сетка не мае доступу ў інтэрнэт"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Сетка не мае доступу ў інтэрнэт"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Не ўдалося атрымаць доступ да прыватнага DNS-сервера"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Падключана"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> мае абмежаваную магчымасць падключэння"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Націсніце, каб падключыцца"</string> @@ -2031,9 +2034,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Захаваць"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Не, дзякуй"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Абнавіць"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Працягнуць"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"пароль"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"адрас"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"крэдытная картка"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"дэбетовая картка"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"плацежная картка"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"картка"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"карыстальнік"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"адрас электроннай пошты"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Заставайцеся спакойнымі і пашукайце прытулак паблізу."</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index e7b309028cce..b0cea11e295e 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> няма достъп до интернет"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Докоснете за опции"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Мобилната мрежа няма достъп до интернет"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Мрежата няма достъп до интернет"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Не може да се осъществи достъп до частния DNS сървър"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Установена е връзка"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничена свързаност"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Докоснете, за да се свържете въпреки това"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Запазване"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Не, благодаря"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Актуализиране"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Напред"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"Паролата"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"Адресът"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"Кредитната карта"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"дебитна карта"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"карта за плащане"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"карта"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"потребителско име"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"имейл адрес"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Запазете спокойствие и потърсете убежище в района."</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index beb08b22e953..514a6ceb0f08 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-এর ইন্টারনেটে অ্যাক্সেস নেই"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"বিকল্পগুলির জন্য আলতো চাপুন"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"মোবাইল নেটওয়ার্কে কোনও ইন্টারনেট অ্যাক্সেস নেই"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"নেটওয়ার্কে কোনও ইন্টারনেট অ্যাক্সেস নেই"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"ব্যক্তিগত ডিএনএস সার্ভার অ্যাক্সেস করা যাবে না"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"কানেক্ট করা হয়েছে"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-এর সীমিত কানেক্টিভিটি আছে"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"তবুও কানেক্ট করতে ট্যাপ করুন"</string> @@ -1962,9 +1965,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"সেভ করুন"</string> <string name="autofill_save_no" msgid="2625132258725581787">"না থাক"</string> <string name="autofill_update_yes" msgid="310358413273276958">"আপডেট করুন"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"চালিয়ে যান"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"পাসওয়ার্ড"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"ঠিকানা"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"ক্রেডিট কার্ড"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"ডেবিট কার্ড"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"পেমেন্টের কার্ড"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"কার্ড"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"ইউজারনেম"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"ইমেল ঠিকানা"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"শান্ত থাকুন, আশেপাশে আশ্রয় খুঁজুন।"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index 862beee9d53a..1ddc0e2862ed 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -1298,6 +1298,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"Mreža <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Dodirnite za opcije"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobilna mreža nema pristup internetu"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Mreža nema pristup internetu"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Nije moguće pristupiti privatnom DNS serveru"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Povezano"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"Mreža <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu povezivost"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Dodirnite da se ipak povežete"</string> @@ -1998,9 +2001,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Sačuvaj"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Ne, hvala"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Ažuriraj"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Nastavi"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"lozinka"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"adresa"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"kreditna kartica"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debitna kartica"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"kartica za plaćanje"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kartica"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"korisničko ime"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"adresa e-pošte"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Ostanite smireni i potražite sklonište u blizini."</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 28b9a160ba46..fe7f87c63879 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -532,21 +532,21 @@ <string name="biometric_not_recognized" msgid="5770511773560736082">"No s\'ha reconegut"</string> <string name="biometric_error_canceled" msgid="349665227864885880">"S\'ha cancel·lat l\'autenticació"</string> <string name="biometric_error_device_not_secured" msgid="6583143098363528349">"No s\'ha establert cap PIN, patró o contrasenya"</string> - <string name="fingerprint_acquired_partial" msgid="735082772341716043">"S\'ha detectat una empremta dactilar parcial. Torna-ho a provar."</string> - <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"No s\'ha pogut processar l\'empremta dactilar. Torna-ho a provar."</string> + <string name="fingerprint_acquired_partial" msgid="735082772341716043">"S\'ha detectat una empremta digital parcial. Torna-ho a provar."</string> + <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"No s\'ha pogut processar l\'empremta digital. Torna-ho a provar."</string> <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"El sensor d\'empremtes dactilars està brut. Neteja\'l i torna-ho a provar."</string> <string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"El dit s\'ha mogut massa ràpid. Torna-ho a provar."</string> <string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"El dit s\'ha mogut massa lentament. Torna-ho a provar."</string> <string-array name="fingerprint_acquired_vendor"> </string-array> - <string name="fingerprint_authenticated" msgid="5309333983002526448">"L\'empremta dactilar s\'ha autenticat"</string> + <string name="fingerprint_authenticated" msgid="5309333983002526448">"L\'empremta digital s\'ha autenticat"</string> <string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Cara autenticada"</string> <string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Cara autenticada; prem el botó per confirmar"</string> <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"El maquinari per a empremtes dactilars no està disponible."</string> - <string name="fingerprint_error_no_space" msgid="1055819001126053318">"L\'empremta dactilar no es pot desar. Suprimeix-ne una."</string> - <string name="fingerprint_error_timeout" msgid="3927186043737732875">"S\'ha esgotat el temps d\'espera per a l\'empremta dactilar. Torna-ho a provar."</string> - <string name="fingerprint_error_canceled" msgid="4402024612660774395">"S\'ha cancel·lat l\'operació d\'empremta dactilar."</string> - <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"L\'usuari ha cancel·lat l\'operació d\'empremta dactilar."</string> + <string name="fingerprint_error_no_space" msgid="1055819001126053318">"L\'empremta digital no es pot desar. Suprimeix-ne una."</string> + <string name="fingerprint_error_timeout" msgid="3927186043737732875">"S\'ha esgotat el temps d\'espera per a l\'empremta digital. Torna-ho a provar."</string> + <string name="fingerprint_error_canceled" msgid="4402024612660774395">"S\'ha cancel·lat l\'operació d\'empremta digital."</string> + <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"L\'usuari ha cancel·lat l\'operació d\'empremta digital."</string> <string name="fingerprint_error_lockout" msgid="5536934748136933450">"S\'han produït massa intents. Torna-ho a provar més tard."</string> <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"S\'han fet massa intents. S\'ha desactivat el sensor d\'empremtes dactilars."</string> <string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Torna-ho a provar."</string> @@ -555,7 +555,7 @@ <string name="fingerprint_name_template" msgid="5870957565512716938">"Dit <xliff:g id="FINGERID">%d</xliff:g>"</string> <string-array name="fingerprint_error_vendor"> </string-array> - <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icona d\'empremta dactilar"</string> + <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icona d\'empremta digital"</string> <string name="permlab_manageFace" msgid="7262837876352591553">"gestiona el maquinari de desbloqueig facial"</string> <string name="permdesc_manageFace" msgid="8919637120670185330">"Permet que l\'aplicació afegeixi i suprimeixi plantilles de cares que es puguin fer servir."</string> <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"utilitza el maquinari de desbloqueig facial"</string> @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> no té accés a Internet"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Toca per veure les opcions"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"La xarxa mòbil no té accés a Internet"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"La xarxa no té accés a Internet"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"No es pot accedir al servidor DNS privat"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Connectat"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> té una connectivitat limitada"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Toca per connectar igualment"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Desa"</string> <string name="autofill_save_no" msgid="2625132258725581787">"No, gràcies"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Actualitza"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Continua"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"contrasenya"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"adreça"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"targeta de crèdit"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"targeta de dèbit"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"targeta de pagament"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"targeta"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"nom d\'usuari"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"adreça electrònica"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Mantén la calma i busca refugi a prop."</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 8ba6f3425103..b6841936325e 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -1318,6 +1318,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"Síť <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nemá přístup k internetu"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Klepnutím zobrazíte možnosti"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobilní síť nemá přístup k internetu"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Síť nemá přístup k internetu"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Nelze získat přístup k soukromému serveru DNS"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Připojeno"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"Síť <xliff:g id="NETWORK_SSID">%1$s</xliff:g> umožňuje jen omezené připojení"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Klepnutím se i přesto připojíte"</string> @@ -2031,9 +2034,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Uložit"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Ne, děkuji"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Aktualizovat"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Pokračovat"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"heslo"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"adresa"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"platební karta"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debetní karta"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"platební karta"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"karta"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"uživatelské jméno"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"e-mailová adresa"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Zachovejte klid a přesuňte se na bezpečné místo v okolí."</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 0d40688ec427..22f1e5db6abd 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internetforbindelse"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tryk for at se valgmuligheder"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobilnetværket har ingen internetadgang"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Netværket har ingen internetadgang"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Der er ikke adgang til den private DNS-server"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Der er oprettet forbindelse"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begrænset forbindelse"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Tryk for at oprette forbindelse alligevel"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Gem"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Nej tak"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Opdater"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Fortsæt"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"adgangskode"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"adresse"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"kreditkort"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"betalingskort"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"betalingskort"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kort"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"brugernavn"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"mailadresse"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Bevar roen, og søg ly i nærheden."</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index f0513456e6e6..b74cd7e1bd08 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> hat keinen Internetzugriff"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Für Optionen tippen"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobiles Netzwerk hat keinen Internetzugriff"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Netzwerk hat keinen Internetzugriff"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Auf den privaten DNS-Server kann nicht zugegriffen werden"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Verbunden"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"Schlechte Verbindung mit <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Tippen, um die Verbindung trotzdem herzustellen"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Speichern"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Nein danke"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Aktualisieren"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Fortsetzen"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"Passwort"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"Adresse"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"Kreditkarte"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"Debitkarte"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"Zahlungskarte"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"Karte"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"Nutzername"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"E-Mail-Adresse"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Bleibe ruhig und suche in der Nähe Schutz."</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index a360de0f38b0..4fb03d6dc328 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"Η εφαρμογή <xliff:g id="NETWORK_SSID">%1$s</xliff:g> δεν έχει πρόσβαση στο διαδίκτυο"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Πατήστε για να δείτε τις επιλογές"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Το δίκτυο κινητής τηλεφωνίας δεν έχει πρόσβαση στο διαδίκτυο."</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Το δίκτυο δεν έχει πρόσβαση στο διαδίκτυο."</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Δεν είναι δυνατή η πρόσβαση στον ιδιωτικό διακομιστή DNS."</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Συνδέθηκε"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"Το δίκτυο <xliff:g id="NETWORK_SSID">%1$s</xliff:g> έχει περιορισμένη συνδεσιμότητα"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Πατήστε για σύνδεση ούτως ή άλλως"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Αποθήκευση"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Όχι, ευχαριστώ"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Ενημέρωση"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Συνέχεια"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"κωδικός πρόσβασης"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"διεύθυνση"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"πιστωτική κάρτα"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"χρεωστική κάρτα"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"κάρτα πληρωμής"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"κάρτα"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"όνομα χρήστη"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"διεύθυνση email"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Μείνετε ψύχραιμοι και αναζητήστε κάποιο κοντινό καταφύγιο."</string> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 1e0e93a5f1d3..88fb0fdadd43 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tap for options"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobile network has no Internet access"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Network has no Internet access"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Private DNS server cannot be accessed"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Connected"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Tap to connect anyway"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Save"</string> <string name="autofill_save_no" msgid="2625132258725581787">"No, thanks"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Update"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Continue"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"password"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"address"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"credit card"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debit card"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"payment card"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"card"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"username"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"email address"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Stay calm and seek shelter nearby."</string> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 4deaf2e0cf14..8a1c2c415ff2 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tap for options"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobile network has no Internet access"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Network has no Internet access"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Private DNS server cannot be accessed"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Connected"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Tap to connect anyway"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Save"</string> <string name="autofill_save_no" msgid="2625132258725581787">"No, thanks"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Update"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Continue"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"password"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"address"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"credit card"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debit card"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"payment card"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"card"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"username"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"email address"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Stay calm and seek shelter nearby."</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 1e0e93a5f1d3..88fb0fdadd43 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tap for options"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobile network has no Internet access"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Network has no Internet access"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Private DNS server cannot be accessed"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Connected"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Tap to connect anyway"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Save"</string> <string name="autofill_save_no" msgid="2625132258725581787">"No, thanks"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Update"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Continue"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"password"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"address"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"credit card"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debit card"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"payment card"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"card"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"username"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"email address"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Stay calm and seek shelter nearby."</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index 1e0e93a5f1d3..88fb0fdadd43 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tap for options"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobile network has no Internet access"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Network has no Internet access"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Private DNS server cannot be accessed"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Connected"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Tap to connect anyway"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Save"</string> <string name="autofill_save_no" msgid="2625132258725581787">"No, thanks"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Update"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Continue"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"password"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"address"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"credit card"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debit card"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"payment card"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"card"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"username"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"email address"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Stay calm and seek shelter nearby."</string> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index 5fe1bb19a06e..fbb1cc748bd7 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no internet access"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tap for options"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobile network has no internet access"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Network has no internet access"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Private DNS server cannot be accessed"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Connected"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Tap to connect anyway"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Save"</string> <string name="autofill_save_no" msgid="2625132258725581787">"No thanks"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Update"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Continue"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"password"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"address"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"credit card"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debit card"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"payment card"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"card"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"username"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"email address"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Stay calm and seek shelter nearby."</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index feb4aeeb5396..7fe219e95684 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>no tiene acceso a Internet"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Presiona para ver opciones"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"La red móvil no tiene acceso a Internet"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"La red no tiene acceso a Internet"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"No se puede acceder al servidor DNS privado"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Se estableció conexión"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiene conectividad limitada"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Presiona para conectarte de todas formas"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Guardar"</string> <string name="autofill_save_no" msgid="2625132258725581787">"No, gracias"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Actualizar"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Continuar"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"contraseña"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"dirección"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"tarjeta de crédito"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"tarjeta de débito"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"tarjeta de pago"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"tarjeta"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"nombre de usuario"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"dirección de correo electrónico"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Mantén la calma y busca un refugio cercano."</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 75510b3c7395..35e2c384c65e 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> no tiene acceso a Internet"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Toca para ver opciones"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"La red móvil no tiene acceso a Internet"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"La red no tiene acceso a Internet"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"No se ha podido acceder al servidor DNS privado"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Conectado"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiene una conectividad limitada"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Toca para conectarte de todas formas"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Guardar"</string> <string name="autofill_save_no" msgid="2625132258725581787">"No, gracias"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Actualizar"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Continuar"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"contraseña"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"dirección"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"tarjeta de crédito"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"tarjeta de débito"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"tarjeta de pago"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"tarjeta"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"nombre de usuario"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"dirección de correo electrónico"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Mantén la calma y busca refugio en algún lugar cercano."</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 0e7befdf9227..a8f546d015e5 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"Võrgul <xliff:g id="NETWORK_SSID">%1$s</xliff:g> puudub Interneti-ühendus"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Puudutage valikute nägemiseks"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobiilsidevõrgul puudub Interneti-ühendus"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Võrgul puudub Interneti-ühendus"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Privaatsele DNS-serverile ei pääse juurde"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Ühendatud"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"Võrgu <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ühendus on piiratud"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Puudutage, kui soovite siiski ühenduse luua"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Salvesta"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Tänan, ei"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Värskenda"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Jätka"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"parool"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"aadress"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"krediitkaart"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"deebetkaart"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"maksekaart"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kaart"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"kasutajanimi"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"e-posti aadress"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Jääge rahulikuks ja otsige lähedusest peavarju."</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 22391dc2ba30..d87bdfb51c60 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -861,7 +861,7 @@ <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Eredua ahaztu zaizu?"</string> <string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"Kontua desblokeatzea"</string> <string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"Eredua marrazteko saiakera gehiegi egin dira"</string> - <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"Desblokeatzeko, hasi saioa Google kontuarekin."</string> + <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"Desblokeatzeko, hasi saioa Google-ko kontuarekin."</string> <string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Erabiltzaile-izena (helbide elektronikoa)"</string> <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Pasahitza"</string> <string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Hasi saioa"</string> @@ -1133,7 +1133,7 @@ <string name="whichApplication" msgid="4533185947064773386">"Gauzatu ekintza hau erabilita:"</string> <string name="whichApplicationNamed" msgid="8260158865936942783">"Osatu ekintza %1$s erabiliz"</string> <string name="whichApplicationLabel" msgid="7425855495383818784">"Osatu ekintza"</string> - <string name="whichViewApplication" msgid="3272778576700572102">"Ireki honekin:"</string> + <string name="whichViewApplication" msgid="3272778576700572102">"Ireki honekin"</string> <string name="whichViewApplicationNamed" msgid="2286418824011249620">"Irekin %1$s aplikazioarekin"</string> <string name="whichViewApplicationLabel" msgid="2666774233008808473">"Ireki"</string> <string name="whichOpenHostLinksWith" msgid="3788174881117226583">"Ireki <xliff:g id="HOST">%1$s</xliff:g> ostalariko estekak honekin:"</string> @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"Ezin da konektatu Internetera <xliff:g id="NETWORK_SSID">%1$s</xliff:g> sarearen bidez"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Sakatu aukerak ikusteko"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Sare mugikorra ezin da konektatu Internetera"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Sarea ezin da konektatu Internetera"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Ezin da atzitu DNS zerbitzari pribatua"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Konektatuta"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> sareak konektagarritasun murriztua du"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Sakatu hala ere konektatzeko"</string> @@ -1645,7 +1648,7 @@ <string name="kg_invalid_puk" msgid="3638289409676051243">"Idatzi berriro PUK kode zuzena. Hainbat saiakera oker eginez gero, betiko desgaituko da SIMa."</string> <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN kodeak ez datoz bat"</string> <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Eredua marrazteko saiakera gehiegi egin dira"</string> - <string name="kg_login_instructions" msgid="1100551261265506448">"Desblokeatzeko, hasi saioa Google kontuarekin."</string> + <string name="kg_login_instructions" msgid="1100551261265506448">"Desblokeatzeko, hasi saioa Google-ko kontuarekin."</string> <string name="kg_login_username_hint" msgid="5718534272070920364">"Erabiltzaile-izena (helbide elektronikoa)"</string> <string name="kg_login_password_hint" msgid="9057289103827298549">"Pasahitza"</string> <string name="kg_login_submit_button" msgid="5355904582674054702">"Hasi saioa"</string> @@ -1962,9 +1965,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Gorde"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Ez, eskerrik asko"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Eguneratu"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Egin aurrera"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"pasahitza"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"helbidea"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"kreditu-txartela"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"zordunketa-txartela"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"ordainketa-txartela"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"txartela"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"erabiltzaile-izena"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"helbide elektronikoa"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Ez larritu eta bilatu babesleku bat inguruan."</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 33421c0448c8..163826383cd4 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> به اینترنت دسترسی ندارد"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"برای گزینهها ضربه بزنید"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"شبکه تلفن همراه به اینترنت دسترسی ندارد"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"شبکه به اینترنت دسترسی ندارد"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"سرور DNS خصوصی قابل دسترسی نیست"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"متصل"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> اتصال محدودی دارد"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"بههرصورت، برای اتصال ضربه بزنید"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"ذخیره"</string> <string name="autofill_save_no" msgid="2625132258725581787">"نه سپاسگزارم"</string> <string name="autofill_update_yes" msgid="310358413273276958">"بهروزرسانی"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"ادامه"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"گذرواژه"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"نشانی"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"کارت اعتباری"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"کارت نقدی"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"کارت پرداخت"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"کارت"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"نام کاربری"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"نشانی ایمیل"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"آرام باشید و پناهگاهی در این اطراف پیدا کنید."</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 124a16071f4c..5b050c3a1680 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ei ole yhteydessä internetiin"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Näytä vaihtoehdot napauttamalla."</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobiiliverkko ei ole yhteydessä internetiin"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Verkko ei ole yhteydessä internetiin"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Ei pääsyä yksityiselle DNS-palvelimelle"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Yhdistetty"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> toimii rajoitetulla yhteydellä"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Yhdistä napauttamalla"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Tallenna"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Ei kiitos"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Muuta"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Jatka"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"salasana"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"osoite"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"luottokortti"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debit-kortti"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"maksukortti"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kortti"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"käyttäjänimi"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"sähköpostiosoite"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Pysy rauhallisena ja hakeudu lähimpään suojapaikkaan."</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index ecfdcd29e785..32530d3cfb60 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"Le réseau <xliff:g id="NETWORK_SSID">%1$s</xliff:g> n\'offre aucun accès à Internet"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Touchez pour afficher les options"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Le réseau cellulaire n\'offre aucun accès à Internet"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Le réseau n\'offre aucun accès à Internet"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Impossible d\'accéder au serveur DNS privé"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Connecté"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"Le réseau <xliff:g id="NETWORK_SSID">%1$s</xliff:g> offre une connectivité limitée"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Touchez pour vous connecter quand même"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Enregistrer"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Non, merci"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Mettre à jour"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Continuer"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"mot de passe"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"adresse"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"carte de crédit"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"carte de débit"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"carte de paiement"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"carte"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"nom d\'utilisateur"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"adresse de courriel"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Restez calme et cherchez un abri à proximité."</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 1a44b799cbdd..b5b0a56659cd 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"Aucune connexion à Internet pour <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Appuyez ici pour afficher des options."</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Le réseau mobile ne dispose d\'aucun accès à Internet"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Le réseau ne dispose d\'aucun accès à Internet"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Impossible d\'accéder au serveur DNS privé"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Connecté"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"La connectivité de <xliff:g id="NETWORK_SSID">%1$s</xliff:g> est limitée"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Appuyer pour se connecter quand même"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Enregistrer"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Non, merci"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Mettre à jour"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Continuer"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"mot de passe"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"adresse"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"carte de paiement"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"carte de débit"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"carte de paiement"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"carte"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"nom d\'utilisateur"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"adresse e-mail"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Restez calme et cherchez un abri à proximité."</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 933fbabefe90..bf3b3bd7adc4 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> non ten acceso a Internet"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Toca para ver opcións."</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"A rede de telefonía móbil non ten acceso a Internet"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"A rede non ten acceso a Internet"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Non se puido acceder ao servidor DNS privado"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Estableceuse conexión"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"A conectividade de <xliff:g id="NETWORK_SSID">%1$s</xliff:g> é limitada"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Toca para conectarte de todas formas"</string> @@ -1962,9 +1965,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Gardar"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Non, grazas"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Actualizar"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Continuar"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"contrasinal"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"enderezo"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"tarxeta de crédito"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"tarxeta de débito"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"tarxeta de pago"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"tarxeta"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"nome de usuario"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"enderezo de correo electrónico"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Mantén a calma e busca refuxio cerca."</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 05e5a4eb0965..66bba2372b44 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"વિકલ્પો માટે ટૅપ કરો"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"મોબાઇલ નેટવર્ક કોઈ ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"નેટવર્ક કોઈ ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"ખાનગી DNS સર્વર ઍક્સેસ કરી શકાતા નથી"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"કનેક્ટેડ"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> મર્યાદિત કનેક્ટિવિટી ધરાવે છે"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"છતાં કનેક્ટ કરવા માટે ટૅપ કરો"</string> @@ -1962,9 +1965,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"સાચવો"</string> <string name="autofill_save_no" msgid="2625132258725581787">"ના, આભાર"</string> <string name="autofill_update_yes" msgid="310358413273276958">"અપડેટ કરો"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"ચાલુ રાખો"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"પાસવર્ડ"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"સરનામું"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"ક્રેડિટ કાર્ડ"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"ડેબિટ કાર્ડ"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"ચુકવણી કાર્ડ"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"કાર્ડ"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"વપરાશકર્તાનામ"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"ઇમેઇલ સરનામું"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"શાંત રહો અને નજીકમાં આશ્રય લો."</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index f16ad65ded26..717c7ae519b3 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> का इंटरनेट नहीं चल रहा है"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"विकल्पों के लिए टैप करें"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"मोबाइल नेटवर्क पर इंटरनेट ऐक्सेस नहीं है"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"इस नेटवर्क पर इंटरनेट ऐक्सेस नहीं है"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"निजी डीएनएस सर्वर को ऐक्सेस नहीं किया जा सकता"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"जुड़ गया है"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> की कनेक्टिविटी सीमित है"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"फिर भी कनेक्ट करने के लिए टैप करें"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"सेव करें"</string> <string name="autofill_save_no" msgid="2625132258725581787">"नहीं, धन्यवाद"</string> <string name="autofill_update_yes" msgid="310358413273276958">"अपडेट करें"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"जारी रखें"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"पासवर्ड"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"पता"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"क्रेडिट कार्ड"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"डेबिट कार्ड"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"भुगतान कार्ड"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"कार्ड"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"उपयोगकर्ता का नाम"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"ईमेल पता"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"शांत रहें और आस-पास शरण लेने की जगह तलाशें."</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 45c39f2fb5af..00da024ed54e 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -1296,6 +1296,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Dodirnite za opcije"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobilna mreža nema pristup internetu"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Mreža nema pristup internetu"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Nije moguće pristupiti privatnom DNS poslužitelju"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Povezano"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu povezivost"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Dodirnite da biste se ipak povezali"</string> @@ -1996,9 +1999,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Spremi"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Ne, hvala"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Ažuriraj"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Nastavi"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"zaporku"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"adresu"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"kreditnu karticu"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debitna kartica"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"kartica za plaćanje"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kartica"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"korisničko ime"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"e-adresa"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Ostanite mirni i potražite sklonište u blizini."</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index aac2d76de845..2958ca629278 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"A(z) <xliff:g id="NETWORK_SSID">%1$s</xliff:g> hálózaton nincs internet-hozzáférés"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Koppintson a beállítások megjelenítéséhez"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"A mobilhálózaton nincs internet-hozzáférés"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"A hálózaton nincs internet-hozzáférés"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"A privát DNS-kiszolgálóhoz nem lehet hozzáférni"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Csatlakozva"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"A(z) <xliff:g id="NETWORK_SSID">%1$s</xliff:g> hálózat korlátozott kapcsolatot biztosít"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Koppintson, ha mindenképpen csatlakozni szeretne"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Mentés"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Nem, köszönöm"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Frissítés"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Tovább"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"jelszó"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"cím"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"hitelkártya"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"bankkártya"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"fizetőkártya"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kártya"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"felhasználónév"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"e-mail-cím"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Őrizze meg nyugalmát, és keressen menedéket a közelben."</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index a5b4e30b64e7..73db059c71a2 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ցանցը չունի մուտք ինտերնետին"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Հպեք՝ ընտրանքները տեսնելու համար"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Բջջային ցանցում ինտերնետ կապ չկա"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Ցանցում ինտերնետ կապ չկա"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Մասնավոր DNS սերվերն անհասանելի է"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Միացված է"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ցանցի կապը սահմանափակ է"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Հպեք՝ միանալու համար"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Պահել"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Ոչ"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Թարմացնել"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Շարունակել"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"գաղտնաբառ"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"հասցե"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"վարկային քարտ"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"դեբետային քարտ"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"վճարային քարտ"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"քարտ"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"օգտանուն"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"էլ․ հասցե"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Պահպանեք հանգստությունը և մոտակայքում ապաստարան փնտրեք:"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 228da880b0d3..662a65486b1f 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tidak memiliki akses internet"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Ketuk untuk melihat opsi"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Jaringan seluler tidak memiliki akses internet"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Jaringan tidak memiliki akses internet"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Server DNS pribadi tidak dapat diakses"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Tersambung"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> memiliki konektivitas terbatas"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Ketuk untuk tetap menyambungkan"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Simpan"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Lain kali"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Update"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Lanjutkan"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"sandi"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"alamat"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"kartu kredit"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"kartu debit"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"kartu pembayaran"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kartu"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"nama pengguna"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"alamat email"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Tetap tenang dan cari tempat berlindung terdekat."</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index d0f802819021..9ae165d58e07 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> er ekki með internetaðgang"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Ýttu til að sjá valkosti"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Farsímakerfið er ekki tengt við internetið"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Netkerfið er ekki tengt við internetið"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Ekki næst í DNS-einkaþjón"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Tengt"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"Tengigeta <xliff:g id="NETWORK_SSID">%1$s</xliff:g> er takmörkuð"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Ýttu til að tengjast samt"</string> @@ -1962,9 +1965,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Vista"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Nei, takk"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Uppfæra"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Áfram"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"aðgangsorð"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"heimilisfang"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"kreditkort"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debetkort"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"greiðslukort"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kort"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"notandanafn"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"netfang"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Sýndu stillingu og leitaðu skjóls."</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 747d9189c8e1..77eafcccbb1c 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -283,7 +283,7 @@ <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Consentire all\'app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> di accedere ai tuoi contatti?"</string> <string name="permgrouplab_location" msgid="7275582855722310164">"Geolocalizz."</string> <string name="permgroupdesc_location" msgid="1346617465127855033">"accedere alla posizione di questo dispositivo"</string> - <string name="permgrouprequest_location" msgid="3788275734953323491">"Consentire all\'app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> di accedere alla posizione del dispositivo?"</string> + <string name="permgrouprequest_location" msgid="3788275734953323491">"Consentire a <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> di accedere alla posizione del dispositivo?"</string> <string name="permgrouprequestdetail_location" msgid="1347189607421252902">"L\'app avrà accesso alla posizione soltanto quando la usi"</string> <string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"Consentire a <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> di accedere <b>sempre</b> alla posizione di questo dispositivo?"</string> <string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"L\'app al momento può accedere alla posizione soltanto mentre la usi"</string> @@ -301,7 +301,7 @@ <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Consentire all\'app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> di registrare audio?"</string> <string name="permgrouplab_activityRecognition" msgid="1565108047054378642">"Attività fisica"</string> <string name="permgroupdesc_activityRecognition" msgid="6949472038320473478">"Consente di accedere all\'attività fisica"</string> - <string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"Vuoi consentire a <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> di accedere alla tua attività fisica?"</string> + <string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"Consentire a <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> di accedere alla tua attività fisica?"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Fotocamera"</string> <string name="permgroupdesc_camera" msgid="3250611594678347720">"scattare foto e registrare video"</string> <string name="permgrouprequest_camera" msgid="1299833592069671756">"Consentire all\'app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> di scattare foto e registrare video?"</string> @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> non ha accesso a Internet"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tocca per le opzioni"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"La rete mobile non ha accesso a Internet"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"La rete non ha accesso a Internet"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Non è possibile accedere al server DNS privato"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Connesso"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ha una connettività limitata"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Tocca per connettere comunque"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Salva"</string> <string name="autofill_save_no" msgid="2625132258725581787">"No, grazie"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Aggiorna"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Continua"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"password"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"indirizzo"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"carta di credito"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"carta di debito"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"carta di pagamento"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"carta"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"nome utente"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"indirizzo email"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Mantieni la calma e cerca riparo nelle vicinanze."</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 4c12e432b6da..462ee36b5f4d 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -1318,6 +1318,12 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"ל-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> אין גישה לאינטרנט"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"הקש לקבלת אפשרויות"</string> + <!-- no translation found for mobile_no_internet (1445208572588803493) --> + <skip /> + <!-- no translation found for other_networks_no_internet (1553338015597653827) --> + <skip /> + <!-- no translation found for private_dns_broken_detailed (4293356177543535578) --> + <skip /> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"הרשת מחוברת"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"הקישוריות של <xliff:g id="NETWORK_SSID">%1$s</xliff:g> מוגבלת"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"כדי להתחבר למרות זאת יש להקיש"</string> @@ -2031,9 +2037,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"שמירה"</string> <string name="autofill_save_no" msgid="2625132258725581787">"לא, תודה"</string> <string name="autofill_update_yes" msgid="310358413273276958">"עדכון"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"המשך"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"סיסמה"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"כתובת"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"כרטיס אשראי"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"כרטיס חיוב"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"כרטיס תשלום"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"כרטיס"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"שם משתמש"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"כתובת אימייל"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"הישאר רגוע וחפש מחסה בקרבת מקום."</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 98600b8cb590..2a2b71b2838b 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -301,7 +301,7 @@ <string name="permgrouprequest_microphone" msgid="9167492350681916038">"音声の録音を「<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>」に許可しますか?"</string> <string name="permgrouplab_activityRecognition" msgid="1565108047054378642">"身体活動"</string> <string name="permgroupdesc_activityRecognition" msgid="6949472038320473478">"身体活動にアクセス"</string> - <string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"身体活動へのアクセスを「<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>」に許可しますか?"</string> + <string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"身体活動データへのアクセスを「<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>」に許可しますか?"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"カメラ"</string> <string name="permgroupdesc_camera" msgid="3250611594678347720">"写真と動画の撮影"</string> <string name="permgrouprequest_camera" msgid="1299833592069671756">"写真と動画の撮影を「<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>」に許可しますか?"</string> @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> はインターネットにアクセスできません"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"タップしてその他のオプションを表示"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"モバイル ネットワークがインターネットに接続されていません"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"ネットワークがインターネットに接続されていません"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"プライベート DNS サーバーにアクセスできません"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"接続しました"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> の接続が制限されています"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"接続するにはタップしてください"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"はい"</string> <string name="autofill_save_no" msgid="2625132258725581787">"いいえ"</string> <string name="autofill_update_yes" msgid="310358413273276958">"更新"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"続行"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"パスワード"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"住所"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"クレジット カード"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"デビットカード"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"支払いカード"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"カード"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"ユーザー名"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"メールアドレス"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"強い揺れに備えてください"</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index cc8ecc2b9e4e..ebc7f2aa6fa7 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-ს არ აქვს ინტერნეტზე წვდომა"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"შეეხეთ ვარიანტების სანახავად"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"მობილურ ქსელს არ აქვს ინტერნეტზე წვდომა"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"ქსელს არ აქვს ინტერნეტზე წვდომა"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"პირად DNS სერვერზე წვდომა შეუძლებელია"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"დაკავშირებულია"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-ის კავშირები შეზღუდულია"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"შეეხეთ, თუ მაინც გსურთ დაკავშირება"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"შენახვა"</string> <string name="autofill_save_no" msgid="2625132258725581787">"არა, გმადლობთ"</string> <string name="autofill_update_yes" msgid="310358413273276958">"განახლება"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"გაგრძელება"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"პაროლი"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"მისამართი"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"საკრედიტო ბარათი"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"სადებეტო ბარათი"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"გადახდის ბარათი"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"ბარათი"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"მომხმარებლის სახელი"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"ელფოსტის მისამართი"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"შეინარჩუნეთ სიმშვიდე და იპოვეთ ახლომდებარე თავშესაფარი."</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index db9fad24f1ef..281db7ae9bcb 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> желісінің интернетті пайдалану мүмкіндігі шектеулі."</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Опциялар үшін түртіңіз"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Мобильдік желі интернетке қосылмаған."</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Желі интернетке қосылмаған."</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Жеке DNS серверіне кіру мүмкін емес."</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Жалғанды"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> желісінің қосылу мүмкіндігі шектеулі."</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Бәрібір жалғау үшін түртіңіз."</string> @@ -1962,9 +1965,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Сақтау"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Жоқ, рақмет"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Жаңарту"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Жалғастыру"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"құпия сөз"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"мекенжай"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"несие картасы"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"дебеттік карта"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"төлем картасы"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"карта"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"пайдаланушы аты"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"электрондық пошта мекенжайы"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Сабыр сақтап, жақын жерден баспана іздеңіз."</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 021da3ea4a42..532e3c7e9808 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -1276,6 +1276,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> មិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ប៉ះសម្រាប់ជម្រើស"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"បណ្ដាញទូរសព្ទចល័តមិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"បណ្ដាញមិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"មិនអាចចូលប្រើម៉ាស៊ីនមេ DNS ឯកជនបានទេ"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"បានភ្ជាប់"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> មានការតភ្ជាប់មានកម្រិត"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"មិនអីទេ ចុចភ្ជាប់ចុះ"</string> @@ -1963,9 +1966,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"រក្សាទុក"</string> <string name="autofill_save_no" msgid="2625132258725581787">"ទេ អរគុណ"</string> <string name="autofill_update_yes" msgid="310358413273276958">"ធ្វើបច្ចុប្បន្នភាព"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"បន្ត"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"ពាក្យសម្ងាត់"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"អាសយដ្ឋាន"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"បណ្ណឥណទាន"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"បណ្ណឥណពន្ធ"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"កាតទូទាត់ប្រាក់"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"កាត"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"ឈ្មោះអ្នកប្រើប្រាស់"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"អាសយដ្ឋានអ៊ីមែល"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"សូមរក្សាភាពស្ងប់ស្ងាត់ ហើយស្វែងរកជម្រកសុវត្ថិភាពដែលនៅជិត។"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index edcb6d1dfee0..d1317e967c34 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -1274,6 +1274,12 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಲ್ಲ"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ಆಯ್ಕೆಗಳಿಗೆ ಟ್ಯಾಪ್ ಮಾಡಿ"</string> + <!-- no translation found for mobile_no_internet (1445208572588803493) --> + <skip /> + <!-- no translation found for other_networks_no_internet (1553338015597653827) --> + <skip /> + <!-- no translation found for private_dns_broken_detailed (4293356177543535578) --> + <skip /> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ಸೀಮಿತ ಸಂಪರ್ಕ ಕಲ್ಪಿಸುವಿಕೆಯನ್ನು ಹೊಂದಿದೆ"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"ಹೇಗಾದರೂ ಸಂಪರ್ಕಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> @@ -1962,9 +1968,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"ಉಳಿಸಿ"</string> <string name="autofill_save_no" msgid="2625132258725581787">"ಬೇಡ"</string> <string name="autofill_update_yes" msgid="310358413273276958">"ಅಪ್ಡೇಟ್"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"ಮುಂದುವರಿಯಿರಿ"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"ಪಾಸ್ವರ್ಡ್"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"ವಿಳಾಸ"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"ಡೆಬಿಟ್ ಕಾರ್ಡ್"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"ಪಾವತಿ ಕಾರ್ಡ್"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"ಕಾರ್ಡ್"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"ಬಳಕೆದಾರರ ಹೆಸರು"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"ಇಮೇಲ್ ವಿಳಾಸ"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"ಶಾಂತರಾಗಿರಿ ಮತ್ತು ಸಮೀಪದಲ್ಲೆಲ್ಲಾದರೂ ಆಶ್ರಯ ಪಡೆದುಕೊಳ್ಳಿ."</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 2f06beeacc6b..acc6e950666f 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>이(가) 인터넷에 액세스할 수 없습니다."</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"탭하여 옵션 보기"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"모바일 네트워크에 인터넷이 연결되어 있지 않습니다."</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"네트워크에 인터넷이 연결되어 있지 않습니다."</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"비공개 DNS 서버에 액세스할 수 없습니다."</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"연결되었습니다."</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>에서 연결을 제한했습니다."</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"계속 연결하려면 탭하세요."</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"저장"</string> <string name="autofill_save_no" msgid="2625132258725581787">"사용 안함"</string> <string name="autofill_update_yes" msgid="310358413273276958">"업데이트"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"계속"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"비밀번호"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"주소"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"신용카드"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"체크카드"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"결제 카드"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"카드"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"사용자 이름"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"이메일 주소"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"침착하게 가까운 대피소를 찾으세요."</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 2353ed09c9e3..dcb766d2bd8c 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> Интернетке туташуусу жок"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Параметрлерди ачуу үчүн таптап коюңуз"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Мобилдик Интернет жок"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Тармактын Интернет байланышы жок"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Жеке DNS сервери жеткиликсиз"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Туташты"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> байланышы чектелген"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Баары бир туташуу үчүн таптаңыз"</string> @@ -1668,7 +1671,7 @@ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Алып салуу"</string> <string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Сунушталган деңгээлден да катуулатып уккуңуз келеби?\n\nМузыканы узакка чейин катуу уксаңыз, угууңуз начарлап кетиши мүмкүн."</string> - <string name="accessibility_shortcut_warning_dialog_title" msgid="8404780875025725199">"Атайын мүмкүнчүлүктөр функциясынын кыска жолу колдонулсунбу?"</string> + <string name="accessibility_shortcut_warning_dialog_title" msgid="8404780875025725199">"Ыкчам иштетесизби?"</string> <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Атайын мүмкүнчүлүктөр функциясын пайдалануу үчүн, ал күйгүзүлгөндө, үндү катуулатып/акырындаткан эки баскычты тең үч секунддай кое бербей басып туруңуз.\n\n Учурдагы атайын мүмкүнчүлүктөрдүн жөндөөлөрү:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\nЖөндөөлөр > Атайын мүмкүнчүлүктөр бөлүмүнөн өзгөртө аласыз."</string> <string name="disable_accessibility_shortcut" msgid="627625354248453445">"Кыска жолду өчүрүү"</string> <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Кыска жолду колдонуу"</string> @@ -1963,9 +1966,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Сактоо"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Жок, рахмат"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Жаңыртуу"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Улантуу"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"сырсөз"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"дарек"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"насыя картасы"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"дебет картасы"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"төлөм картасы"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"карта"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"колдонуучунун аты"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"электрондук почта дареги"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Эс алып, жакын жерден калканч издеңиз."</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index a83fae1f55d8..6ef4cca18ef4 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ແຕະເພື່ອເບິ່ງຕົວເລືອກ"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"ເຄືອຂ່າຍມືຖືບໍ່ສາມາດເຂົ້າເຖິງອິນເຕີເນັດໄດ້"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"ເຄືອຂ່າຍບໍ່ສາມາດເຂົ້າເຖິງອິນເຕີເນັດໄດ້"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"ບໍ່ສາມາດເຂົ້າເຖິງເຊີບເວີ DNS ສ່ວນຕົວໄດ້"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"ເຊື່ອມຕໍ່ແລ້ວ"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ມີການເຊື່ອມຕໍ່ທີ່ຈຳກັດ"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"ແຕະເພື່ອຢືນຢັນການເຊື່ອມຕໍ່"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"ບັນທຶກ"</string> <string name="autofill_save_no" msgid="2625132258725581787">"ບໍ່, ຂອບໃຈ"</string> <string name="autofill_update_yes" msgid="310358413273276958">"ອັບເດດ"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"ສືບຕໍ່"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"ລະຫັດຜ່ານ"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"ທີ່ຢູ່"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"ບັດເຄຣດິດ"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"ບັດເດບິດ"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"ບັດຈ່າຍເງິນ"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"ບັດ"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"ຊື່ຜູ້ໃຊ້"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"ທີ່ຢູ່ອີເມລ"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"ໃຈເຢັນໆ ແລະ ຊອກຫາບ່ອນພັກຢູ່ໃກ້ໆ."</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index fdd5d2aac1ec..f2c17c30b56e 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -1318,6 +1318,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"„<xliff:g id="NETWORK_SSID">%1$s</xliff:g>“ negali pasiekti interneto"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Palieskite, kad būtų rodomos parinktys."</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobiliojo ryšio tinkle nėra prieigos prie interneto"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Tinkle nėra prieigos prie interneto"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Privataus DNS serverio negalima pasiekti"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Prisijungta"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"„<xliff:g id="NETWORK_SSID">%1$s</xliff:g>“ ryšys apribotas"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Palieskite, jei vis tiek norite prisijungti"</string> @@ -2031,9 +2034,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Išsaugoti"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Ne, ačiū"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Atnaujinti"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Tęsti"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"slaptažodį"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"adresą"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"kredito kortelę"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debeto kortelė"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"mokėjimo kortelė"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kortelė"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"naudotojo vardas"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"el. pašto adresas"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Nesijaudinkite ir ieškokite prieglobsčio netoliese."</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index d1b7764362e0..be8d5d11e41e 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -1296,6 +1296,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"Tīklā <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nav piekļuves internetam"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Pieskarieties, lai skatītu iespējas."</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobilajā tīklā nav piekļuves internetam."</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Tīklā nav piekļuves internetam."</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Nevar piekļūt privātam DNS serverim."</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Izveidots savienojums"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"Tīklā <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ir ierobežota savienojamība"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Lai tik un tā izveidotu savienojumu, pieskarieties"</string> @@ -1996,9 +1999,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Saglabāt"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Nē, paldies"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Atjaunināt"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Turpināt"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"paroli"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"adresi"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"kredītkartes informāciju"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debetkarte"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"maksājumu karte"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"karte"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"lietotājvārds"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"e-pasta adrese"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Saglabājiet mieru un meklējiet tuvumā patvērumu."</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index d30c8f2c70a8..ea9a6b476f68 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> нема интернет-пристап"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Допрете за опции"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Мобилната мрежа нема интернет-пристап"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Мрежата нема интернет-пристап"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Не може да се пристапи до приватниот DNS-сервер"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Поврзано"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничена поврзливост"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Допрете за да се поврзете и покрај тоа"</string> @@ -1964,9 +1967,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Зачувај"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Не, фала"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Ажурирај"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Продолжи"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"лозинка"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"адреса"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"кредитна картичка"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"дебитна картичка"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"платежна картичка"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"картичка"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"корисничко име"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"адреса на е-пошта"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Бидете смирени и побарајте засолниште во близина."</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 5a604fbe92ab..9d87652667f3 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> എന്നതിന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ഓപ്ഷനുകൾക്ക് ടാപ്പുചെയ്യുക"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"മൊബെെൽ നെറ്റ്വർക്കിന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"നെറ്റ്വർക്കിന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"സ്വകാര്യ DNS സെർവർ ആക്സസ് ചെയ്യാനാവില്ല"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"കണക്റ്റ് ചെയ്തു"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> എന്നതിന് പരിമിതമായ കണക്റ്റിവിറ്റി ഉണ്ട്"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"ഏതുവിധേനയും കണക്റ്റ് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string> @@ -1962,9 +1965,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"സംരക്ഷിക്കുക"</string> <string name="autofill_save_no" msgid="2625132258725581787">"വേണ്ട, നന്ദി"</string> <string name="autofill_update_yes" msgid="310358413273276958">"അപ്ഡേറ്റ് ചെയ്യുക"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"തുടരുക"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"പാസ്വേഡ്"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"വിലാസം"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"ക്രെഡിറ്റ് കാർഡ്"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"ഡെബിറ്റ് കാർഡ്"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"പെയ്മെന്റ് കാർഡ്"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"കാർഡ്"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"ഉപയോക്തൃനാമം"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"വിലാസം നൽകുക"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"പരിഭ്രമിക്കാതിരിക്കുക, അടുത്തുള്ള അഭയകേന്ദ്രം തേടുക."</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index f4c6b60ce942..9a4ffae4ac16 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-д интернэтийн хандалт алга"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Сонголт хийхийн тулд товшино уу"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Мобайл сүлжээнд интернэт хандалт байхгүй байна"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Сүлжээнд интернэт хандалт байхгүй байна"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Хувийн DNS серверт хандах боломжгүй байна"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Холбогдсон"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> зарим үйлчилгээнд хандах боломжгүй байна"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Ямар ч тохиолдолд холбогдохын тулд товших"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Хадгалах"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Үгүй, баярлалаа"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Шинэчлэх"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Үргэлжлүүлэх"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"нууц үг"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"хаяг"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"кредит карт"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"дебит карт"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"төлбөрийн карт"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"карт"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"хэрэглэгчийн нэр"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"имэйл хаяг"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Тайван байж, ойролцоох нуугдах газар хайна уу."</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 4fb2347f5641..03b973b38520 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -345,7 +345,7 @@ <string name="permlab_receiveMms" msgid="1821317344668257098">"मजकूर मेसेज मिळवा (MMS)"</string> <string name="permdesc_receiveMms" msgid="533019437263212260">"MMS मेसेज प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यास अॅप ला अनुमती देते. म्हणजेच अॅप आपल्या डिव्हाइसवर पाठविलेले मेसेज तुम्हाला न दर्शवता त्यांचे परीक्षण करू किंवा ते हटवू शकतो."</string> <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"सेल प्रसारण मेसेज फॉरवर्ड करा"</string> - <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"सेल प्रसारण मेसेज मिळाल्यानंतर ते फॉरवर्ड करण्यासाठी अॅपला सेल प्रसारण मॉड्यूलमध्ये प्रतिबद्ध करण्याची अनुमती देते. काही स्थानांमध्ये तुम्हाला आणीबाणीच्या परिस्थीतींची चेतावणी देण्यासाठी सेल प्रसारण सूचना वितरित केल्या जातात. दुर्भावनापूर्ण अॅप्स आणीबाणी सेल प्रसारण मिळवतात तेव्हा ती तुमच्या डिव्हाइसच्या परफॉर्मन्समध्ये किंवा कामामध्ये कदाचित व्यत्यय आणू शकतात."</string> + <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"सेल प्रसारण मेसेज मिळाल्यानंतर ते फॉरवर्ड करण्यासाठी अॅपला सेल प्रसारण मॉड्यूलमध्ये प्रतिबद्ध करण्याची अनुमती देते. काही स्थानांमध्ये तुम्हाला आणीबाणीच्या परिस्थीतींची चेतावणी देण्यासाठी सेल प्रसारण सूचना वितरित केल्या जातात. दुर्भावनापूर्ण अॅप्स आणीबाणी सेल प्रसारण मिळवतात तेव्हा ती तुमच्या डिव्हाइसच्या परफॉर्मन्समध्ये किंवा कामामध्ये कदाचित व्यत्यय आणू शकतात."</string> <string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"सेल प्रसारण मेसेज वाचा"</string> <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"आपल्या डिव्हाइसद्वारे प्राप्त केलेले सेल प्रसारण मेसेज वाचण्यासाठी अॅप ला अनुमती देते. काही स्थानांमध्ये तुम्हाला आणीबाणीच्या परिस्थितीची चेतावणी देण्यासाठी सेल प्रसारण सूचना वितरीत केल्या जातात. आणीबाणी सेल प्रसारण प्राप्त होते तेव्हा आपल्या डिव्हाइसच्या कार्यप्रदर्शनात किंवा कार्यात दुर्भावनापूर्ण अॅप्स व्यत्यय आणू शकतात."</string> <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"सदस्यता घेतलेली फीड वाचा"</string> @@ -394,7 +394,7 @@ <string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"रोचक प्रसारणे पाठविण्यासाठी अॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो फोनला धीमा किंवा अस्थिर करू शकतो."</string> <string name="permlab_readContacts" msgid="8348481131899886131">"तुमचे संपर्क वाचा"</string> <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"तुम्ही कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुमचा संपर्क डेटा सेव्ह करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा शेअर करू शकतात."</string> - <string name="permdesc_readContacts" product="tv" msgid="3890061004911027912">"तुम्ही केलेले कॉल, केलेले ईमेल किंवा विशिष्ट संपर्कांसह अन्य मार्गांनी केलेले कम्युनिकेशन यांची वारंवारता, यासह तुमच्या Android TV डिव्हाइसवर स्टोअर केलेल्या तुमच्या संपर्कांविषयीचा डेटा वाचण्याची अॅपला अनुमती देते. ही परवानगी अॅप्सना तुमचा संपर्क डेटा सेव्ह करण्याची अनुमती देते आणि ही दुर्भावनापूर्ण अॅप्स तुम्हाला न कळवता संपर्क डेटा शेअर करू शकतात."</string> + <string name="permdesc_readContacts" product="tv" msgid="3890061004911027912">"तुम्ही केलेले कॉल, केलेले ईमेल किंवा विशिष्ट संपर्कांसह अन्य मार्गांनी केलेले कम्युनिकेशन यांची वारंवारता, यासह तुमच्या Android TV डिव्हाइसवर स्टोअर केलेल्या तुमच्या संपर्कांविषयीचा डेटा वाचण्याची अॅपला अनुमती देते. ही परवानगी अॅप्सना तुमचा संपर्क डेटा सेव्ह करण्याची अनुमती देते आणि ही दुर्भावनापूर्ण अॅप्स तुम्हाला न कळवता संपर्क डेटा शेअर करू शकतात."</string> <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"तुम्ही कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या फोनवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुमचा संपर्क डेटा सेव्ह करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा शेअर करू शकतात."</string> <string name="permlab_writeContacts" msgid="5107492086416793544">"तुमचे संपर्क सुधारित करा"</string> <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"तुम्ही विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅप ला अनुमती देते."</string> @@ -404,7 +404,7 @@ <string name="permdesc_readCallLog" msgid="3204122446463552146">"हा अॅप तुमचा कॉल इतिहास वाचू शकता."</string> <string name="permlab_writeCallLog" msgid="8552045664743499354">"कॉल लॉग लिहा"</string> <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, आपल्या टॅब्लेटचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string> - <string name="permdesc_writeCallLog" product="tv" msgid="7939219462637746280">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, तुमच्या Android TV डिव्हाइसचा कॉल लॉग सुधारित करण्यासाठी अॅपला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string> + <string name="permdesc_writeCallLog" product="tv" msgid="7939219462637746280">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, तुमच्या Android TV डिव्हाइसचा कॉल लॉग सुधारित करण्यासाठी अॅपला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string> <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, आपल्या फोनचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string> <string name="permlab_bodySensors" msgid="4683341291818520277">"शरीर सेंसर (हृदय गती मॉनिटरसारखे) अॅक्सेस करा"</string> <string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"हृदय गती सारख्या, आपल्या शारीरिक स्थितीचे नियंत्रण करणार्या सेन्सरवरून डेटामध्ये प्रवेश करण्यासाठी अॅपला अनुमती देते."</string> @@ -1274,6 +1274,12 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ला इंटरनेट अॅक्सेस नाही"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"पर्यायांसाठी टॅप करा"</string> + <!-- no translation found for mobile_no_internet (1445208572588803493) --> + <skip /> + <!-- no translation found for other_networks_no_internet (1553338015597653827) --> + <skip /> + <!-- no translation found for private_dns_broken_detailed (4293356177543535578) --> + <skip /> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"कनेक्ट केले"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ला मर्यादित कनेक्टिव्हिटी आहे"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"तरीही कनेक्ट करण्यासाठी टॅप करा"</string> @@ -1962,9 +1968,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"सेव्ह करा"</string> <string name="autofill_save_no" msgid="2625132258725581787">"नाही, नको"</string> <string name="autofill_update_yes" msgid="310358413273276958">"अपडेट करा"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"पुढे सुरू ठेवा"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"पासवर्ड"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"पत्ता"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"क्रेडिट कार्ड"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"डेबिट कार्ड"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"पेमेंट कार्ड"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"कार्ड"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"वापरकर्तानाव"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"ईमेल अॅड्रेस"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"शांत रहा आणि जवळपास निवारा शोधा."</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 4c811fde4ae7..bfe6f2b10097 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiada akses Internet"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Ketik untuk mendapatkan pilihan"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Rangkaian mudah alih tiada akses Internet"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Rangkaian tiada akses Internet"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Pelayan DNS peribadi tidak boleh diakses"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Disambungkan"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> mempunyai kesambungan terhad"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Ketik untuk menyambung juga"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Simpan"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Tidak, terima kasih"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Kemas kini"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Teruskan"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"kata laluan"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"alamat"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"kad kredit"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"kad debit"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"kad pembayaran"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kad"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"nama pengguna"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"alamat e-mel"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Bertenang dan cari perlindungan di kawasan yang berdekatan."</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 38a33bf6ed86..d7375e25b9e3 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> တွင် အင်တာနက်အသုံးပြုခွင့် မရှိပါ"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"အခြားရွေးချယ်စရာများကိုကြည့်ရန် တို့ပါ"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"မိုဘိုင်းကွန်ရက်တွင် အင်တာနက်ချိတ်ဆက်မှု မရှိပါ"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"ကွန်ရက်တွင် အင်တာနက်အသုံးပြုခွင့် မရှိပါ"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"သီးသန့် ဒီအန်အက်စ် (DNS) ဆာဗာကို သုံး၍မရပါ။"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"ချိတ်ဆက်ထားသည်"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> တွင် ချိတ်ဆက်မှုကို ကန့်သတ်ထားသည်"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"မည်သို့ပင်ဖြစ်စေ ချိတ်ဆက်ရန် တို့ပါ"</string> @@ -1962,9 +1965,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"သိမ်းရန်"</string> <string name="autofill_save_no" msgid="2625132258725581787">"မလိုပါ"</string> <string name="autofill_update_yes" msgid="310358413273276958">"အပ်ဒိတ်လုပ်ရန်"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"ရှေ့ဆက်ရန်"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"စကားဝှက်"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"လိပ်စာ"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"ခရက်တစ်ကတ်"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"ဒက်ဘစ် ကတ်"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"ငွေပေးချေမှုကတ်"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"ကတ်"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"အသုံးပြုသူအမည်"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"အီးမေးလ်လိပ်စာ"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"စိတ်ငြိမ်ငြိမ်ထားပြီး အနီးအနားတဝိုက်တွင် ခိုနားစရာ နေရာရှာပါ။"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 2d4a5f525ee1..3a78f79dbbf0 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internettilkobling"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Trykk for å få alternativer"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobilnettverket har ingen internettilgang"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Nettverket har ingen internettilgang"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Den private DNS-tjeneren kan ikke nås"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Tilkoblet"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begrenset tilkobling"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Trykk for å koble til likevel"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Lagre"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Nei takk"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Oppdater"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Fortsett"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"passord"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"adresse"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"kredittkort"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debetkort"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"betalingskort"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kort"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"brukernavn"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"e-postadresse"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Hold deg rolig og søk ly i nærheten."</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index dc44d2a2f659..d3eb0c3953ca 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -1280,6 +1280,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> को इन्टरनेटमाथि पहुँच छैन"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"विकल्पहरूका लागि ट्याप गर्नुहोस्"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"मोबाइल नेटवर्कको इन्टरनेटमाथि पहुँच छैन"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"नेटवर्कको इन्टरनेटमाथि पहुँच छैन"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"निजी DNS सर्भरमाथि पहुँच प्राप्त गर्न सकिँदैन"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"जोडियो"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> को जडान सीमित छ"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"जसरी भए पनि जडान गर्न ट्याप गर्नुहोस्"</string> @@ -1967,9 +1970,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"सुरक्षित गर्नुहोस्"</string> <string name="autofill_save_no" msgid="2625132258725581787">"पर्दैन, धन्यवाद"</string> <string name="autofill_update_yes" msgid="310358413273276958">"अद्यावधिक गर्नुहोस्"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"जारी राख्नुहोस्"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"पासवर्ड"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"ठेगाना"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"क्रेडिट कार्ड"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"डेबिट कार्ड"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"भुक्तानी कार्ड"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"कार्ड"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"प्रयोगकर्ताको नाम"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"इमेल ठेगाना"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"शान्त रहनुहोस् र नजिकै आश्रयस्थल खोज्नुहोस्।"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 5c30c41f806e..9300f74aab3f 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> heeft geen internettoegang"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tik voor opties"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobiel netwerk heeft geen internettoegang"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Netwerk heeft geen internettoegang"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Geen toegang tot privé-DNS-server"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Verbonden"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> heeft beperkte connectiviteit"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Tik om toch verbinding te maken"</string> @@ -1666,14 +1669,14 @@ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Verwijderen"</string> <string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Volume verhogen tot boven het aanbevolen niveau?\n\nAls je langere tijd op hoog volume naar muziek luistert, raakt je gehoor mogelijk beschadigd."</string> - <string name="accessibility_shortcut_warning_dialog_title" msgid="8404780875025725199">"Sneltoets voor toegankelijkheid gebruiken?"</string> + <string name="accessibility_shortcut_warning_dialog_title" msgid="8404780875025725199">"Snelkoppeling toegankelijkheid gebruiken?"</string> <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Wanneer de snelkoppeling is ingeschakeld, kun je drie seconden op beide volumeknoppen drukken om een toegankelijkheidsfunctie te starten.\n\n Huidige toegankelijkheidsfunctie:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Je kunt de functie wijzigen in Instellingen > Toegankelijkheid."</string> <string name="disable_accessibility_shortcut" msgid="627625354248453445">"Sneltoets uitschakelen"</string> <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Sneltoets gebruiken"</string> <string name="color_inversion_feature_name" msgid="4231186527799958644">"Kleurinversie"</string> <string name="color_correction_feature_name" msgid="6779391426096954933">"Kleurcorrectie"</string> - <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"\'Snelle link voor toegankelijkheid\' heeft <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ingeschakeld"</string> - <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"\'Snelle link voor toegankelijkheid\' heeft <xliff:g id="SERVICE_NAME">%1$s</xliff:g> uitgeschakeld"</string> + <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"\'Snelkoppeling toegankelijkheid\' heeft <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ingeschakeld"</string> + <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"\'Snelkoppeling toegankelijkheid\' heeft <xliff:g id="SERVICE_NAME">%1$s</xliff:g> uitgeschakeld"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Houd beide volumetoetsen drie seconden ingedrukt om <xliff:g id="SERVICE_NAME">%1$s</xliff:g> te gebruiken"</string> <string name="accessibility_button_prompt_text" msgid="1176658502969738564">"Kies een service om te gebruiken wanneer je op de toegankelijkheidsknop tikt:"</string> <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Kies een service om te gebruiken met het toegankelijkheidsgebaar (veeg met twee vingers omhoog vanaf de onderkant van het scherm):"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Opslaan"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Nee, bedankt"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Updaten"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Doorgaan"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"Wachtwoord"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"Adres"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"Creditcard"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"pinpas"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"betaalkaart"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kaart"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"gebruikersnaam"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"e-mailadres"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Blijf kalm en zoek onderdak in de buurt."</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index ac85a401da17..b4079ef53dc6 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -1274,6 +1274,12 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ର ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ବିକଳ୍ପ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string> + <!-- no translation found for mobile_no_internet (1445208572588803493) --> + <skip /> + <!-- no translation found for other_networks_no_internet (1553338015597653827) --> + <skip /> + <!-- no translation found for private_dns_broken_detailed (4293356177543535578) --> + <skip /> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"ସଂଯୁକ୍ତ ହୋଇଛି"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ର ସୀମିତ ସଂଯୋଗ ଅଛି"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"ତଥାପି ଯୋଗାଯୋଗ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string> @@ -1961,9 +1967,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"ସେଭ୍ କରନ୍ତୁ"</string> <string name="autofill_save_no" msgid="2625132258725581787">"ନାଁ, ପଚାରିଥିବାରୁ ଧନ୍ୟବାଦ"</string> <string name="autofill_update_yes" msgid="310358413273276958">"ଅପ୍ଡେଟ୍ କରନ୍ତୁ"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"ଜାରି ରଖନ୍ତୁ"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"ପାସୱର୍ଡ୍"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"ଠିକଣା"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"କ୍ରେଡିଟ୍ କାର୍ଡ"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"ଡେବିଟ୍ କାର୍ଡ"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"ପେମେଣ୍ଟ କାର୍ଡ"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"କାର୍ଡ"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"ୟୁଜରଙ୍କ ନାମ"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"ଇମେଲ୍ ଠିକଣା"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"ଶାନ୍ତ ରୁହନ୍ତୁ ଓ ନିକଟର କୌଣସି ଏକ ଆଶ୍ରୟସ୍ଥଳ ଖୋଜନ୍ତୁ"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 177c7bce4001..68cfc5ff4f58 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ਵਿਕਲਪਾਂ ਲਈ ਟੈਪ ਕਰੋ"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"ਮੋਬਾਈਲ ਨੈੱਟਵਰਕ ਕੋਲ ਇੰਟਰਨੈੱਟ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"ਨੈੱਟਵਰਕ ਕੋਲ ਇੰਟਰਨੈੱਟ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"ਨਿੱਜੀ ਡੋਮੇਨ ਨਾਮ ਪ੍ਰਣਾਲੀ (DNS) ਸਰਵਰ \'ਤੇ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"ਕਨੈਕਟ ਹੋਏ"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ਕੋਲ ਸੀਮਤ ਕਨੈਕਟੀਵਿਟੀ ਹੈ"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"ਫਿਰ ਵੀ ਕਨੈਕਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string> @@ -1962,9 +1965,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"ਰੱਖਿਅਤ ਕਰੋ"</string> <string name="autofill_save_no" msgid="2625132258725581787">"ਨਹੀਂ ਧੰਨਵਾਦ"</string> <string name="autofill_update_yes" msgid="310358413273276958">"ਅੱਪਡੇਟ ਕਰੋ"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"ਜਾਰੀ ਰੱਖੋ"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"ਪਾਸਵਰਡ"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"ਪਤਾ"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"ਕ੍ਰੈਡਿਟ ਕਾਰਡ"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"ਡੈਬਿਟ ਕਾਰਡ"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"ਭੁਗਤਾਨ ਕਾਰਡ"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"ਕਾਰਡ"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"ਵਰਤੋਂਕਾਰ ਨਾਮ"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"ਈਮੇਲ ਪਤਾ"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"ਸ਼ਾਂਤ ਰਹੋ ਅਤੇ ਆਸ-ਪਾਸ ਪਨਾਹ ਮੰਗੋ।"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 1f2430cde8b5..b238d4f08a83 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -307,7 +307,7 @@ <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Zezwolić aplikacji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> na nagrywanie dźwięku?"</string> <string name="permgrouplab_activityRecognition" msgid="1565108047054378642">"Aktywność fizyczna"</string> <string name="permgroupdesc_activityRecognition" msgid="6949472038320473478">"dostęp do aktywności fizycznej"</string> - <string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"Zezwolić aplikacji „<xliff:g id="APP_NAME">%1$s</xliff:g>” na dostęp do aktywności fizycznej?"</string> + <string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"Zezwolić aplikacji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> na dostęp do aktywności fizycznej?"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Aparat"</string> <string name="permgroupdesc_camera" msgid="3250611594678347720">"robienie zdjęć i nagrywanie filmów"</string> <string name="permgrouprequest_camera" msgid="1299833592069671756">"Zezwolić aplikacji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> na robienie zdjęć i nagrywanie filmów?"</string> @@ -1318,6 +1318,12 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nie ma dostępu do internetu"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Kliknij, by wyświetlić opcje"</string> + <!-- no translation found for mobile_no_internet (1445208572588803493) --> + <skip /> + <!-- no translation found for other_networks_no_internet (1553338015597653827) --> + <skip /> + <!-- no translation found for private_dns_broken_detailed (4293356177543535578) --> + <skip /> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Połączono"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ma ograniczoną łączność"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Kliknij, by mimo to nawiązać połączenie"</string> @@ -2031,9 +2037,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Zapisz"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Nie, dziękuję"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Zaktualizuj"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Dalej"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"hasło"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"adres"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"karta kredytowa"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"karta debetowa"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"karta płatnicza"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"karta"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"nazwa użytkownika"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"adres e-mail"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Zachowaj spokój i poszukaj schronienia w pobliżu."</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 05967afc6fd9..744270373883 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Toque para ver opções"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"A rede móvel não tem acesso à Internet"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"A rede não tem acesso à Internet"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Não é possível acessar o servidor DNS privado"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Conectado"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conectividade limitada"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Toque para conectar mesmo assim"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Salvar"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Não, obrigado"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Atualizar"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Continuar"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"senha"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"endereço"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"cartão de crédito"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"cartão de débito"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"cartão de pagamento"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"cartão"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"nome de usuário"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"endereço de e-mail"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Fique calmo e procure um abrigo por perto."</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 400d5762acca..add12d94bfd1 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Toque para obter mais opções"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"A rede móvel não tem acesso à Internet"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"A rede não tem acesso à Internet"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Não é possível aceder ao servidor DNS."</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Ligado"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conetividade limitada."</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Toque para ligar mesmo assim."</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Guardar"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Não, obrigado"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Atualizar"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Continuar"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"palavra-passe"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"endereço"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"cartão de crédito"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"cartão de débito"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"cartão de pagamento"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"cartão"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"nome de utilizador"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"endereço de email"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Mantenha a calma e procure abrigo nas proximidades."</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 05967afc6fd9..744270373883 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Toque para ver opções"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"A rede móvel não tem acesso à Internet"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"A rede não tem acesso à Internet"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Não é possível acessar o servidor DNS privado"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Conectado"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conectividade limitada"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Toque para conectar mesmo assim"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Salvar"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Não, obrigado"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Atualizar"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Continuar"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"senha"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"endereço"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"cartão de crédito"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"cartão de débito"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"cartão de pagamento"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"cartão"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"nome de usuário"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"endereço de e-mail"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Fique calmo e procure um abrigo por perto."</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 27e6ec06596f..f1a6136ef006 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -1296,6 +1296,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nu are acces la internet"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Atingeți pentru opțiuni"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Rețeaua mobilă nu are acces la internet"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Rețeaua nu are acces la internet"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Serverul DNS privat nu poate fi accesat"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Conectat"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> are conectivitate limitată"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Atingeți pentru a vă conecta oricum"</string> @@ -1996,9 +1999,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Salvați"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Nu, mulțumesc"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Actualizați"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Continuați"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"parolă"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"adresă"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"card de credit"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"card de debit"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"card de plată"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"card"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"nume de utilizator"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"adresă de e-mail"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Păstrați-vă calmul și căutați un adăpost în apropiere."</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 6390b95bfaf5..f01de3658ee9 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -1318,6 +1318,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"Сеть \"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>\" не подключена к Интернету"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Нажмите, чтобы показать варианты."</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Мобильная сеть не подключена к Интернету"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Сеть не подключена к Интернету"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Доступа к частному DNS-серверу нет."</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Подключено"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"Подключение к сети \"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>\" ограничено"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Нажмите, чтобы подключиться"</string> @@ -2031,9 +2034,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Сохранить"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Нет, спасибо"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Обновить"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Продолжить"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"Пароль"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"Адрес"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"Банковская карта"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"дебетовая карта"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"платежная карта"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"карта"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"имя пользователя"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"адрес электронной почты"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Сохраняйте спокойствие и поищите укрытие поблизости."</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index fc69d0e95340..6dbf50218b62 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -1276,6 +1276,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> හට අන්තර්ජාල ප්රවේශය නැත"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"විකල්ප සඳහා තට්ටු කරන්න"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"ජංගම ජාලවලට අන්තර්ජාල ප්රවේශය නැත"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"ජාලයට අන්තර්ජාල ප්රවේශය නැත"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"පුද්ගලික DNS සේවාදායකයට ප්රවේශ වීමට නොහැකිය"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"සම්බන්ධයි"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> හට සීමිත සබැඳුම් හැකියාවක් ඇත"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"කෙසේ වෙතත් ඉදිරියට යාමට තට්ටු කරන්න"</string> @@ -1963,9 +1966,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"සුරකින්න"</string> <string name="autofill_save_no" msgid="2625132258725581787">"එපා ස්තූතියි"</string> <string name="autofill_update_yes" msgid="310358413273276958">"යාවත්කාලීන කරන්න"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"ඉදිරියට යන්න"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"මුරපදය"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"ලිපිනය"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"ණය කාඩ්පත"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"හර කාඩ්පත"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"ගෙවීම් කාඩ්පත"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"කාඩ්පත"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"පරිශීලක නාමය"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"ඊ-තැපැල් ලිපිනය"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"සන්සුන්ව ඉන්න සහ අවට ඇති නවාතැන් පහසුකම් බලන්න."</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 59e8b5e042a1..1549f4ddfacd 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -1318,6 +1318,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nemá prístup k internetu"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Klepnutím získate možnosti"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobilná sieť nemá prístup k internetu"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Sieť nemá prístup k internetu"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"K súkromnému serveru DNS sa nepodarilo získať prístup"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Pripojené"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> má obmedzené pripojenie"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Ak sa chcete aj napriek tomu pripojiť, klepnite"</string> @@ -2031,9 +2034,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Uložiť"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Nie, vďaka"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Aktualizovať"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Pokračovať"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"heslo"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"adresa"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"kreditná karta"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debetná karta"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"platobná karta"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"karta"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"používateľské meno"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"e-mailová adresa"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Zachovajte pokoj a vyhľadajte úkryt v okolí."</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index c49d5ffbc4d2..40d0dde738b1 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -1318,6 +1318,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"Omrežje <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nima dostopa do interneta"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Dotaknite se za možnosti"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobilno omrežje nima dostopa do interneta"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Omrežje nima dostopa do interneta"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Do zasebnega strežnika DNS ni mogoče dostopati"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Povezava je vzpostavljena"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"Povezljivost omrežja <xliff:g id="NETWORK_SSID">%1$s</xliff:g> je omejena"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Dotaknite se, da kljub temu vzpostavite povezavo"</string> @@ -2031,9 +2034,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Shrani"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Ne, hvala"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Posodobi"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Naprej"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"geslo"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"naslov"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"kreditno kartico"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debetna kartica"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"plačilna kartica"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kartica"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"uporabniško ime"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"e-poštni naslov"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Ostanite mirni in poiščite zavetje v bližini."</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 045c7d5d8405..ee46d3b7deea 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nuk ka qasje në internet"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Trokit për opsionet"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Rrjeti celular nuk ka qasje në internet"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Rrjeti nuk ka qasje në internet"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Serveri privat DNS nuk mund të qaset"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Lidhur"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ka lidhshmëri të kufizuar"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Trokit për t\'u lidhur gjithsesi"</string> @@ -1962,9 +1965,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Ruaj"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Jo, faleminderit"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Përditëso"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Vazhdo"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"fjalëkalimi"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"adresa"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"karta e kreditit"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"kartë debiti"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"kartë pagese"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kartë"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"emri i përdoruesit"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"adresa e mail-it"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Qëndro i qetë dhe kërko strehim në afërsi."</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 0d8acc69e5e4..7fd7353617f1 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -1296,6 +1296,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> нема приступ интернету"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Додирните за опције"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Мобилна мрежа нема приступ интернету"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Мрежа нема приступ интернету"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Приступ приватном DNS серверу није успео"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Повезано је"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничену везу"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Додирните да бисте се ипак повезали"</string> @@ -1996,9 +1999,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Сачувај"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Не, хвала"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Ажурирај"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Настави"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"лозинка"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"адреса"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"кредитна картица"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"дебитна картица"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"платна картица"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"картица"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"корисничко име"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"имејл адреса"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Останите мирни и потражите склониште у околини."</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index dba5ebb2e134..b8da1b687561 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internetanslutning"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tryck för alternativ"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobilnätverket har ingen internetanslutning"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Nätverket har ingen internetanslutning"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Det går inte att komma åt den privata DNS-servern."</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Ansluten"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begränsad anslutning"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Tryck för att ansluta ändå"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Spara"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Nej tack"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Uppdatera"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Fortsätt"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"lösenordet"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"adressen"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"kreditkortet"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"bankkort"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"kontokort"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kort"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"användarnamn"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"e-postadress"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Håll dig lugn och sök skydd i närheten."</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 89e3231ff8d3..88221b639f76 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> haina uwezo wa kufikia intaneti"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Gusa ili upate chaguo"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mtandao wa simu hauna uwezo wa kufikia intaneti"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Mtandao hauna uwezo wa kufikia intaneti"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Seva ya faragha ya DNS haiwezi kufikiwa"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Imeunganisha"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ina muunganisho unaofikia huduma chache."</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Gusa ili uunganishe tu"</string> @@ -1349,7 +1352,7 @@ <string name="dlg_ok" msgid="7376953167039865701">"Sawa"</string> <string name="usb_charging_notification_title" msgid="1595122345358177163">"Unachaji kifaa hiki kupitia USB"</string> <string name="usb_supplying_notification_title" msgid="4631045789893086181">"Inachaji kifaa kilichounganishwa kupitia USB"</string> - <string name="usb_mtp_notification_title" msgid="4238227258391151029">"Umewasha hali ya uhamishaji wa faili kupitia USB"</string> + <string name="usb_mtp_notification_title" msgid="4238227258391151029">"Umewasha uhamishaji wa faili kupitia USB"</string> <string name="usb_ptp_notification_title" msgid="5425857879922006878">"Umewasha hali ya PTP kupitia USB"</string> <string name="usb_tether_notification_title" msgid="3716143122035802501">"Umewasha hali ya kusambaza mtandao ukitumia USB"</string> <string name="usb_midi_notification_title" msgid="5356040379749154805">"Umewasha hali ya MIDI kupitia USB"</string> @@ -1666,14 +1669,14 @@ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Ondoa"</string> <string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Ungependa kupandisha sauti zaidi ya kiwango kinachopendekezwa?\n\nKusikiliza kwa sauti ya juu kwa muda mrefu kunaweza kuharibu uwezo wako wa kusikia."</string> - <string name="accessibility_shortcut_warning_dialog_title" msgid="8404780875025725199">"Ungependa Kutumia Njia ya Mkato ya Zana za walio na matatizo ya kuona au kusikia?"</string> - <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Unapowasha kipengele cha njia ya mkato, hatua ya kubonyeza vitufe vyote viwili vya sauti kwa dakika 3 itafungua kipengele cha zana za walio na matatizo ya kuona au kusikia.\n\n Kipengele kilichopo cha zana za walio na matatizo ya kuona au kusikia:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Unaweza kubadilisha kipengele hiki katika Mipangilio > Zana za walio na matatizo ya kuona au kusikia."</string> + <string name="accessibility_shortcut_warning_dialog_title" msgid="8404780875025725199">"Ungependa kutumia njia ya mkato ya ufikivu?"</string> + <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Unapowasha kipengele cha njia ya mkato, hatua ya kubonyeza vitufe vyote viwili vya sauti kwa dakika 3 itafungua kipengele cha ufikivu.\n\n Kipengele cha ufikivu kilichopo kwa sasa:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Unaweza kubadilisha kipengele hiki katika Mipangilio > Zana za ufikivu."</string> <string name="disable_accessibility_shortcut" msgid="627625354248453445">"Zima kipengele cha Njia ya Mkato"</string> <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Tumia Njia ya Mkato"</string> <string name="color_inversion_feature_name" msgid="4231186527799958644">"Ugeuzaji rangi"</string> <string name="color_correction_feature_name" msgid="6779391426096954933">"Usahihishaji wa rangi"</string> - <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Njia ya mkato ya zana za walio na matatizo ya kuona au kusikia imewasha <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Njia ya mkato ya zana za walio na matatizo ya kuona au kusikia imezima <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> + <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Njia ya mkato ya ufikivu imewasha <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> + <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Njia ya mkato ya ufikivu imezima <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Bonyeza na ushikilie vitufe vyote viwili vya sauti kwa sekunde tatu ili utumie <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> <string name="accessibility_button_prompt_text" msgid="1176658502969738564">"Chagua huduma ya kutumia unapogusa kitufe cha ufikivu:"</string> <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Chagua huduma ya kutumia pamoja na ishara ya ufikivu (telezesha vidole viwili kutoka chini kwenda juu kwenye skrini):"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Hifadhi"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Hapana, asante"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Sasisha"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Endelea"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"nenosiri"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"anwani"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"kadi ya mikopo"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"kadi ya malipo"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"kadi ya malipo"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kadi"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"jina la mtumiaji"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"anwani ya barua pepe"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Tulia na utafute hifadhi ya karibu."</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 0ed1421bb92b..6c354230a772 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"விருப்பங்களுக்கு, தட்டவும்"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"மொபைல் நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"தனிப்பட்ட DNS சேவையகத்தை அணுக இயலாது"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"இணைக்கப்பட்டது"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> வரம்பிற்கு உட்பட்ட இணைப்புநிலையைக் கொண்டுள்ளது"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"எப்படியேனும் இணைப்பதற்குத் தட்டவும்"</string> @@ -1962,9 +1965,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"சேமி"</string> <string name="autofill_save_no" msgid="2625132258725581787">"வேண்டாம்"</string> <string name="autofill_update_yes" msgid="310358413273276958">"புதுப்பி"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"தொடர்க"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"கடவுச்சொல்"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"முகவரி"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"கிரெடிட் கார்டு"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"டெபிட் கார்டு"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"பேமெண்ட் கார்டு"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"கார்டு"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"பயனர்பெயர்"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"மின்னஞ்சல் முகவரி"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"பதட்டப்படாதீர்கள், அருகில் ஏதேனும் பாதுகாப்பான இடம் உள்ளதா எனப் பாருங்கள்."</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 72b83c4d944c..7e91460252d7 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>కి ఇంటర్నెట్ యాక్సెస్ లేదు"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ఎంపికల కోసం నొక్కండి"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"మొబైల్ నెట్వర్క్కు ఇంటర్నెట్ యాక్సెస్ లేదు"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"నెట్వర్క్కు ఇంటర్నెట్ యాక్సెస్ లేదు"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"ప్రైవేట్ DNS సర్వర్ను యాక్సెస్ చేయడం సాధ్యపడదు"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"కనెక్ట్ చేయబడింది"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> పరిమిత కనెక్టివిటీని కలిగి ఉంది"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"ఏదేమైనా కనెక్ట్ చేయడానికి నొక్కండి"</string> @@ -1962,9 +1965,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"సేవ్ చేయి"</string> <string name="autofill_save_no" msgid="2625132258725581787">"వద్దు, ధన్యవాదాలు"</string> <string name="autofill_update_yes" msgid="310358413273276958">"అప్డేట్ చేయి"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"కొనసాగించు"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"పాస్వర్డ్"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"చిరునామా"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"క్రెడిట్ కార్డ్"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"డెబిట్ కార్డ్"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"చెల్లింపు కార్డ్"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"కార్డ్"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"వినియోగదారు పేరు"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"ఇమెయిల్ చిరునామా"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"ప్రశాంతంగా ఉండండి మరియు దగ్గర్లో తలదాచుకోండి."</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index caaa32d5f358..5431796ece89 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> เข้าถึงอินเทอร์เน็ตไม่ได้"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"แตะเพื่อดูตัวเลือก"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"เครือข่ายมือถือไม่มีการเข้าถึงอินเทอร์เน็ต"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"เครือข่ายไม่มีการเข้าถึงอินเทอร์เน็ต"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"เข้าถึงเซิร์ฟเวอร์ DNS ไม่ได้"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"เชื่อมต่อแล้ว"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> มีการเชื่อมต่อจำกัด"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"แตะเพื่อเชื่อมต่อ"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"บันทึก"</string> <string name="autofill_save_no" msgid="2625132258725581787">"ไม่เป็นไร"</string> <string name="autofill_update_yes" msgid="310358413273276958">"อัปเดต"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"ทำต่อ"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"รหัสผ่าน"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"ที่อยู่"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"บัตรเครดิต"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"บัตรเดบิต"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"บัตรสำหรับชำระเงิน"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"บัตร"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"ชื่อผู้ใช้"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"ที่อยู่อีเมล"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"ทำใจให้สงบและหาที่กำบังในบริเวณใกล้เคียง"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 01cf28f48c86..3e71d77fd0fe 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"Walang access sa internet ang <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"I-tap para sa mga opsyon"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Walang access sa internet ang mobile network"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Walang access sa internet ang network"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Hindi ma-access ang pribadong DNS server"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Nakakonekta"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"Limitado ang koneksyon ng <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"I-tap para kumonekta pa rin"</string> @@ -1666,8 +1669,8 @@ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Alisin"</string> <string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Lakasan ang volume nang lagpas sa inirerekomendang antas?\n\nMaaaring mapinsala ng pakikinig sa malakas na volume sa loob ng mahahabang panahon ang iyong pandinig."</string> - <string name="accessibility_shortcut_warning_dialog_title" msgid="8404780875025725199">"Gagamitin ang Shortcut sa Pagiging Naa-access?"</string> - <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Kapag naka-on ang shortcut, magsisimula ang isang feature ng pagiging naa-access kapag pinindot ang parehong button ng volume sa loob ng 3 segundo.\n\n Kasalukuyang feature ng pagiging naa-access:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Maaari mong baguhin ang feature sa Mga Setting > Pagiging Naa-access."</string> + <string name="accessibility_shortcut_warning_dialog_title" msgid="8404780875025725199">"Gagamitin ang Shortcut sa Pagiging Accessible?"</string> + <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Kapag naka-on ang shortcut, magsisimula ang isang feature ng pagiging naa-access kapag pinindot ang parehong button ng volume sa loob ng 3 segundo.\n\n Kasalukuyang feature ng pagiging naa-access:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Maaari mong baguhin ang feature sa Mga Setting > Pagiging Accessible."</string> <string name="disable_accessibility_shortcut" msgid="627625354248453445">"I-off ang Shortcut"</string> <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Gamitin ang Shortcut"</string> <string name="color_inversion_feature_name" msgid="4231186527799958644">"Pag-invert ng Kulay"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"I-save"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Hindi, salamat na lang"</string> <string name="autofill_update_yes" msgid="310358413273276958">"I-update"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Magpatuloy"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"password"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"address"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"credit card"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debit card"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"card sa pagbabayad"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"card"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"username"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"email address"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Manatiling kalmado at maghanap ng matutuluyan sa malapit."</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index d3e9d938ffe3..8a63348e6049 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ağının internet bağlantısı yok"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Seçenekler için dokunun"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobil ağın internet bağlantısı yok"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Ağın internet bağlantısı yok"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Gizli DNS sunucusuna erişilemiyor"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Bağlandı"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> sınırlı bağlantıya sahip"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Yine de bağlanmak için dokunun"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Kaydet"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Hayır, teşekkürler"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Güncelle"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Devam"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"şifre"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"adres"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"kredi kartı"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"banka kartı"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"ödeme kartı"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"kart"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"kullanıcı adı"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"e-posta adresi"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Sakin olun ve yakınlarda sığınabileceğiniz bir yer bulun."</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 88350a29180b..3d7358e1ec25 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -1318,6 +1318,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"Мережа <xliff:g id="NETWORK_SSID">%1$s</xliff:g> не має доступу до Інтернету"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Торкніться, щоб відкрити опції"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Мобільна мережа не має доступу до Інтернету"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Мережа не має доступу до Інтернету"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Немає доступу до приватного DNS-сервера"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Підключено"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"Підключення до мережі <xliff:g id="NETWORK_SSID">%1$s</xliff:g> обмежено"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Натисніть, щоб усе одно підключитися"</string> @@ -1714,14 +1717,14 @@ <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Вилучити"</string> <string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Збільшити гучність понад рекомендований рівень?\n\nЯкщо слухати надто гучну музику тривалий час, можна пошкодити слух."</string> - <string name="accessibility_shortcut_warning_dialog_title" msgid="8404780875025725199">"Використовувати ярлик спеціальних можливостей?"</string> + <string name="accessibility_shortcut_warning_dialog_title" msgid="8404780875025725199">"Використовувати швидке ввімкнення?"</string> <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Коли ярлик увімкнено, після натискання обох клавіш гучності й утримування їх протягом 3 секунд увімкнеться функція спеціальних можливостей.\n\n Поточна функція спеціальних можливостей:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Цю функцію можна змінити в меню \"Налаштування\" > \"Спеціальні можливості\"."</string> <string name="disable_accessibility_shortcut" msgid="627625354248453445">"Вимкнути ярлик"</string> <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Використовувати ярлик"</string> <string name="color_inversion_feature_name" msgid="4231186527799958644">"Інверсія кольорів"</string> <string name="color_correction_feature_name" msgid="6779391426096954933">"Корекція кольорів"</string> - <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Ярлик спеціальних можливостей увімкнув <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Ярлик спеціальних можливостей вимкнув <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> + <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Сервіс <xliff:g id="SERVICE_NAME">%1$s</xliff:g> увімкнено"</string> + <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Сервіс <xliff:g id="SERVICE_NAME">%1$s</xliff:g> вимкнено"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Щоб скористатися службою <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, утримуйте обидві клавіші гучності впродовж трьох секунд"</string> <string name="accessibility_button_prompt_text" msgid="1176658502969738564">"Виберіть сервіс для кнопки спеціальних можливостей:"</string> <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Виберіть сервіс для жесту спеціальних можливостей (проведення двома пальцями знизу вгору):"</string> @@ -2031,9 +2034,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Зберегти"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Ні, дякую"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Оновити"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Продовжити"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"пароль"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"адреса"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"кредитна картка"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"дебетова картка"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"платіжна картка"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"картка"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"ім’я користувача"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"електронна адреса"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Не хвилюйтеся та знайдіть прихисток поблизу."</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index e5015ea0ec1b..776595f0311b 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -1274,6 +1274,12 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> کو انٹرنیٹ تک رسائی حاصل نہیں ہے"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"اختیارات کیلئے تھپتھپائیں"</string> + <!-- no translation found for mobile_no_internet (1445208572588803493) --> + <skip /> + <!-- no translation found for other_networks_no_internet (1553338015597653827) --> + <skip /> + <!-- no translation found for private_dns_broken_detailed (4293356177543535578) --> + <skip /> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"منسلک ہے"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> کی کنیکٹوٹی محدود ہے"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"بہر حال منسلک کرنے کے لیے تھپتھپائیں"</string> @@ -1962,9 +1968,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"محفوظ کریں"</string> <string name="autofill_save_no" msgid="2625132258725581787">"نہیں، شکریہ"</string> <string name="autofill_update_yes" msgid="310358413273276958">"اپ ڈیٹ کریں"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"جاری رکھیں"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"پاس ورڈ"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"پتہ"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"کریڈٹ کارڈ"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"ڈیبٹ کارڈ"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"ادائیگی کارڈ"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"کارڈ"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"صارف نام"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"ای میل پتہ"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"پُرسکون رہیں اور قریبی پناہ حاصل کریں۔"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 88997476b9fd..1f70155e2f82 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nomli tarmoqda internetga ruxsati yoʻq"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Variantlarni ko‘rsatish uchun bosing"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mobil tarmoq internetga ulanmagan"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Tarmoq internetga ulanmagan"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Xususiy DNS server ishlamayapti"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Ulandi"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nomli tarmoqda aloqa cheklangan"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Baribir ulash uchun bosing"</string> @@ -1962,9 +1965,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Saqlash"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Kerak emas"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Yangilash"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Davom etish"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"parol"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"manzil"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"kredit karta"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"debet karta"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"toʻlov kartasi"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"bildirgi"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"foydalanuvchi nomi"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"email manzili"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Tinchlaning va yaqin-atrofdan boshpana qidiring."</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index fc8994e4b42a..dc9076f78890 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> không có quyền truy cập Internet"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Nhấn để biết tùy chọn"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Mạng di động không có quyền truy cập Internet"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Mạng không có quyền truy cập Internet"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Không thể truy cập máy chủ DNS riêng tư"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Đã kết nối"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> có khả năng kết nối giới hạn"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Nhấn để tiếp tục kết nối"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Lưu"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Không, cảm ơn"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Cập nhật"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Tiếp tục"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"mật khẩu"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"địa chỉ"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"thẻ tín dụng"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"thẻ ghi nợ"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"thẻ thanh toán"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"thẻ"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"tên người dùng"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"địa chỉ email"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Hãy bình tĩnh và tìm kiếm nơi trú ẩn gần đó."</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 56d815357288..8ab182efae1a 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 无法访问互联网"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"点按即可查看相关选项"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"此移动网络无法访问互联网"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"此网络无法访问互联网"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"无法访问私人 DNS 服务器"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"已连接"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 的连接受限"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"点按即可继续连接"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"保存"</string> <string name="autofill_save_no" msgid="2625132258725581787">"不用了"</string> <string name="autofill_update_yes" msgid="310358413273276958">"更新"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"继续"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"密码"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"地址"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"信用卡"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"借记卡"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"支付卡"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"卡片"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"用户名"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"电子邮件地址"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"请保持冷静,并寻找附近的避难地点。"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index c8360aa7439e..d3187647c6d5 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>未有連接至互聯網"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"輕按即可查看選項"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"流動網絡並未連接互聯網"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"網絡並未連接互聯網"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"無法存取私人 DNS 伺服器"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"已連接"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>連線受限"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"仍要輕按以連結至此網絡"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"儲存"</string> <string name="autofill_save_no" msgid="2625132258725581787">"不用了,謝謝"</string> <string name="autofill_update_yes" msgid="310358413273276958">"更新"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"繼續"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"密碼"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"地址"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"信用卡"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"扣帳卡"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"付款卡"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"卡"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"使用者名稱"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"電郵地址"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"請保持冷靜,並尋找附近的避難所。"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 23b68e1412a2..cd787c057c49 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 沒有網際網路連線"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"輕觸即可查看選項"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"這個行動網路沒有網際網路連線"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"這個網路沒有網際網路連線"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"無法存取私人 DNS 伺服器"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"已連線"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 的連線能力受限"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"輕觸即可繼續連線"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"儲存"</string> <string name="autofill_save_no" msgid="2625132258725581787">"不用了,謝謝"</string> <string name="autofill_update_yes" msgid="310358413273276958">"更新"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"繼續"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"密碼"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"地址"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"信用卡"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"簽帳金融卡"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"付款卡"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"卡片"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"使用者名稱"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"電子郵件地址"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"請保持冷靜並尋找附近的避難地點。"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index dac7c106d2f5..45bda9e73be3 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -1274,6 +1274,9 @@ <skip /> <string name="wifi_no_internet" msgid="5198100389964214865">"I-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ayinakho ukufinyelela kwe-inthanethi"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Thepha ukuze uthole izinketho"</string> + <string name="mobile_no_internet" msgid="1445208572588803493">"Inethiwekhi yeselula ayinakho ukufinyelela kwe-inthanethi"</string> + <string name="other_networks_no_internet" msgid="1553338015597653827">"Inethiwekhi ayinakho ukufinyelela kwenethiwekhi"</string> + <string name="private_dns_broken_detailed" msgid="4293356177543535578">"Iseva eyimfihlo ye-DNS ayikwazi ukufinyelelwa"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"Kuxhunyiwe"</string> <string name="network_partial_connectivity" msgid="7774883385494762741">"I-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> inokuxhumeka okukhawulelwe"</string> <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"Thepha ukuze uxhume noma kunjalo"</string> @@ -1961,9 +1964,13 @@ <string name="autofill_save_yes" msgid="6398026094049005921">"Londoloza"</string> <string name="autofill_save_no" msgid="2625132258725581787">"Cha ngiyabonga"</string> <string name="autofill_update_yes" msgid="310358413273276958">"Buyekeza"</string> + <string name="autofill_continue_yes" msgid="7473570809545975068">"Qhubeka"</string> <string name="autofill_save_type_password" msgid="5288448918465971568">"iphasiwedi"</string> <string name="autofill_save_type_address" msgid="4936707762193009542">"ikheli"</string> <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"ikhadi lesikweletu"</string> + <string name="autofill_save_type_debit_card" msgid="177366134636469277">"ikhaddi lasebhange"</string> + <string name="autofill_save_type_payment_card" msgid="446631844461198737">"ikhadi lokukhokha"</string> + <string name="autofill_save_type_generic_card" msgid="5898375974937018019">"ikhadi"</string> <string name="autofill_save_type_username" msgid="239040540379769562">"igama lomsebenzisi"</string> <string name="autofill_save_type_email_address" msgid="5752949432129262174">"ikheli le-imeyili"</string> <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Hlala ubeke umoya phansi uphinde ufune ukukhuselwa eduze."</string> diff --git a/core/tests/coretests/src/android/app/NotificationHistoryTest.java b/core/tests/coretests/src/android/app/NotificationHistoryTest.java new file mode 100644 index 000000000000..08595bb43e06 --- /dev/null +++ b/core/tests/coretests/src/android/app/NotificationHistoryTest.java @@ -0,0 +1,232 @@ +/* + * 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.app; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.NotificationHistory.HistoricalNotification; +import android.graphics.drawable.Icon; +import android.os.Parcel; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +public class NotificationHistoryTest { + + private HistoricalNotification getHistoricalNotification(int index) { + return getHistoricalNotification("package" + index, index); + } + + private HistoricalNotification getHistoricalNotification(String packageName, int index) { + String expectedChannelName = "channelName" + index; + String expectedChannelId = "channelId" + index; + int expectedUid = 1123456 + index; + int expectedUserId = 11 + index; + long expectedPostTime = 987654321 + index; + String expectedTitle = "title" + index; + String expectedText = "text" + index; + Icon expectedIcon = Icon.createWithResource(InstrumentationRegistry.getContext(), + index); + + return new HistoricalNotification.Builder() + .setPackage(packageName) + .setChannelName(expectedChannelName) + .setChannelId(expectedChannelId) + .setUid(expectedUid) + .setUserId(expectedUserId) + .setPostedTimeMs(expectedPostTime) + .setTitle(expectedTitle) + .setText(expectedText) + .setIcon(expectedIcon) + .build(); + } + + @Test + public void testHistoricalNotificationBuilder() { + String expectedPackage = "package"; + String expectedChannelName = "channelName"; + String expectedChannelId = "channelId"; + int expectedUid = 1123456; + int expectedUserId = 11; + long expectedPostTime = 987654321; + String expectedTitle = "title"; + String expectedText = "text"; + Icon expectedIcon = Icon.createWithResource(InstrumentationRegistry.getContext(), + android.R.drawable.btn_star); + + HistoricalNotification n = new HistoricalNotification.Builder() + .setPackage(expectedPackage) + .setChannelName(expectedChannelName) + .setChannelId(expectedChannelId) + .setUid(expectedUid) + .setUserId(expectedUserId) + .setPostedTimeMs(expectedPostTime) + .setTitle(expectedTitle) + .setText(expectedText) + .setIcon(expectedIcon) + .build(); + + assertThat(n.getPackage()).isEqualTo(expectedPackage); + assertThat(n.getChannelName()).isEqualTo(expectedChannelName); + assertThat(n.getChannelId()).isEqualTo(expectedChannelId); + assertThat(n.getUid()).isEqualTo(expectedUid); + assertThat(n.getUserId()).isEqualTo(expectedUserId); + assertThat(n.getPostedTimeMs()).isEqualTo(expectedPostTime); + assertThat(n.getTitle()).isEqualTo(expectedTitle); + assertThat(n.getText()).isEqualTo(expectedText); + assertThat(expectedIcon.sameAs(n.getIcon())).isTrue(); + } + + @Test + public void testAddNotificationToWrite() { + NotificationHistory history = new NotificationHistory(); + HistoricalNotification n = getHistoricalNotification(0); + HistoricalNotification n2 = getHistoricalNotification(1); + + history.addNotificationToWrite(n2); + history.addNotificationToWrite(n); + + assertThat(history.getNotificationsToWrite().size()).isEqualTo(2); + assertThat(history.getNotificationsToWrite().get(0)).isSameAs(n2); + assertThat(history.getNotificationsToWrite().get(1)).isSameAs(n); + assertThat(history.getHistoryCount()).isEqualTo(2); + } + + @Test + public void testPoolStringsFromNotifications() { + NotificationHistory history = new NotificationHistory(); + + List<String> expectedStrings = new ArrayList<>(); + for (int i = 1; i <= 10; i++) { + HistoricalNotification n = getHistoricalNotification(i); + expectedStrings.add(n.getPackage()); + expectedStrings.add(n.getChannelName()); + expectedStrings.add(n.getChannelId()); + history.addNotificationToWrite(n); + } + + history.poolStringsFromNotifications(); + + assertThat(history.getPooledStringsToWrite().length).isEqualTo(expectedStrings.size()); + String previous = null; + for (String actual : history.getPooledStringsToWrite()) { + assertThat(expectedStrings).contains(actual); + + if (previous != null) { + assertThat(actual).isGreaterThan(previous); + } + previous = actual; + } + } + + @Test + public void testAddPooledStrings() { + NotificationHistory history = new NotificationHistory(); + + List<String> expectedStrings = new ArrayList<>(); + for (int i = 1; i <= 10; i++) { + HistoricalNotification n = getHistoricalNotification(i); + expectedStrings.add(n.getPackage()); + expectedStrings.add(n.getChannelName()); + expectedStrings.add(n.getChannelId()); + history.addNotificationToWrite(n); + } + + history.addPooledStrings(expectedStrings); + + String[] actualStrings = history.getPooledStringsToWrite(); + assertThat(actualStrings.length).isEqualTo(expectedStrings.size()); + String previous = null; + for (String actual : actualStrings) { + assertThat(expectedStrings).contains(actual); + + if (previous != null) { + assertThat(actual).isGreaterThan(previous); + } + previous = actual; + } + } + + @Test + public void testRemoveNotificationsFromWrite() { + NotificationHistory history = new NotificationHistory(); + + List<HistoricalNotification> postRemoveExpectedEntries = new ArrayList<>(); + List<String> postRemoveExpectedStrings = new ArrayList<>(); + for (int i = 1; i <= 10; i++) { + HistoricalNotification n = + getHistoricalNotification((i % 2 == 0) ? "pkgEven" : "pkgOdd", i); + + if (i % 2 == 0) { + postRemoveExpectedStrings.add(n.getPackage()); + postRemoveExpectedStrings.add(n.getChannelName()); + postRemoveExpectedStrings.add(n.getChannelId()); + postRemoveExpectedEntries.add(n); + } + + history.addNotificationToWrite(n); + } + + history.poolStringsFromNotifications(); + + assertThat(history.getNotificationsToWrite().size()).isEqualTo(10); + // 2 package names and 10 * 2 unique channel names and ids + assertThat(history.getPooledStringsToWrite().length).isEqualTo(22); + + history.removeNotificationsFromWrite("pkgOdd"); + + + // 1 package names and 5 * 2 unique channel names and ids + assertThat(history.getPooledStringsToWrite().length).isEqualTo(11); + assertThat(history.getNotificationsToWrite()) + .containsExactlyElementsIn(postRemoveExpectedEntries); + } + + @Test + public void testParceling() { + NotificationHistory history = new NotificationHistory(); + + List<HistoricalNotification> expectedEntries = new ArrayList<>(); + for (int i = 10; i >= 1; i--) { + HistoricalNotification n = getHistoricalNotification(i); + expectedEntries.add(n); + history.addNotificationToWrite(n); + } + history.poolStringsFromNotifications(); + + Parcel parcel = Parcel.obtain(); + history.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + NotificationHistory parceledHistory = NotificationHistory.CREATOR.createFromParcel(parcel); + + assertThat(parceledHistory.getHistoryCount()).isEqualTo(expectedEntries.size()); + + for (int i = 0; i < expectedEntries.size(); i++) { + assertThat(parceledHistory.hasNextNotification()).isTrue(); + + HistoricalNotification postParcelNotification = parceledHistory.getNextNotification(); + assertThat(postParcelNotification).isEqualTo(expectedEntries.get(i)); + } + } +} diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java index f14f2896d8f5..e140ad2a9a8f 100644 --- a/core/tests/coretests/src/android/content/ContentResolverTest.java +++ b/core/tests/coretests/src/android/content/ContentResolverTest.java @@ -82,22 +82,23 @@ public class ContentResolverTest { final AssetFileDescriptor afd = new AssetFileDescriptor( new ParcelFileDescriptor(mImage.getFileDescriptor()), 0, mSize, null); - when(mProvider.openTypedAssetFile(any(), any(), any(), any(), any())).thenReturn(afd); + when(mProvider.openTypedAssetFile(any(), any(), any(), any(), any(), any())).thenReturn( + afd); } - private static void assertImageAspectAndContents(Bitmap bitmap) { + private static void assertImageAspectAndContents(int width, int height, Bitmap bitmap) { // And correct aspect ratio - final int before = (100 * 1280) / 960; + final int before = (100 * width) / height; final int after = (100 * bitmap.getWidth()) / bitmap.getHeight(); assertEquals(before, after); // And scaled correctly final int halfX = bitmap.getWidth() / 2; final int halfY = bitmap.getHeight() / 2; - assertEquals(Color.BLUE, bitmap.getPixel(halfX - 10, halfY - 10)); - assertEquals(Color.RED, bitmap.getPixel(halfX + 10, halfY - 10)); - assertEquals(Color.RED, bitmap.getPixel(halfX - 10, halfY + 10)); - assertEquals(Color.RED, bitmap.getPixel(halfX + 10, halfY + 10)); + assertEquals(Color.BLUE, bitmap.getPixel(halfX - 5, halfY - 5)); + assertEquals(Color.RED, bitmap.getPixel(halfX + 5, halfY - 5)); + assertEquals(Color.RED, bitmap.getPixel(halfX - 5, halfY + 5)); + assertEquals(Color.RED, bitmap.getPixel(halfX + 5, halfY + 5)); } @Test @@ -112,7 +113,7 @@ public class ContentResolverTest { assertEquals(1280, res.getWidth()); assertEquals(960, res.getHeight()); - assertImageAspectAndContents(res); + assertImageAspectAndContents(1280, 960, res); } @Test @@ -127,7 +128,7 @@ public class ContentResolverTest { assertTrue(res.getWidth() <= 640); assertTrue(res.getHeight() <= 480); - assertImageAspectAndContents(res); + assertImageAspectAndContents(1280, 960, res); } @Test @@ -142,7 +143,7 @@ public class ContentResolverTest { assertTrue(res.getWidth() <= 640); assertTrue(res.getHeight() <= 480); - assertImageAspectAndContents(res); + assertImageAspectAndContents(1280, 960, res); } @Test @@ -157,7 +158,23 @@ public class ContentResolverTest { assertEquals(32, res.getWidth()); assertEquals(24, res.getHeight()); - assertImageAspectAndContents(res); + assertImageAspectAndContents(32, 24, res); + } + + @Test + public void testLoadThumbnail_Large() throws Exception { + // Test very large and extreme ratio image + initImage(1080, 30000); + + Bitmap res = ContentResolver.loadThumbnail(mClient, + Uri.parse("content://com.example/"), new Size(1080, 540), null, + ImageDecoder.ALLOCATOR_SOFTWARE); + + // Size should be much smaller + assertTrue(res.getWidth() <= 2160); + assertTrue(res.getHeight() <= 1080); + + assertImageAspectAndContents(1080, 30000, res); } @Test diff --git a/core/tests/coretests/src/android/graphics/TypefaceEqualsTest.java b/core/tests/coretests/src/android/graphics/TypefaceEqualsTest.java new file mode 100644 index 000000000000..6ae7eb72fab2 --- /dev/null +++ b/core/tests/coretests/src/android/graphics/TypefaceEqualsTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import android.content.res.AssetManager; +import android.graphics.fonts.Font; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.IOException; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class TypefaceEqualsTest { + @Test + public void testFontEqualWithLocale() throws IOException { + final AssetManager am = + InstrumentationRegistry.getInstrumentation().getContext().getAssets(); + + Font masterFont = new Font.Builder(am, "fonts/a3em.ttf").build(); + + Font jaFont = new Font.Builder(masterFont.getBuffer(), new File("fonts/a3em.ttf"), "ja") + .build(); + Font jaFont2 = new Font.Builder(masterFont.getBuffer(), new File("fonts/a3em.ttf"), "ja") + .build(); + Font koFont = new Font.Builder(masterFont.getBuffer(), new File("fonts/a3em.ttf"), "ko") + .build(); + + assertEquals(jaFont, jaFont2); + assertNotEquals(jaFont, koFont); + assertNotEquals(jaFont, masterFont); + } +} diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java index 77b7f2a3ce6c..0c83390f9f1e 100644 --- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java +++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java @@ -16,12 +16,10 @@ package android.provider; -import static android.provider.DeviceConfig.OnPropertiesChangedListener; import static android.provider.DeviceConfig.Properties; import static com.google.common.truth.Truth.assertThat; -import android.app.ActivityThread; import android.content.ContentResolver; import android.os.Bundle; import android.platform.test.annotations.Presubmit; @@ -35,9 +33,6 @@ import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - /** Tests that ensure appropriate settings are backed up. */ @Presubmit @RunWith(AndroidJUnit4.class) @@ -393,6 +388,13 @@ public class DeviceConfigTest { assertThat(properties.getKeyset()).containsExactly(KEY, KEY2); assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE3); assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2); + + DeviceConfig.setProperty(NAMESPACE, KEY3, VALUE, false); + properties = DeviceConfig.getProperties(NAMESPACE); + assertThat(properties.getKeyset()).containsExactly(KEY, KEY2, KEY3); + assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE3); + assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2); + assertThat(properties.getString(KEY3, DEFAULT_VALUE)).isEqualTo(VALUE); } @Test @@ -482,29 +484,30 @@ public class DeviceConfigTest { assertThat(properties.getString(KEY3, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE); } - @Test - public void testOnPropertiesChangedListener() throws InterruptedException { - final CountDownLatch countDownLatch = new CountDownLatch(1); - - OnPropertiesChangedListener changeListener = (properties) -> { - assertThat(properties.getNamespace()).isEqualTo(NAMESPACE); - assertThat(properties.getKeyset()).contains(KEY); - assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE); - countDownLatch.countDown(); - }; - - try { - DeviceConfig.addOnPropertiesChangedListener(NAMESPACE, - ActivityThread.currentApplication().getMainExecutor(), changeListener); - DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false); - assertThat(countDownLatch.await( - WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue(); - } catch (InterruptedException e) { - Assert.fail(e.getMessage()); - } finally { - DeviceConfig.removeOnPropertiesChangedListener(changeListener); - } - } + // TODO(mpape): resolve b/142727848 and re-enable this test +// @Test +// public void testOnPropertiesChangedListener() throws InterruptedException { +// final CountDownLatch countDownLatch = new CountDownLatch(1); +// +// OnPropertiesChangedListener changeListener = (properties) -> { +// assertThat(properties.getNamespace()).isEqualTo(NAMESPACE); +// assertThat(properties.getKeyset()).contains(KEY); +// assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE); +// countDownLatch.countDown(); +// }; +// +// try { +// DeviceConfig.addOnPropertiesChangedListener(NAMESPACE, +// ActivityThread.currentApplication().getMainExecutor(), changeListener); +// DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false); +// assertThat(countDownLatch.await( +// WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue(); +// } catch (InterruptedException e) { +// Assert.fail(e.getMessage()); +// } finally { +// DeviceConfig.removeOnPropertiesChangedListener(changeListener); +// } +// } private static boolean deleteViaContentProvider(String namespace, String key) { ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver(); diff --git a/core/tests/coretests/src/android/provider/TestDocumentsProvider.java b/core/tests/coretests/src/android/provider/TestDocumentsProvider.java index 1bd8ff6bdea3..5f640bee7ce9 100644 --- a/core/tests/coretests/src/android/provider/TestDocumentsProvider.java +++ b/core/tests/coretests/src/android/provider/TestDocumentsProvider.java @@ -93,12 +93,14 @@ public class TestDocumentsProvider extends DocumentsProvider { } @Override - protected int enforceReadPermissionInner(Uri uri, String callingPkg, IBinder callerToken) { + protected int enforceReadPermissionInner(Uri uri, String callingPkg, + @Nullable String callingFeatureId, IBinder callerToken) { return AppOpsManager.MODE_ALLOWED; } @Override - protected int enforceWritePermissionInner(Uri uri, String callingPkg, IBinder callerToken) { + protected int enforceWritePermissionInner(Uri uri, String callingPkg, + @Nullable String callingFeatureId, IBinder callerToken) { return AppOpsManager.MODE_ALLOWED; } diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index 5ea91da98fd3..d427cbda7fb6 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -24,12 +24,12 @@ import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withText; -import static com.android.internal.app.ChooserActivity.CALLER_TARGET_SCORE_BOOST; -import static com.android.internal.app.ChooserActivity.SHORTCUT_TARGET_SCORE_BOOST; import static com.android.internal.app.ChooserActivity.TARGET_TYPE_CHOOSER_TARGET; import static com.android.internal.app.ChooserActivity.TARGET_TYPE_DEFAULT; import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE; import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER; +import static com.android.internal.app.ChooserListAdapter.CALLER_TARGET_SCORE_BOOST; +import static com.android.internal.app.ChooserListAdapter.SHORTCUT_TARGET_SCORE_BOOST; import static com.android.internal.app.ChooserWrapperActivity.sOverrides; import static org.hamcrest.CoreMatchers.is; @@ -69,6 +69,7 @@ import androidx.test.rule.ActivityTestRule; import com.android.internal.R; import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; +import com.android.internal.app.chooser.DisplayResolveInfo; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -819,17 +820,18 @@ public class ChooserActivityTest { when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); - when(sOverrides.resolverListController.getScore(Mockito.isA( - ResolverActivity.DisplayResolveInfo.class))).thenReturn(testBaseScore); + when(sOverrides.resolverListController.getScore(Mockito.isA(DisplayResolveInfo.class))) + .thenReturn(testBaseScore); final ChooserWrapperActivity activity = mActivityRule .launchActivity(Intent.createChooser(sendIntent, null)); waitForIdle(); - final ResolverActivity.DisplayResolveInfo testDri = + final DisplayResolveInfo testDri = activity.createTestDisplayResolveInfo(sendIntent, - ResolverDataProvider.createResolveInfo(3, 0), "testLabel", "testInfo", sendIntent); - final ChooserActivity.ChooserListAdapter adapter = activity.getAdapter(); + ResolverDataProvider.createResolveInfo(3, 0), "testLabel", "testInfo", sendIntent, + /* resolveInfoPresentationGetter */ null); + final ChooserListAdapter adapter = activity.getAdapter(); assertThat(adapter.getBaseScore(null, 0), is(CALLER_TARGET_SCORE_BOOST)); assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_DEFAULT), is(testBaseScore)); @@ -970,7 +972,8 @@ public class ChooserActivityTest { ri, "testLabel", "testInfo", - sendIntent), + sendIntent, + /* resolveInfoPresentationGetter */ null), serviceTargets, TARGET_TYPE_CHOOSER_TARGET) ); @@ -1036,7 +1039,8 @@ public class ChooserActivityTest { ri, "testLabel", "testInfo", - sendIntent), + sendIntent, + /* resolveInfoPresentationGetter */ null), serviceTargets, TARGET_TYPE_CHOOSER_TARGET) ); @@ -1097,7 +1101,8 @@ public class ChooserActivityTest { ri, "testLabel", "testInfo", - sendIntent), + sendIntent, + /* resolveInfoPresentationGetter */ null), serviceTargets, TARGET_TYPE_CHOOSER_TARGET) ); diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java index 1d567c73f376..03705d0599e5 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java @@ -18,6 +18,7 @@ package com.android.internal.app; import static org.mockito.Mockito.mock; +import android.annotation.Nullable; import android.app.usage.UsageStatsManager; import android.content.ContentResolver; import android.content.Context; @@ -30,6 +31,9 @@ import android.graphics.Bitmap; import android.net.Uri; import android.util.Size; +import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter; +import com.android.internal.app.chooser.DisplayResolveInfo; +import com.android.internal.app.chooser.TargetInfo; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -64,7 +68,7 @@ public class ChooserWrapperActivity extends ChooserActivity { } @Override - public void safelyStartActivity(TargetInfo cti) { + public void safelyStartActivity(com.android.internal.app.chooser.TargetInfo cti) { if (sOverrides.onSafelyStartCallback != null && sOverrides.onSafelyStartCallback.apply(cti)) { return; @@ -133,8 +137,10 @@ public class ChooserWrapperActivity extends ChooserActivity { } public DisplayResolveInfo createTestDisplayResolveInfo(Intent originalIntent, ResolveInfo pri, - CharSequence pLabel, CharSequence pInfo, Intent pOrigIntent) { - return new DisplayResolveInfo(originalIntent, pri, pLabel, pInfo, pOrigIntent); + CharSequence pLabel, CharSequence pInfo, Intent replacementIntent, + @Nullable ResolveInfoPresentationGetter resolveInfoPresentationGetter) { + return new DisplayResolveInfo(originalIntent, pri, pLabel, pInfo, replacementIntent, + resolveInfoPresentationGetter); } /** diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java index 0fa29bf4436a..a401e21df805 100644 --- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java @@ -43,10 +43,10 @@ import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; -import com.android.internal.app.ResolverActivity.ActivityInfoPresentationGetter; -import com.android.internal.app.ResolverActivity.ResolveInfoPresentationGetter; import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; import com.android.internal.app.ResolverDataProvider.PackageManagerMockedInfo; +import com.android.internal.app.ResolverListAdapter.ActivityInfoPresentationGetter; +import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter; import com.android.internal.widget.ResolverDrawerLayout; import org.junit.Before; @@ -83,7 +83,7 @@ public class ResolverActivityTest { Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); - Espresso.registerIdlingResources(activity.getLabelIdlingResource()); + Espresso.registerIdlingResources(activity.getAdapter().getLabelIdlingResource()); waitForIdle(); assertThat(activity.getAdapter().getCount(), is(2)); @@ -216,7 +216,7 @@ public class ResolverActivityTest { Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); - Espresso.registerIdlingResources(activity.getLabelIdlingResource()); + Espresso.registerIdlingResources(activity.getAdapter().getLabelIdlingResource()); waitForIdle(); // The other entry is filtered to the last used slot @@ -254,7 +254,7 @@ public class ResolverActivityTest { Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); - Espresso.registerIdlingResources(activity.getLabelIdlingResource()); + Espresso.registerIdlingResources(activity.getAdapter().getLabelIdlingResource()); waitForIdle(); // The other entry is filtered to the other profile slot @@ -300,7 +300,7 @@ public class ResolverActivityTest { .thenReturn(resolvedComponentInfos.get(1).getResolveInfoAt(0)); final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); - Espresso.registerIdlingResources(activity.getLabelIdlingResource()); + Espresso.registerIdlingResources(activity.getAdapter().getLabelIdlingResource()); waitForIdle(); // The other entry is filtered to the other profile slot diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java index 9082543e8ebc..39cc83c3bc43 100644 --- a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java +++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java @@ -19,10 +19,14 @@ package com.android.internal.app; import static org.mockito.Mockito.mock; import android.app.usage.UsageStatsManager; +import android.content.Context; +import android.content.Intent; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; -import androidx.test.espresso.idling.CountingIdlingResource; +import com.android.internal.app.chooser.TargetInfo; +import java.util.List; import java.util.function.Function; /* @@ -31,15 +35,17 @@ import java.util.function.Function; public class ResolverWrapperActivity extends ResolverActivity { static final OverrideData sOverrides = new OverrideData(); private UsageStatsManager mUsm; - private CountingIdlingResource mLabelIdlingResource = - new CountingIdlingResource("LoadLabelTask"); - public CountingIdlingResource getLabelIdlingResource() { - return mLabelIdlingResource; + @Override + public ResolverListAdapter createAdapter(Context context, List<Intent> payloadIntents, + Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed, + boolean useLayoutForBrowsables) { + return new ResolverWrapperAdapter(context, payloadIntents, initialIntents, rList, + filterLastUsed, createListController(), useLayoutForBrowsables, this); } - ResolveListAdapter getAdapter() { - return mAdapter; + ResolverWrapperAdapter getAdapter() { + return (ResolverWrapperAdapter) mAdapter; } @Override @@ -72,11 +78,6 @@ public class ResolverWrapperActivity extends ResolverActivity { return super.getPackageManager(); } - @Override - protected LoadLabelTask getLoadLabelTask(DisplayResolveInfo info, ViewHolder holder) { - return new LoadLabelWrapperTask(info, holder); - } - /** * We cannot directly mock the activity created since instrumentation creates it. * <p> @@ -96,22 +97,4 @@ public class ResolverWrapperActivity extends ResolverActivity { resolverListController = mock(ResolverListController.class); } } - - class LoadLabelWrapperTask extends LoadLabelTask { - - protected LoadLabelWrapperTask(DisplayResolveInfo dri, ViewHolder holder) { - super(dri, holder); - } - - @Override - protected void onPreExecute() { - mLabelIdlingResource.increment(); - } - - @Override - protected void onPostExecute(CharSequence[] result) { - super.onPostExecute(result); - mLabelIdlingResource.decrement(); - } - } }
\ No newline at end of file diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperAdapter.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperAdapter.java new file mode 100644 index 000000000000..e41df4186a12 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperAdapter.java @@ -0,0 +1,71 @@ +/* + * 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.internal.app; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; + +import androidx.test.espresso.idling.CountingIdlingResource; + +import com.android.internal.app.chooser.DisplayResolveInfo; + +import java.util.List; + +public class ResolverWrapperAdapter extends ResolverListAdapter { + + private CountingIdlingResource mLabelIdlingResource = + new CountingIdlingResource("LoadLabelTask"); + + public ResolverWrapperAdapter(Context context, + List<Intent> payloadIntents, + Intent[] initialIntents, + List<ResolveInfo> rList, boolean filterLastUsed, + ResolverListController resolverListController, boolean useLayoutForBrowsables, + ResolverListCommunicator resolverListCommunicator) { + super(context, payloadIntents, initialIntents, rList, filterLastUsed, + resolverListController, + useLayoutForBrowsables, resolverListCommunicator); + } + + public CountingIdlingResource getLabelIdlingResource() { + return mLabelIdlingResource; + } + + @Override + protected LoadLabelTask getLoadLabelTask(DisplayResolveInfo info, ViewHolder holder) { + return new LoadLabelWrapperTask(info, holder); + } + + class LoadLabelWrapperTask extends LoadLabelTask { + + protected LoadLabelWrapperTask(DisplayResolveInfo dri, ViewHolder holder) { + super(dri, holder); + } + + @Override + protected void onPreExecute() { + mLabelIdlingResource.increment(); + } + + @Override + protected void onPostExecute(CharSequence[] result) { + super.onPostExecute(result); + mLabelIdlingResource.decrement(); + } + } +} diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java index b3f6fdbd9b30..364e4ea6e3ec 100644 --- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java +++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java @@ -46,10 +46,12 @@ import android.content.res.Configuration; import android.os.Binder; import android.os.RemoteException; import android.os.UserHandle; +import android.platform.test.annotations.Presubmit; import android.view.WindowManagerGlobal; import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.FlakyTest; import androidx.test.filters.MediumTest; import androidx.test.platform.app.InstrumentationRegistry; @@ -70,6 +72,8 @@ import org.mockito.quality.Strictness; */ @RunWith(AndroidJUnit4.class) @MediumTest +@Presubmit +@FlakyTest(bugId = 143153552) public class ActivityThreadClientTest { @Test diff --git a/data/etc/platform.xml b/data/etc/platform.xml index dceb243972e2..80098c5a81f5 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -174,6 +174,7 @@ <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="incidentd" /> <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="incidentd" /> <assign-permission name="android.permission.REQUEST_INCIDENT_REPORT_APPROVAL" uid="incidentd" /> + <assign-permission name="android.permission.PEEK_DROPBOX_DATA" uid="incidentd" /> <assign-permission name="android.permission.ACCESS_LOWPAN_STATE" uid="lowpan" /> <assign-permission name="android.permission.MANAGE_LOWPAN_INTERFACES" uid="lowpan" /> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 342259d18b05..ecdf5373f24f 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -169,12 +169,6 @@ "group": "WM_DEBUG_RESIZE", "at": "com\/android\/server\/wm\/WindowState.java" }, - "-1822611824": { - "message": "\tRemove token=%s", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, "-1797409732": { "message": "Skipping %s because %s", "level": "VERBOSE", @@ -421,6 +415,12 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-1248645819": { + "message": "\tAdd container=%s", + "level": "DEBUG", + "group": "WM_DEBUG_REMOTE_ANIMATIONS", + "at": "com\/android\/server\/wm\/RemoteAnimationController.java" + }, "-1219773477": { "message": "setInputConsumerEnabled(%s): mCanceled=%b", "level": "DEBUG", @@ -493,12 +493,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/RootWindowContainer.java" }, - "-1099052739": { - "message": "\tAdd token=%s", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, "-1089874824": { "message": "SURFACE SHOW (performLayout): %s", "level": "INFO", @@ -721,6 +715,12 @@ "group": "WM_DEBUG_SCREEN_ON", "at": "com\/android\/server\/wm\/DisplayContent.java" }, + "-633961578": { + "message": "applyAnimation: transition animation is disabled or skipped. container=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "-622997754": { "message": "postWindowRemoveCleanupLocked: %s", "level": "VERBOSE", @@ -883,12 +883,6 @@ "group": "WM_DEBUG_ADD_REMOVE", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "-253016819": { - "message": "applyAnimation: transition animation is disabled or skipped. atoken=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, "-251259736": { "message": "No longer freezing: %s", "level": "VERBOSE", @@ -961,6 +955,12 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/Session.java" }, + "-33096143": { + "message": "applyAnimation: transition animation is disabled or skipped. container=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", + "at": "com\/android\/server\/wm\/WindowContainer.java" + }, "-29233992": { "message": "SURFACE CLEAR CROP: %s", "level": "INFO", @@ -1375,6 +1375,12 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/WindowSurfaceController.java" }, + "638429464": { + "message": "\tRemove container=%s", + "level": "DEBUG", + "group": "WM_DEBUG_REMOTE_ANIMATIONS", + "at": "com\/android\/server\/wm\/RemoteAnimationController.java" + }, "644675193": { "message": "Real start recents", "level": "DEBUG", @@ -1465,12 +1471,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "815803557": { - "message": "applyAnimation: atoken=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, "829434921": { "message": "Draw state now committed in %s", "level": "VERBOSE", @@ -1543,6 +1543,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "972354148": { + "message": "\tcontainer=%s", + "level": "DEBUG", + "group": "WM_DEBUG_REMOTE_ANIMATIONS", + "at": "com\/android\/server\/wm\/RemoteAnimationController.java" + }, "1001904964": { "message": "***** BOOT TIMEOUT: forcing display enabled", "level": "WARN", @@ -1675,12 +1681,6 @@ "group": "WM_DEBUG_FOCUS", "at": "com\/android\/server\/wm\/DisplayContent.java" }, - "1358786604": { - "message": "No thumbnail header bitmap for: %d", - "level": "DEBUG", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, "1364498663": { "message": "notifyAppResumed: wasStopped=%b %s", "level": "VERBOSE", @@ -1795,11 +1795,11 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimationController.java" }, - "1531527061": { - "message": "createAnimationAdapter(): token=%s", + "1528528509": { + "message": "No thumbnail header bitmap for: %s", "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" + "group": "WM_DEBUG_APP_TRANSITIONS", + "at": "com\/android\/server\/wm\/ActivityRecord.java" }, "1563755163": { "message": "Permission Denial: %s from pid=%d, uid=%d requires %s", @@ -1819,6 +1819,12 @@ "group": "WM_DEBUG_ADD_REMOVE", "at": "com\/android\/server\/wm\/WindowState.java" }, + "1584270979": { + "message": "applyAnimation: container=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", + "at": "com\/android\/server\/wm\/WindowContainer.java" + }, "1589610525": { "message": "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS: anim=%s transit=%s isEntrance=true Callers=%s", "level": "VERBOSE", @@ -1909,11 +1915,11 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "1804869745": { + "1831008694": { "message": "Loading animation for app transition. transit=%s enter=%b frame=%s insets=%s surfaceInsets=%s", "level": "DEBUG", "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/ActivityRecord.java" + "at": "com\/android\/server\/wm\/WindowContainer.java" }, "1836214582": { "message": "startingData was nulled out before handling mAddStartingWindow: %s", @@ -1939,12 +1945,6 @@ "group": "WM_DEBUG_SCREEN_ON", "at": "com\/android\/server\/wm\/DisplayPolicy.java" }, - "1865246212": { - "message": "\tapp=%s", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, "1866772666": { "message": "SAFE MODE not enabled", "level": "INFO", @@ -2017,6 +2017,12 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "2022422429": { + "message": "createAnimationAdapter(): container=%s", + "level": "DEBUG", + "group": "WM_DEBUG_REMOTE_ANIMATIONS", + "at": "com\/android\/server\/wm\/RemoteAnimationController.java" + }, "2028163120": { "message": "applyAnimation: anim=%s nextAppTransition=ANIM_SCALE_UP transit=%s isEntrance=%s Callers=%s", "level": "VERBOSE", diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java index 552088f7c478..ba96a06cc852 100644 --- a/graphics/java/android/graphics/fonts/Font.java +++ b/graphics/java/android/graphics/fonts/Font.java @@ -519,12 +519,13 @@ public final class Font { } Font f = (Font) o; return mFontStyle.equals(f.mFontStyle) && f.mTtcIndex == mTtcIndex - && Arrays.equals(f.mAxes, mAxes) && f.mBuffer.equals(mBuffer); + && Arrays.equals(f.mAxes, mAxes) && f.mBuffer.equals(mBuffer) + && Objects.equals(f.mLocaleList, mLocaleList); } @Override public int hashCode() { - return Objects.hash(mFontStyle, mTtcIndex, Arrays.hashCode(mAxes), mBuffer); + return Objects.hash(mFontStyle, mTtcIndex, Arrays.hashCode(mAxes), mBuffer, mLocaleList); } @Override diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index 254456cea536..538319c3f1d7 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -763,28 +763,33 @@ public final class KeyChain { * @see KeyChain#bind */ public static class KeyChainConnection implements Closeable { - private final Context context; - private final ServiceConnection serviceConnection; - private final IKeyChainService service; + private final Context mContext; + private final ServiceConnection mServiceConnection; + private final IKeyChainService mService; protected KeyChainConnection(Context context, ServiceConnection serviceConnection, IKeyChainService service) { - this.context = context; - this.serviceConnection = serviceConnection; - this.service = service; + this.mContext = context; + this.mServiceConnection = serviceConnection; + this.mService = service; } @Override public void close() { - context.unbindService(serviceConnection); + mContext.unbindService(mServiceConnection); } + + /** returns the service binder. */ public IKeyChainService getService() { - return service; + return mService; } } /** - * @hide for reuse by CertInstaller and Settings. - * + * Bind to KeyChainService in the current user. * Caller should call unbindService on the result when finished. + * + *@throws InterruptedException if interrupted during binding. + *@throws AssertionError if unable to bind to KeyChainService. + * @hide for reuse by CertInstaller and Settings. */ @WorkerThread public static KeyChainConnection bind(@NonNull Context context) throws InterruptedException { @@ -792,6 +797,11 @@ public final class KeyChain { } /** + * Bind to KeyChainService in the target user. + * Caller should call unbindService on the result when finished. + * + * @throws InterruptedException if interrupted during binding. + * @throws AssertionError if unable to bind to KeyChainService. * @hide */ @WorkerThread @@ -814,6 +824,16 @@ public final class KeyChain { } } } + @Override public void onBindingDied(ComponentName name) { + if (!mConnectedAtLeastOnce) { + mConnectedAtLeastOnce = true; + try { + q.put(null); + } catch (InterruptedException e) { + // will never happen, since the queue starts with one available slot + } + } + } @Override public void onServiceDisconnected(ComponentName name) {} }; Intent intent = new Intent(IKeyChainService.class.getName()); @@ -823,7 +843,13 @@ public final class KeyChain { intent, keyChainServiceConnection, Context.BIND_AUTO_CREATE, user)) { throw new AssertionError("could not bind to KeyChainService"); } - return new KeyChainConnection(context, keyChainServiceConnection, q.take()); + IKeyChainService service = q.take(); + if (service != null) { + return new KeyChainConnection(context, keyChainServiceConnection, service); + } else { + context.unbindService(keyChainServiceConnection); + throw new AssertionError("KeyChainService died while binding"); + } } private static void ensureNotOnMainThread(@NonNull Context context) { diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index ec83a1961eff..1eb089d8764c 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -136,7 +136,6 @@ public: const Paint* paint) override; virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) override; - virtual bool drawTextAbsolutePos() const override { return true; } virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override; virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left, diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index b98ffca84dfc..c138a32eacc2 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -95,18 +95,10 @@ public: void operator()(size_t start, size_t end) { auto glyphFunc = [&](uint16_t* text, float* positions) { - if (canvas->drawTextAbsolutePos()) { - for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) { - text[textIndex++] = layout.getGlyphId(i); - positions[posIndex++] = x + layout.getX(i); - positions[posIndex++] = y + layout.getY(i); - } - } else { - for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) { - text[textIndex++] = layout.getGlyphId(i); - positions[posIndex++] = layout.getX(i); - positions[posIndex++] = layout.getY(i); - } + for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) { + text[textIndex++] = layout.getGlyphId(i); + positions[posIndex++] = x + layout.getX(i); + positions[posIndex++] = y + layout.getY(i); } }; @@ -166,9 +158,6 @@ void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, minikin::MinikinRect bounds; layout.getBounds(&bounds); - if (!drawTextAbsolutePos()) { - bounds.offset(x, y); - } // Set align to left for drawing, as we don't want individual // glyphs centered or right-aligned; the offset above takes diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index b90a4a3d68e6..27dfed305a94 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -253,15 +253,6 @@ public: virtual void drawPicture(const SkPicture& picture) = 0; /** - * Specifies if the positions passed to ::drawText are absolute or relative - * to the (x,y) value provided. - * - * If true the (x,y) values are ignored. Otherwise, those (x,y) values need - * to be added to each glyph's position to get its absolute position. - */ - virtual bool drawTextAbsolutePos() const = 0; - - /** * Draws a VectorDrawable onto the canvas. */ virtual void drawVectorDrawable(VectorDrawableRoot* tree) = 0; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index b41bdf6a174f..e5c502cca09a 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -564,8 +564,8 @@ SkISize CanvasContext::getNextFrameSize() const { ReliableSurface* surface = mNativeSurface.get(); if (surface) { SkISize size; - surface->query(NATIVE_WINDOW_WIDTH, &size.fWidth); - surface->query(NATIVE_WINDOW_HEIGHT, &size.fHeight); + size.fWidth = ANativeWindow_getWidth(surface); + size.fHeight = ANativeWindow_getHeight(surface); return size; } return {INT32_MAX, INT32_MAX}; diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp index 6fb164a99ae4..7d999c43b560 100644 --- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp +++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp @@ -208,9 +208,8 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscr test::TestContext testContext; testContext.setRenderOffscreen(true); auto surface = testContext.surface(); - int width, height; - surface->query(NATIVE_WINDOW_WIDTH, &width); - surface->query(NATIVE_WINDOW_HEIGHT, &height); + int width = ANativeWindow_getWidth(surface.get()); + int height = ANativeWindow_getHeight(surface.get()); canvasContext->setSurface(std::move(surface)); TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get()); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index d3db9d83b3e7..ca9d4d3e8812 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -230,7 +230,7 @@ public class LocationManager { public static final String METADATA_SETTINGS_FOOTER_STRING = "com.android.settings.location.FOOTER_STRING"; - private static final long GET_CURRENT_LOCATION_TIMEOUT_MS = 30 * 1000; + private static final long GET_CURRENT_LOCATION_MAX_TIMEOUT_MS = 30 * 1000; private final Context mContext; @@ -630,7 +630,10 @@ public class LocationManager { @Nullable CancellationSignal cancellationSignal, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Location> consumer) { LocationRequest currentLocationRequest = new LocationRequest(locationRequest) - .setNumUpdates(1).setExpireIn(GET_CURRENT_LOCATION_TIMEOUT_MS); + .setNumUpdates(1); + if (currentLocationRequest.getExpireIn() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) { + currentLocationRequest.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS); + } GetCurrentLocationTransport listenerTransport = new GetCurrentLocationTransport(executor, consumer); @@ -684,6 +687,7 @@ public class LocationManager { LocationRequest request = LocationRequest.createFromDeprecatedProvider( provider, 0, 0, true); + request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS); requestLocationUpdates(request, listener, looper); } @@ -714,6 +718,7 @@ public class LocationManager { LocationRequest request = LocationRequest.createFromDeprecatedCriteria( criteria, 0, 0, true); + request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS); requestLocationUpdates(request, listener, looper); } @@ -740,6 +745,7 @@ public class LocationManager { LocationRequest request = LocationRequest.createFromDeprecatedProvider( provider, 0, 0, true); + request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS); requestLocationUpdates(request, pendingIntent); } @@ -767,6 +773,7 @@ public class LocationManager { LocationRequest request = LocationRequest.createFromDeprecatedCriteria( criteria, 0, 0, true); + request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS); requestLocationUpdates(request, pendingIntent); } @@ -1835,7 +1842,7 @@ public class LocationManager { } try { - return mGnssStatusListenerManager.addListener(listener, new Handler()); + return mGnssStatusListenerManager.addListener(listener, Runnable::run); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2422,7 +2429,7 @@ public class LocationManager { mAlarmManager = alarmManager; mAlarmManager.set( ELAPSED_REALTIME, - SystemClock.elapsedRealtime() + GET_CURRENT_LOCATION_TIMEOUT_MS, + SystemClock.elapsedRealtime() + GET_CURRENT_LOCATION_MAX_TIMEOUT_MS, "GetCurrentLocation", this, null); @@ -2694,9 +2701,9 @@ public class LocationManager { return mTtff; } - public boolean addListener(@NonNull GpsStatus.Listener listener, @NonNull Handler handler) + public boolean addListener(@NonNull GpsStatus.Listener listener, @NonNull Executor executor) throws RemoteException { - return addInternal(listener, handler); + return addInternal(listener, executor); } public boolean addListener(@NonNull OnNmeaMessageListener listener, @@ -2750,9 +2757,14 @@ public class LocationManager { protected boolean registerService() throws RemoteException { Preconditions.checkState(mListenerTransport == null); - mListenerTransport = new GnssStatusListener(); - return mService.registerGnssStatusCallback(mListenerTransport, - mContext.getPackageName(), mContext.getFeatureId()); + GnssStatusListener transport = new GnssStatusListener(); + if (mService.registerGnssStatusCallback(transport, mContext.getPackageName(), + mContext.getFeatureId())) { + mListenerTransport = transport; + return true; + } else { + return false; + } } @Override @@ -2810,10 +2822,14 @@ public class LocationManager { protected boolean registerService() throws RemoteException { Preconditions.checkState(mListenerTransport == null); - mListenerTransport = new GnssMeasurementsListener(); - return mService.addGnssMeasurementsListener(mListenerTransport, - mContext.getPackageName(), mContext.getFeatureId(), - "gnss measurement callback"); + GnssMeasurementsListener transport = new GnssMeasurementsListener(); + if (mService.addGnssMeasurementsListener(transport, mContext.getPackageName(), + mContext.getFeatureId(), "gnss measurement callback")) { + mListenerTransport = transport; + return true; + } else { + return false; + } } @Override @@ -2847,10 +2863,14 @@ public class LocationManager { protected boolean registerService() throws RemoteException { Preconditions.checkState(mListenerTransport == null); - mListenerTransport = new GnssNavigationMessageListener(); - return mService.addGnssNavigationMessageListener(mListenerTransport, - mContext.getPackageName(), mContext.getFeatureId(), - "gnss navigation callback"); + GnssNavigationMessageListener transport = new GnssNavigationMessageListener(); + if (mService.addGnssNavigationMessageListener(transport, mContext.getPackageName(), + mContext.getFeatureId(), "gnss navigation callback")) { + mListenerTransport = transport; + return true; + } else { + return false; + } } @Override @@ -2884,9 +2904,14 @@ public class LocationManager { protected boolean registerService() throws RemoteException { Preconditions.checkState(mListenerTransport == null); - mListenerTransport = new BatchedLocationCallback(); - return mService.addGnssBatchingCallback(mListenerTransport, mContext.getPackageName(), - mContext.getFeatureId(), "batched location callback"); + BatchedLocationCallback transport = new BatchedLocationCallback(); + if (mService.addGnssBatchingCallback(transport, mContext.getPackageName(), + mContext.getFeatureId(), "batched location callback")) { + mListenerTransport = transport; + return true; + } else { + return false; + } } @Override diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java index 0f38f7f057fa..3abd2c2144e1 100644 --- a/location/java/android/location/LocationRequest.java +++ b/location/java/android/location/LocationRequest.java @@ -161,6 +161,7 @@ public final class LocationRequest implements Parcelable { private boolean mExplicitFastestInterval = false; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private long mExpireAt = Long.MAX_VALUE; // no expiry + private long mExpireIn = Long.MAX_VALUE; // no expiry @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private int mNumUpdates = Integer.MAX_VALUE; // no expiry @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @@ -268,6 +269,7 @@ public final class LocationRequest implements Parcelable { mFastestInterval = src.mFastestInterval; mExplicitFastestInterval = src.mExplicitFastestInterval; mExpireAt = src.mExpireAt; + mExpireIn = src.mExpireIn; mNumUpdates = src.mNumUpdates; mSmallestDisplacement = src.mSmallestDisplacement; mProvider = src.mProvider; @@ -342,7 +344,7 @@ public final class LocationRequest implements Parcelable { * @throws IllegalArgumentException if the interval is less than zero */ public @NonNull LocationRequest setInterval(long millis) { - checkInterval(millis); + Preconditions.checkArgument(millis >= 0, "invalid interval: + millis"); mInterval = millis; if (!mExplicitFastestInterval) { mFastestInterval = (long) (mInterval / FASTEST_INTERVAL_FACTOR); @@ -370,9 +372,7 @@ public final class LocationRequest implements Parcelable { * * @param enabled Enable or disable low power mode * @return the same object, so that setters can be chained - * @hide */ - @SystemApi public @NonNull LocationRequest setLowPowerMode(boolean enabled) { mLowPowerMode = enabled; return this; @@ -380,10 +380,7 @@ public final class LocationRequest implements Parcelable { /** * Returns true if low power mode is enabled. - * - * @hide */ - @SystemApi public boolean isLowPowerMode() { return mLowPowerMode; } @@ -431,93 +428,80 @@ public final class LocationRequest implements Parcelable { * <p>An interval of 0 is allowed, but not recommended, since * location updates may be extremely fast on future implementations. * - * <p>If {@link #setFastestInterval} is set slower than {@link #setInterval}, + * <p>If the fastest interval set is slower than {@link #setInterval}, * then your effective fastest interval is {@link #setInterval}. * - * @param millis fastest interval for updates in milliseconds, exact + * @param millis fastest interval for updates in milliseconds * @return the same object, so that setters can be chained * @throws IllegalArgumentException if the interval is less than zero */ public @NonNull LocationRequest setFastestInterval(long millis) { - checkInterval(millis); + Preconditions.checkArgument(millis >= 0, "invalid interval: + millis"); mExplicitFastestInterval = true; mFastestInterval = millis; return this; } /** - * Get the fastest interval of this request, in milliseconds. + * Get the fastest interval of this request in milliseconds. The system will never provide + * location updates faster than the minimum of the fastest interval and {@link #getInterval}. * - * <p>The system will never provide location updates faster - * than the minimum of {@link #getFastestInterval} and - * {@link #getInterval}. - * - * @return fastest interval in milliseconds, exact + * @return fastest interval in milliseconds */ public long getFastestInterval() { return mFastestInterval; } /** - * Set the duration of this request, in milliseconds. - * - * <p>The duration begins immediately (and not when the request - * is passed to the location manager), so call this method again - * if the request is re-used at a later time. - * - * <p>The location manager will automatically stop updates after - * the request expires. - * - * <p>The duration includes suspend time. Values less than 0 - * are allowed, but indicate that the request has already expired. + * Set the expiration time of this request in milliseconds of realtime since boot. Values in the + * past are allowed, but indicate that the request has already expired. The location manager + * will automatically stop updates after the request expires. * - * @param millis duration of request in milliseconds + * @param millis expiration time of request in milliseconds since boot * @return the same object, so that setters can be chained + * @see SystemClock#elapsedRealtime() + * @deprecated Prefer {@link #setExpireIn(long)}. */ - public @NonNull LocationRequest setExpireIn(long millis) { - long elapsedRealtime = SystemClock.elapsedRealtime(); - - // Check for > Long.MAX_VALUE overflow (elapsedRealtime > 0): - if (millis > Long.MAX_VALUE - elapsedRealtime) { - mExpireAt = Long.MAX_VALUE; - } else { - mExpireAt = millis + elapsedRealtime; - } - - if (mExpireAt < 0) mExpireAt = 0; + @Deprecated + public @NonNull LocationRequest setExpireAt(long millis) { + mExpireAt = Math.max(millis, 0); return this; } /** - * Set the request expiration time, in millisecond since boot. - * - * <p>This expiration time uses the same time base as {@link SystemClock#elapsedRealtime}. - * - * <p>The location manager will automatically stop updates after - * the request expires. + * Get the request expiration time in milliseconds of realtime since boot. * - * <p>The duration includes suspend time. Values before {@link SystemClock#elapsedRealtime} - * are allowed, but indicate that the request has already expired. + * @return request expiration time in milliseconds since boot + * @see SystemClock#elapsedRealtime() + * @deprecated Prefer {@link #getExpireIn()}. + */ + @Deprecated + public long getExpireAt() { + return mExpireAt; + } + + /** + * Set the duration of this request in milliseconds of realtime. Values less than 0 are allowed, + * but indicate that the request has already expired. The location manager will automatically + * stop updates after the request expires. * - * @param millis expiration time of request, in milliseconds since boot including suspend + * @param millis duration of request in milliseconds * @return the same object, so that setters can be chained + * @see SystemClock#elapsedRealtime() */ - public @NonNull LocationRequest setExpireAt(long millis) { - mExpireAt = millis; - if (mExpireAt < 0) mExpireAt = 0; + public @NonNull LocationRequest setExpireIn(long millis) { + mExpireIn = millis; return this; } /** - * Get the request expiration time, in milliseconds since boot. + * Get the request expiration duration in milliseconds of realtime. * - * <p>This value can be compared to {@link SystemClock#elapsedRealtime} to determine - * the time until expiration. - * - * @return expiration time of request, in milliseconds since boot including suspend + * @return request expiration duration in milliseconds + * @see SystemClock#elapsedRealtime() */ - public long getExpireAt() { - return mExpireAt; + public long getExpireIn() { + return mExpireIn; } /** @@ -638,13 +622,6 @@ public final class LocationRequest implements Parcelable { } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - private static void checkInterval(long millis) { - if (millis < 0) { - throw new IllegalArgumentException("invalid interval: " + millis); - } - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private static void checkQuality(int quality) { switch (quality) { case ACCURACY_FINE: @@ -682,6 +659,7 @@ public final class LocationRequest implements Parcelable { request.setFastestInterval(in.readLong()); request.setInterval(in.readLong()); request.setExpireAt(in.readLong()); + request.setExpireIn(in.readLong()); request.setNumUpdates(in.readInt()); request.setSmallestDisplacement(in.readFloat()); request.setHideFromAppOps(in.readInt() != 0); @@ -711,6 +689,7 @@ public final class LocationRequest implements Parcelable { parcel.writeLong(mFastestInterval); parcel.writeLong(mInterval); parcel.writeLong(mExpireAt); + parcel.writeLong(mExpireIn); parcel.writeInt(mNumUpdates); parcel.writeFloat(mSmallestDisplacement); parcel.writeInt(mHideFromAppOps ? 1 : 0); @@ -753,9 +732,11 @@ public final class LocationRequest implements Parcelable { s.append(" fastest="); TimeUtils.formatDuration(mFastestInterval, s); if (mExpireAt != Long.MAX_VALUE) { - long expireIn = mExpireAt - SystemClock.elapsedRealtime(); + s.append(" expireAt=").append(TimeUtils.formatUptime(mExpireAt)); + } + if (mExpireIn != Long.MAX_VALUE) { s.append(" expireIn="); - TimeUtils.formatDuration(expireIn, s); + TimeUtils.formatDuration(mExpireIn, s); } if (mNumUpdates != Integer.MAX_VALUE) { s.append(" num=").append(mNumUpdates); diff --git a/location/lib/Android.bp b/location/lib/Android.bp index fe0f669508eb..cd45e8e6ffa6 100644 --- a/location/lib/Android.bp +++ b/location/lib/Android.bp @@ -17,10 +17,8 @@ java_sdk_library { name: "com.android.location.provider", srcs: ["java/**/*.java"], - api_srcs: [":framework-all-sources"], libs: [ "androidx.annotation_annotation", - "framework-all", ], api_packages: ["com.android.location.provider"], } diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index 33ddfa7849e1..d6a4ea7cb39f 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -39,6 +39,7 @@ import libcore.io.Streams; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.DataInput; import java.io.DataInputStream; import java.io.EOFException; @@ -68,6 +69,7 @@ import java.util.Set; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.zip.CRC32; /** * This is a class for reading and writing Exif tags in a JPEG file or a RAW image file. @@ -502,11 +504,22 @@ public class ExifInterface { // 3.7. eXIf Exchangeable Image File (Exif) Profile private static final byte[] PNG_CHUNK_TYPE_EXIF = new byte[]{(byte) 0x65, (byte) 0x58, (byte) 0x49, (byte) 0x66}; + private static final byte[] PNG_CHUNK_TYPE_IHDR = new byte[]{(byte) 0x49, (byte) 0x48, + (byte) 0x44, (byte) 0x52}; private static final byte[] PNG_CHUNK_TYPE_IEND = new byte[]{(byte) 0x49, (byte) 0x45, (byte) 0x4e, (byte) 0x44}; - private static final int PNG_CHUNK_LENGTH_BYTE_LENGTH = 4; + private static final int PNG_CHUNK_TYPE_BYTE_LENGTH = 4; private static final int PNG_CHUNK_CRC_BYTE_LENGTH = 4; + // See https://developers.google.com/speed/webp/docs/riff_container, Section "WebP File Header" + private static final byte[] WEBP_SIGNATURE_1 = new byte[] {'R', 'I', 'F', 'F'}; + private static final byte[] WEBP_SIGNATURE_2 = new byte[] {'W', 'E', 'B', 'P'}; + private static final int WEBP_FILE_SIZE_BYTE_LENGTH = 4; + private static final byte[] WEBP_CHUNK_TYPE_EXIF = new byte[]{(byte) 0x45, (byte) 0x58, + (byte) 0x49, (byte) 0x46}; + private static final int WEBP_CHUNK_TYPE_BYTE_LENGTH = 4; + private static final int WEBP_CHUNK_SIZE_BYTE_LENGTH = 4; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private static SimpleDateFormat sFormatter; private static SimpleDateFormat sFormatterTz; @@ -1325,6 +1338,7 @@ public class ExifInterface { private static final int IMAGE_TYPE_SRW = 11; private static final int IMAGE_TYPE_HEIF = 12; private static final int IMAGE_TYPE_PNG = 13; + private static final int IMAGE_TYPE_WEBP = 14; static { sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss"); @@ -1869,6 +1883,10 @@ public class ExifInterface { getPngAttributes(inputStream); break; } + case IMAGE_TYPE_WEBP: { + getWebpAttributes(inputStream); + break; + } case IMAGE_TYPE_ARW: case IMAGE_TYPE_CR2: case IMAGE_TYPE_DNG: @@ -1934,7 +1952,7 @@ public class ExifInterface { * {@link #setAttribute(String,String)} to set all attributes to write and * make a single call rather than multiple calls for each attribute. * <p> - * This method is only supported for JPEG files. + * This method is only supported for JPEG and PNG files. * <p class="note"> * Note: after calling this method, any attempts to obtain range information * from {@link #getAttributeRange(String)} or {@link #getThumbnailRange()} @@ -1943,8 +1961,9 @@ public class ExifInterface { * </p> */ public void saveAttributes() throws IOException { - if (!mIsSupportedFile || mMimeType != IMAGE_TYPE_JPEG) { - throw new IOException("ExifInterface only supports saving attributes on JPEG formats."); + if (!mIsSupportedFile || (mMimeType != IMAGE_TYPE_JPEG && mMimeType != IMAGE_TYPE_PNG)) { + throw new IOException("ExifInterface only supports saving attributes on JPEG or PNG " + + "formats."); } if (mIsInputStream || (mSeekableFileDescriptor == null && mFilename == null)) { throw new IOException( @@ -1973,7 +1992,11 @@ public class ExifInterface { throw new IOException("Couldn't rename to " + tempFile.getAbsolutePath()); } } else if (mSeekableFileDescriptor != null) { - tempFile = File.createTempFile("temp", "jpg"); + if (mMimeType == IMAGE_TYPE_JPEG) { + tempFile = File.createTempFile("temp", "jpg"); + } else if (mMimeType == IMAGE_TYPE_PNG) { + tempFile = File.createTempFile("temp", "png"); + } Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET); in = new FileInputStream(mSeekableFileDescriptor); out = new FileOutputStream(tempFile); @@ -1999,7 +2022,11 @@ public class ExifInterface { } try (BufferedInputStream bufferedIn = new BufferedInputStream(in); BufferedOutputStream bufferedOut = new BufferedOutputStream(out)) { - saveJpegAttributes(bufferedIn, bufferedOut); + if (mMimeType == IMAGE_TYPE_JPEG) { + saveJpegAttributes(bufferedIn, bufferedOut); + } else if (mMimeType == IMAGE_TYPE_PNG) { + savePngAttributes(bufferedIn, bufferedOut); + } } } catch (Exception e) { if (mFilename != null) { @@ -2431,6 +2458,8 @@ public class ExifInterface { return IMAGE_TYPE_RW2; } else if (isPngFormat(signatureCheckBytes)) { return IMAGE_TYPE_PNG; + } else if (isWebpFormat(signatureCheckBytes)) { + return IMAGE_TYPE_WEBP; } // Certain file formats (PEF) are identified in readImageFileDirectory() return IMAGE_TYPE_UNKNOWN; @@ -2609,6 +2638,26 @@ public class ExifInterface { return true; } + /** + * WebP's file signature is composed of 12 bytes: + * 'RIFF' (4 bytes) + file length value (4 bytes) + 'WEBP' (4 bytes) + * See https://developers.google.com/speed/webp/docs/riff_container, Section "WebP File Header" + */ + private boolean isWebpFormat(byte[] signatureCheckBytes) throws IOException { + for (int i = 0; i < WEBP_SIGNATURE_1.length; i++) { + if (signatureCheckBytes[i] != WEBP_SIGNATURE_1[i]) { + return false; + } + } + for (int i = 0; i < WEBP_SIGNATURE_2.length; i++) { + if (signatureCheckBytes[i + WEBP_SIGNATURE_1.length + WEBP_FILE_SIZE_BYTE_LENGTH] + != WEBP_SIGNATURE_2[i]) { + return false; + } + } + return true; + } + private static boolean isStandalone(InputStream inputStream) throws IOException { byte[] signatureCheckBytes = new byte[IDENTIFIER_EXIF_APP1.length]; inputStream.read(signatureCheckBytes); @@ -3146,30 +3195,36 @@ public class ExifInterface { int bytesRead = 0; // Skip the signature bytes - in.seek(PNG_SIGNATURE.length); + in.skipBytes(PNG_SIGNATURE.length); bytesRead += PNG_SIGNATURE.length; + // Each chunk is made up of four parts: + // 1) Length: 4-byte unsigned integer indicating the number of bytes in the + // Chunk Data field. Excludes Chunk Type and CRC bytes. + // 2) Chunk Type: 4-byte chunk type code. + // 3) Chunk Data: The data bytes. Can be zero-length. + // 4) CRC: 4-byte data calculated on the preceding bytes in the chunk. Always + // present. + // --> 4 (length bytes) + 4 (type bytes) + X (data bytes) + 4 (CRC bytes) + // See PNG (Portable Network Graphics) Specification, Version 1.2, + // 3.2. Chunk layout try { while (true) { - // Each chunk is made up of four parts: - // 1) Length: 4-byte unsigned integer indicating the number of bytes in the - // Chunk Data field. Excludes Chunk Type and CRC bytes. - // 2) Chunk Type: 4-byte chunk type code. - // 3) Chunk Data: The data bytes. Can be zero-length. - // 4) CRC: 4-byte data calculated on the preceding bytes in the chunk. Always - // present. - // --> 4 (length bytes) + 4 (type bytes) + X (data bytes) + 4 (CRC bytes) - // See PNG (Portable Network Graphics) Specification, Version 1.2, - // 3.2. Chunk layout int length = in.readInt(); bytesRead += 4; - byte[] type = new byte[PNG_CHUNK_LENGTH_BYTE_LENGTH]; + byte[] type = new byte[PNG_CHUNK_TYPE_BYTE_LENGTH]; if (in.read(type) != type.length) { throw new IOException("Encountered invalid length while parsing PNG chunk" + "type"); } - bytesRead += PNG_CHUNK_LENGTH_BYTE_LENGTH; + bytesRead += PNG_CHUNK_TYPE_BYTE_LENGTH; + + // The first chunk must be the IHDR chunk + if (bytesRead == 16 && !Arrays.equals(type, PNG_CHUNK_TYPE_IHDR)) { + throw new IOException("Encountered invalid PNG file--IHDR chunk should appear" + + "as the first chunk"); + } if (Arrays.equals(type, PNG_CHUNK_TYPE_IEND)) { // IEND marks the end of the image. @@ -3181,9 +3236,25 @@ public class ExifInterface { throw new IOException("Failed to read given length for given PNG chunk " + "type: " + byteArrayToHexString(type)); } + + // Compare CRC values for potential data corruption. + int dataCrcValue = in.readInt(); + // Cyclic Redundancy Code used to check for corruption of the data + CRC32 crc = new CRC32(); + crc.update(type); + crc.update(data); + if ((int) crc.getValue() != dataCrcValue) { + throw new IOException("Encountered invalid CRC value for PNG-EXIF chunk." + + "\n recorded CRC value: " + dataCrcValue + ", calculated CRC " + + "value: " + crc.getValue()); + } + readExifSegment(data, IFD_TYPE_PRIMARY); validateImages(); + + // Save offset values for handleThumbnailFromJfif() function + mExifOffset = bytesRead; break; } else { // Skip to next chunk @@ -3191,8 +3262,6 @@ public class ExifInterface { bytesRead += length + PNG_CHUNK_CRC_BYTE_LENGTH; } } - // Save offset values for handleThumbnailFromJfif() function - mExifOffset = bytesRead; } catch (EOFException e) { // Should not reach here. Will only reach here if the file is corrupted or // does not follow the PNG specifications @@ -3200,6 +3269,75 @@ public class ExifInterface { } } + // WebP contains EXIF data as a RIFF File Format Chunk + // All references below can be found in the following link. + // https://developers.google.com/speed/webp/docs/riff_container + private void getWebpAttributes(ByteOrderedDataInputStream in) throws IOException { + if (DEBUG) { + Log.d(TAG, "getWebpAttributes starting with: " + in); + } + // WebP uses little-endian by default. + // See Section "Terminology & Basics" + in.setByteOrder(ByteOrder.LITTLE_ENDIAN); + in.skipBytes(WEBP_SIGNATURE_1.length); + // File size corresponds to the size of the entire file from offset 8. + // See Section "WebP File Header" + int fileSize = in.readInt() + 8; + int bytesRead = 8; + bytesRead += in.skipBytes(WEBP_SIGNATURE_2.length); + try { + while (true) { + // Each chunk is made up of three parts: + // 1) Chunk FourCC: 4-byte concatenating four ASCII characters. + // 2) Chunk Size: 4-byte unsigned integer indicating the size of the chunk. + // Excludes Chunk FourCC and Chunk Size bytes. + // 3) Chunk Payload: data payload. A single padding byte ('0') is added if + // Chunk Size is odd. + // See Section "RIFF File Format" + byte[] code = new byte[WEBP_CHUNK_TYPE_BYTE_LENGTH]; + if (in.read(code) != code.length) { + throw new IOException("Encountered invalid length while parsing WebP chunk" + + "type"); + } + bytesRead += 4; + int chunkSize = in.readInt(); + bytesRead += 4; + if (Arrays.equals(WEBP_CHUNK_TYPE_EXIF, code)) { + // TODO: Need to handle potential OutOfMemoryError + byte[] payload = new byte[chunkSize]; + if (in.read(payload) != chunkSize) { + throw new IOException("Failed to read given length for given PNG chunk " + + "type: " + byteArrayToHexString(code)); + } + readExifSegment(payload, IFD_TYPE_PRIMARY); + break; + } else { + // Add a single padding byte at end if chunk size is odd + chunkSize = (chunkSize % 2 == 1) ? chunkSize + 1 : chunkSize; + // Check if skipping to next chunk is necessary + if (bytesRead + chunkSize == fileSize) { + // Reached end of file + break; + } else if (bytesRead + chunkSize > fileSize) { + throw new IOException("Encountered WebP file with invalid chunk size"); + } + // Skip to next chunk + int skipped = in.skipBytes(chunkSize); + if (skipped != chunkSize) { + throw new IOException("Encountered WebP file with invalid chunk size"); + } + bytesRead += skipped; + } + } + // Save offset values for handleThumbnailFromJfif() function + mExifOffset = bytesRead; + } catch (EOFException e) { + // Should not reach here. Will only reach here if the file is corrupted or + // does not follow the WebP specifications + throw new IOException("Encountered corrupt WebP file."); + } + } + // Stores a new JPEG image with EXIF attributes into a given output stream. private void saveJpegAttributes(InputStream inputStream, OutputStream outputStream) throws IOException { @@ -3298,6 +3436,62 @@ public class ExifInterface { } } + private void savePngAttributes(InputStream inputStream, OutputStream outputStream) + throws IOException { + if (DEBUG) { + Log.d(TAG, "savePngAttributes starting with (inputStream: " + inputStream + + ", outputStream: " + outputStream + ")"); + } + DataInputStream dataInputStream = new DataInputStream(inputStream); + ByteOrderedDataOutputStream dataOutputStream = + new ByteOrderedDataOutputStream(outputStream, ByteOrder.BIG_ENDIAN); + // Copy PNG signature bytes + copy(dataInputStream, dataOutputStream, PNG_SIGNATURE.length); + // EXIF chunk can appear anywhere between the first (IHDR) and last (IEND) chunks, except + // between IDAT chunks. + // Adhering to these rules, + // 1) if EXIF chunk did not exist in the original file, it will be stored right after the + // first chunk, + // 2) if EXIF chunk existed in the original file, it will be stored in the same location. + if (mExifOffset == 0) { + // Copy IHDR chunk bytes + int ihdrChunkLength = dataInputStream.readInt(); + dataOutputStream.writeInt(ihdrChunkLength); + copy(dataInputStream, dataOutputStream, PNG_CHUNK_TYPE_BYTE_LENGTH + + ihdrChunkLength + PNG_CHUNK_CRC_BYTE_LENGTH); + } else { + // Copy up until the point where EXIF chunk length information is stored. + int copyLength = mExifOffset - PNG_SIGNATURE.length + - 4 /* PNG EXIF chunk length bytes */ + - PNG_CHUNK_TYPE_BYTE_LENGTH; + copy(dataInputStream, dataOutputStream, copyLength); + // Skip to the start of the chunk after the EXIF chunk + int exifChunkLength = dataInputStream.readInt(); + dataInputStream.skipBytes(PNG_CHUNK_TYPE_BYTE_LENGTH + exifChunkLength + + PNG_CHUNK_CRC_BYTE_LENGTH); + } + // Write EXIF data + try (ByteArrayOutputStream exifByteArrayOutputStream = new ByteArrayOutputStream()) { + // A byte array is needed to calculate the CRC value of this chunk which requires + // the chunk type bytes and the chunk data bytes. + ByteOrderedDataOutputStream exifDataOutputStream = + new ByteOrderedDataOutputStream(exifByteArrayOutputStream, + ByteOrder.BIG_ENDIAN); + // Store Exif data in separate byte array + writeExifSegment(exifDataOutputStream, 0); + byte[] exifBytes = + ((ByteArrayOutputStream) exifDataOutputStream.mOutputStream).toByteArray(); + // Write EXIF chunk data + dataOutputStream.write(exifBytes); + // Write EXIF chunk CRC + CRC32 crc = new CRC32(); + crc.update(exifBytes, 4 /* skip length bytes */, exifBytes.length - 4); + dataOutputStream.writeInt((int) crc.getValue()); + } + // Copy the rest of the file + Streams.copy(dataInputStream, dataOutputStream); + } + // Reads the given EXIF byte area and save its tag data into attributes. private void readExifSegment(byte[] exifBytes, int imageType) throws IOException { ByteOrderedDataInputStream dataInputStream = @@ -3684,12 +3878,18 @@ public class ExifInterface { int thumbnailOffset = jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder); int thumbnailLength = jpegInterchangeFormatLengthAttribute.getIntValue(mExifByteOrder); - if (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_RAF - || mMimeType == IMAGE_TYPE_RW2 || mMimeType == IMAGE_TYPE_PNG) { - thumbnailOffset += mExifOffset; - } else if (mMimeType == IMAGE_TYPE_ORF) { - // Update offset value since RAF files have IFD data preceding MakerNote data. - thumbnailOffset += mOrfMakerNoteOffset; + switch (mMimeType) { + case IMAGE_TYPE_JPEG: + case IMAGE_TYPE_RAF: + case IMAGE_TYPE_RW2: + case IMAGE_TYPE_PNG: + case IMAGE_TYPE_WEBP: + thumbnailOffset += mExifOffset; + break; + case IMAGE_TYPE_ORF: + // Update offset value since RAF files have IFD data preceding MakerNote data. + thumbnailOffset += mOrfMakerNoteOffset; + break; } // The following code limits the size of thumbnail size not to overflow EXIF data area. thumbnailLength = Math.min(thumbnailLength, in.getLength() - thumbnailOffset); @@ -3991,7 +4191,7 @@ public class ExifInterface { } // Calculate IFD offsets. - int position = 8; + int position = 8; // 8 bytes are for TIFF headers for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) { if (!mAttributes[ifdType].isEmpty()) { ifdOffsets[ifdType] = position; @@ -4006,13 +4206,16 @@ public class ExifInterface { position += mThumbnailLength; } - // Calculate the total size - int totalSize = position + 8; // eight bytes is for header part. + int totalSize = position; + if (mMimeType == IMAGE_TYPE_JPEG) { + // Add 8 bytes for APP1 size and identifier data + totalSize += 8; + } if (DEBUG) { - Log.d(TAG, "totalSize length: " + totalSize); for (int i = 0; i < EXIF_TAGS.length; ++i) { - Log.d(TAG, String.format("index: %d, offsets: %d, tag count: %d, data sizes: %d", - i, ifdOffsets[i], mAttributes[i].size(), ifdDataSizes[i])); + Log.d(TAG, String.format("index: %d, offsets: %d, tag count: %d, data sizes: %d, " + + "total size: %d", i, ifdOffsets[i], mAttributes[i].size(), + ifdDataSizes[i], totalSize)); } } @@ -4030,9 +4233,17 @@ public class ExifInterface { ifdOffsets[IFD_TYPE_INTEROPERABILITY], mExifByteOrder)); } + if (mMimeType == IMAGE_TYPE_JPEG) { + // Write JPEG specific data (APP1 size, APP1 identifier) + dataOutputStream.writeUnsignedShort(totalSize); + dataOutputStream.write(IDENTIFIER_EXIF_APP1); + } else if (mMimeType == IMAGE_TYPE_PNG) { + // Write PNG specific data (chunk size, chunk type) + dataOutputStream.writeInt(totalSize); + dataOutputStream.write(PNG_CHUNK_TYPE_EXIF); + } + // Write TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1. - dataOutputStream.writeUnsignedShort(totalSize); - dataOutputStream.write(IDENTIFIER_EXIF_APP1); dataOutputStream.writeShort(mExifByteOrder == ByteOrder.BIG_ENDIAN ? BYTE_ALIGN_MM : BYTE_ALIGN_II); dataOutputStream.setByteOrder(mExifByteOrder); @@ -4108,7 +4319,7 @@ public class ExifInterface { * Determines the data format of EXIF entry value. * * @param entryValue The value to be determined. - * @return Returns two data formats gussed as a pair in integer. If there is no two candidate + * @return Returns two data formats guessed as a pair in integer. If there is no two candidate data formats for the given entry value, returns {@code -1} in the second of the pair. */ private static Pair<Integer, Integer> guessDataFormat(String entryValue) { @@ -4430,7 +4641,7 @@ public class ExifInterface { // An output stream to write EXIF data area, which can be written in either little or big endian // order. private static class ByteOrderedDataOutputStream extends FilterOutputStream { - private final OutputStream mOutputStream; + final OutputStream mOutputStream; private ByteOrder mByteOrder; public ByteOrderedDataOutputStream(OutputStream out, ByteOrder byteOrder) { @@ -4546,6 +4757,25 @@ public class ExifInterface { } /** + * Copies the given number of the bytes from {@code in} to {@code out}. Neither stream is + * closed. + */ + private static void copy(InputStream in, OutputStream out, int numBytes) throws IOException { + int remainder = numBytes; + byte[] buffer = new byte[8192]; + while (remainder > 0) { + int bytesToRead = Math.min(remainder, 8192); + int bytesRead = in.read(buffer, 0, bytesToRead); + if (bytesRead != bytesToRead) { + throw new IOException("Failed to copy the given amount of bytes from the input" + + "stream to the output stream."); + } + remainder -= bytesRead; + out.write(buffer, 0, bytesRead); + } + } + + /** * Convert given int[] to long[]. If long[] is given, just return it. * Return null for other types of input. */ diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java index f813d1b68419..7bc2b31b16fa 100644 --- a/media/java/android/media/ImageWriter.java +++ b/media/java/android/media/ImageWriter.java @@ -192,13 +192,15 @@ public class ImageWriter implements AutoCloseable { mMaxImages = maxImages; - if (format == ImageFormat.UNKNOWN) { - format = SurfaceUtils.getSurfaceFormat(surface); - } // Note that the underlying BufferQueue is working in synchronous mode // to avoid dropping any buffers. mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, format); + // nativeInit internally overrides UNKNOWN format. So does surface format query after + // nativeInit and before getEstimatedNativeAllocBytes(). + if (format == ImageFormat.UNKNOWN) { + format = SurfaceUtils.getSurfaceFormat(surface); + } // Estimate the native buffer allocation size and register it so it gets accounted for // during GC. Note that this doesn't include the buffers required by the buffer queue // itself and the buffers requested by the producer. diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java index cc5ddeb49813..5d2bdd756840 100644 --- a/media/java/android/media/MediaMetadataRetriever.java +++ b/media/java/android/media/MediaMetadataRetriever.java @@ -34,6 +34,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -62,7 +63,8 @@ public class MediaMetadataRetriever implements AutoCloseable { * method before the rest of the methods in this class. This method may be * time-consuming. * - * @param path The path of the input media file. + * @param path The path, or the URI (doesn't support streaming source currently) + * of the input media file. * @throws IllegalArgumentException If the path is invalid. */ public void setDataSource(String path) throws IllegalArgumentException { @@ -70,6 +72,15 @@ public class MediaMetadataRetriever implements AutoCloseable { throw new IllegalArgumentException("null path"); } + final Uri uri = Uri.parse(path); + final String scheme = uri.getScheme(); + if ("file".equals(scheme)) { + path = uri.getPath(); + } else if (scheme != null) { + setDataSource(path, new HashMap<String, String>()); + return; + } + try (FileInputStream is = new FileInputStream(path)) { FileDescriptor fd = is.getFD(); setDataSource(fd, 0, 0x7ffffffffffffffL); diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index bf7da23323a1..9723652b5bd3 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -337,9 +337,14 @@ public class MediaRecorder implements AudioRouting, /** * Audio source for capturing broadcast radio tuner output. + * Capturing the radio tuner output requires the + * {@link android.Manifest.permission#CAPTURE_AUDIO_OUTPUT} permission. + * This permission is reserved for use by system components and is not available to + * third-party applications. * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int RADIO_TUNER = 1998; /** diff --git a/media/java/android/media/Utils.java b/media/java/android/media/Utils.java index 5b62f16ad315..d942bb653127 100644 --- a/media/java/android/media/Utils.java +++ b/media/java/android/media/Utils.java @@ -16,8 +16,8 @@ package android.media; -import android.content.Context; import android.content.ContentResolver; +import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Environment; @@ -206,12 +206,13 @@ class Utils { } static Size parseSize(Object o, Size fallback) { + if (o == null) { + return fallback; + } try { return Size.parseSize((String) o); } catch (ClassCastException e) { } catch (NumberFormatException e) { - } catch (NullPointerException e) { - return fallback; } Log.w(TAG, "could not parse size '" + o + "'"); return fallback; @@ -226,14 +227,15 @@ class Utils { return Integer.parseInt(s); } catch (ClassCastException e) { } catch (NumberFormatException e) { - } catch (NullPointerException e) { - return fallback; } Log.w(TAG, "could not parse integer '" + o + "'"); return fallback; } static Range<Integer> parseIntRange(Object o, Range<Integer> fallback) { + if (o == null) { + return fallback; + } try { String s = (String)o; int ix = s.indexOf('-'); @@ -246,8 +248,6 @@ class Utils { return Range.create(value, value); } catch (ClassCastException e) { } catch (NumberFormatException e) { - } catch (NullPointerException e) { - return fallback; } catch (IllegalArgumentException e) { } Log.w(TAG, "could not parse integer range '" + o + "'"); @@ -255,6 +255,9 @@ class Utils { } static Range<Long> parseLongRange(Object o, Range<Long> fallback) { + if (o == null) { + return fallback; + } try { String s = (String)o; int ix = s.indexOf('-'); @@ -267,8 +270,6 @@ class Utils { return Range.create(value, value); } catch (ClassCastException e) { } catch (NumberFormatException e) { - } catch (NullPointerException e) { - return fallback; } catch (IllegalArgumentException e) { } Log.w(TAG, "could not parse long range '" + o + "'"); @@ -276,6 +277,9 @@ class Utils { } static Range<Rational> parseRationalRange(Object o, Range<Rational> fallback) { + if (o == null) { + return fallback; + } try { String s = (String)o; int ix = s.indexOf('-'); @@ -288,8 +292,6 @@ class Utils { return Range.create(value, value); } catch (ClassCastException e) { } catch (NumberFormatException e) { - } catch (NullPointerException e) { - return fallback; } catch (IllegalArgumentException e) { } Log.w(TAG, "could not parse rational range '" + o + "'"); @@ -297,6 +299,9 @@ class Utils { } static Pair<Size, Size> parseSizeRange(Object o) { + if (o == null) { + return null; + } try { String s = (String)o; int ix = s.indexOf('-'); @@ -309,8 +314,6 @@ class Utils { return Pair.create(value, value); } catch (ClassCastException e) { } catch (NumberFormatException e) { - } catch (NullPointerException e) { - return null; } catch (IllegalArgumentException e) { } Log.w(TAG, "could not parse size range '" + o + "'"); diff --git a/media/java/android/media/tv/OWNER b/media/java/android/media/tv/OWNER new file mode 100644 index 000000000000..64c0bb53e894 --- /dev/null +++ b/media/java/android/media/tv/OWNER @@ -0,0 +1,5 @@ +amyjojo@google.com +nchalko@google.com +shubang@google.com +quxiangfang@google.com + diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 0228dc94fb25..2257747c2c8b 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -16,6 +16,13 @@ package android.media.tv.tuner; +import android.annotation.IntDef; +import android.hardware.tv.tuner.V1_0.Constants; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; + /** * Tuner is used to interact with tuner devices. * @@ -25,11 +32,41 @@ public final class Tuner implements AutoCloseable { private static final String TAG = "MediaTvTuner"; private static final boolean DEBUG = false; + @Retention(RetentionPolicy.SOURCE) + @IntDef({FRONTEND_TYPE_UNDEFINED, FRONTEND_TYPE_ANALOG, FRONTEND_TYPE_ATSC, FRONTEND_TYPE_ATSC3, + FRONTEND_TYPE_DVBC, FRONTEND_TYPE_DVBS, FRONTEND_TYPE_DVBT, FRONTEND_TYPE_ISDBS, + FRONTEND_TYPE_ISDBS3, FRONTEND_TYPE_ISDBT}) + public @interface FrontendType {} + + public static final int FRONTEND_TYPE_UNDEFINED = Constants.FrontendType.UNDEFINED; + public static final int FRONTEND_TYPE_ANALOG = Constants.FrontendType.ANALOG; + public static final int FRONTEND_TYPE_ATSC = Constants.FrontendType.ATSC; + public static final int FRONTEND_TYPE_ATSC3 = Constants.FrontendType.ATSC3; + public static final int FRONTEND_TYPE_DVBC = Constants.FrontendType.DVBC; + public static final int FRONTEND_TYPE_DVBS = Constants.FrontendType.DVBS; + public static final int FRONTEND_TYPE_DVBT = Constants.FrontendType.DVBT; + public static final int FRONTEND_TYPE_ISDBS = Constants.FrontendType.ISDBS; + public static final int FRONTEND_TYPE_ISDBS3 = Constants.FrontendType.ISDBS3; + public static final int FRONTEND_TYPE_ISDBT = Constants.FrontendType.ISDBT; + + + @Retention(RetentionPolicy.SOURCE) + @IntDef({FRONTEND_EVENT_TYPE_LOCKED, FRONTEND_EVENT_TYPE_NO_SIGNAL, + FRONTEND_EVENT_TYPE_LOST_LOCK}) + public @interface FrontendEventType {} + + public static final int FRONTEND_EVENT_TYPE_LOCKED = Constants.FrontendEventType.LOCKED; + public static final int FRONTEND_EVENT_TYPE_NO_SIGNAL = Constants.FrontendEventType.NO_SIGNAL; + public static final int FRONTEND_EVENT_TYPE_LOST_LOCK = Constants.FrontendEventType.LOST_LOCK; + static { System.loadLibrary("media_tv_tuner"); nativeInit(); } + private FrontendCallback mFrontendCallback; + private List<Integer> mFrontendIds; + public Tuner() { nativeSetup(); } @@ -48,4 +85,48 @@ public final class Tuner implements AutoCloseable { * Native setup. */ private native void nativeSetup(); + + /** + * Native method to get all frontend IDs. + */ + private native List<Integer> nativeGetFrontendIds(); + + /** + * Native method to open frontend of the given ID. + */ + private native Frontend nativeOpenFrontendById(int id); + + + /** + * Frontend Callback. + */ + public interface FrontendCallback { + + /** + * Invoked when there is a frontend event. + */ + void onEvent(int frontendEventType); + } + + protected static class Frontend { + int mId; + private Frontend(int id) { + mId = id; + } + } + + private List<Integer> getFrontendIds() { + mFrontendIds = nativeGetFrontendIds(); + return mFrontendIds; + } + + private Frontend openFrontendById(int id) { + if (mFrontendIds == null) { + getFrontendIds(); + } + if (!mFrontendIds.contains(id)) { + return null; + } + return nativeOpenFrontendById(id); + } } diff --git a/media/jni/Android.bp b/media/jni/Android.bp index a596d89bdcce..2f53cbb24129 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -99,13 +99,14 @@ cc_library_shared { "android_media_Utils.cpp", ], + header_libs: [ + "libgui_headers", + ], + shared_libs: [ "liblog", - "libgui", - "libnativewindow", "libui", "libutils", - "android.hidl.token@1.0-utils", ], include_dirs: [ @@ -132,6 +133,7 @@ cc_library_shared { shared_libs: [ "android.hardware.tv.tuner@1.0", "libandroid_runtime", + "libhidlbase", "liblog", "libutils", ], diff --git a/media/jni/OWNERS b/media/jni/OWNERS index bb91d4b26ecc..f1b0237d9008 100644 --- a/media/jni/OWNERS +++ b/media/jni/OWNERS @@ -1,2 +1,5 @@ # extra for MTP related files per-file android_mtp_*.cpp=marcone@google.com,jsharkey@android.com,jameswei@google.com,rmojumder@google.com + +# extra for TV related files +per-file android_media_tv_*=nchalko@google.com,quxiangfang@google.com diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index d499eee5a9da..ba133fc44fb7 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -25,10 +25,13 @@ #pragma GCC diagnostic ignored "-Wunused-function" +using ::android::hardware::hidl_vec; using ::android::hardware::tv::tuner::V1_0::ITuner; +using ::android::hardware::tv::tuner::V1_0::Result; struct fields_t { jfieldID context; + jmethodID frontendInitID; }; static fields_t gFields; @@ -69,6 +72,50 @@ sp<ITuner> JTuner::getTunerService() { return mTuner; } +jobject JTuner::getFrontendIds() { + ALOGD("JTuner::getFrontendIds()"); + hidl_vec<FrontendId> feIds; + mTuner->getFrontendIds([&](Result, const hidl_vec<FrontendId>& frontendIds) { + feIds = frontendIds; + }); + if (feIds.size() == 0) { + ALOGW("Frontend isn't available"); + return NULL; + } + + JNIEnv *env = AndroidRuntime::getJNIEnv(); + jclass arrayListClazz = env->FindClass("java/util/ArrayList"); + jmethodID arrayListAdd = env->GetMethodID(arrayListClazz, "add", "(Ljava/lang/Object;)Z"); + jobject obj = env->NewObject(arrayListClazz, env->GetMethodID(arrayListClazz, "<init>", "()V")); + + jclass integerClazz = env->FindClass("java/lang/Integer"); + jmethodID intInit = env->GetMethodID(integerClazz, "<init>", "(I)V"); + + for (int i=0; i < feIds.size(); i++) { + jobject idObj = env->NewObject(integerClazz, intInit, feIds[i]); + env->CallBooleanMethod(obj, arrayListAdd, idObj); + } + return obj; +} + +jobject JTuner::openFrontendById(int id) { + mTuner->openFrontendById(id, [&](Result, const sp<IFrontend>& frontend) { + mFe = frontend; + }); + if (mFe == nullptr) { + ALOGE("Failed to open frontend"); + return NULL; + } + + jint jId = (jint) id; + JNIEnv *env = AndroidRuntime::getJNIEnv(); + // TODO: add more fields to frontend + return env->NewObject( + env->FindClass("android/media/tv/tuner/Tuner$Frontend"), + gFields.frontendInitID, + (jint) jId); +} + } // namespace android //////////////////////////////////////////////////////////////////////////////// @@ -99,6 +146,9 @@ static void android_media_tv_Tuner_native_init(JNIEnv *env) { gFields.context = env->GetFieldID(clazz, "mNativeContext", "J"); CHECK(gFields.context != NULL); + + jclass frontendClazz = env->FindClass("android/media/tv/tuner/Tuner$Frontend"); + gFields.frontendInitID = env->GetMethodID(frontendClazz, "<init>", "(I)V"); } static void android_media_tv_Tuner_native_setup(JNIEnv *env, jobject thiz) { @@ -106,9 +156,23 @@ static void android_media_tv_Tuner_native_setup(JNIEnv *env, jobject thiz) { setTuner(env,thiz, tuner); } +static jobject android_media_tv_Tuner_get_frontend_ids(JNIEnv *env, jobject thiz) { + sp<JTuner> tuner = getTuner(env, thiz); + return tuner->getFrontendIds(); +} + +static jobject android_media_tv_Tuner_open_frontend_by_id(JNIEnv *env, jobject thiz, jint id) { + sp<JTuner> tuner = getTuner(env, thiz); + return tuner->openFrontendById(id); +} + static const JNINativeMethod gMethods[] = { { "nativeInit", "()V", (void *)android_media_tv_Tuner_native_init }, { "nativeSetup", "()V", (void *)android_media_tv_Tuner_native_setup }, + { "nativeGetFrontendIds", "()Ljava/util/List;", + (void *)android_media_tv_Tuner_get_frontend_ids }, + { "nativeOpenFrontendById", "(I)Landroid/media/tv/tuner/Tuner$Frontend;", + (void *)android_media_tv_Tuner_open_frontend_by_id }, }; static int register_android_media_tv_Tuner(JNIEnv *env) { diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index e7d5924a7dbe..1425542aa648 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -22,6 +22,8 @@ #include "jni.h" +using ::android::hardware::tv::tuner::V1_0::FrontendId; +using ::android::hardware::tv::tuner::V1_0::IFrontend; using ::android::hardware::tv::tuner::V1_0::ITuner; namespace android { @@ -29,6 +31,8 @@ namespace android { struct JTuner : public RefBase { JTuner(JNIEnv *env, jobject thiz); sp<ITuner> getTunerService(); + jobject getFrontendIds(); + jobject openFrontendById(int id); protected: virtual ~JTuner(); @@ -36,6 +40,7 @@ private: jclass mClass; jweak mObject; static sp<ITuner> mTuner; + sp<IFrontend> mFe; }; } // namespace android diff --git a/media/jni/audioeffect/Visualizer.h b/media/jni/audioeffect/Visualizer.h index 8078e369ee82..d4672a95c6d8 100644 --- a/media/jni/audioeffect/Visualizer.h +++ b/media/jni/audioeffect/Visualizer.h @@ -73,7 +73,8 @@ public: ~Visualizer(); - virtual status_t setEnabled(bool enabled); + // Declared 'final' because we call this in ~Visualizer(). + status_t setEnabled(bool enabled) final; // maximum capture size in samples static uint32_t getMaxCaptureSize() { return VISUALIZER_CAPTURE_SIZE_MAX; } diff --git a/media/lib/signer/Android.bp b/media/lib/signer/Android.bp index 6b03e4de57d7..3b2578754087 100644 --- a/media/lib/signer/Android.bp +++ b/media/lib/signer/Android.bp @@ -17,7 +17,5 @@ java_sdk_library { name: "com.android.mediadrm.signer", srcs: ["java/**/*.java"], - api_srcs: [":framework-all-sources"], - libs: ["framework-all"], api_packages: ["com.android.mediadrm.signer"], } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java index 74bf1a20304a..de353bf58aec 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java @@ -42,6 +42,7 @@ import org.mockito.MockitoAnnotations; public class MediaInserterTest extends InstrumentationTestCase { + private static final String TEST_FEATURE_ID = "testFeature"; private MediaInserter mMediaInserter; private static final int TEST_BUFFER_SIZE = 10; private @Mock IContentProvider mMockProvider; @@ -86,7 +87,8 @@ public class MediaInserterTest extends InstrumentationTestCase { MockitoAnnotations.initMocks(this); final ContentProviderClient client = new ContentProviderClient( - getInstrumentation().getContext().getContentResolver(), mMockProvider, true); + getInstrumentation().getContext().createFeatureContext(TEST_FEATURE_ID) + .getContentResolver(), mMockProvider, true); mMediaInserter = new MediaInserter(client, TEST_BUFFER_SIZE); mPackageName = getInstrumentation().getContext().getPackageName(); mFilesCounter = 0; @@ -142,31 +144,36 @@ public class MediaInserterTest extends InstrumentationTestCase { fillBuffer(sVideoUri, TEST_BUFFER_SIZE - 2); fillBuffer(sImagesUri, TEST_BUFFER_SIZE - 1); - verify(mMockProvider, never()).bulkInsert(eq(mPackageName), any(), any()); + verify(mMockProvider, never()).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(), + any()); } @SmallTest public void testInsertContentsEqualToBufferSize() throws Exception { - when(mMockProvider.bulkInsert(eq(mPackageName), any(), any())).thenReturn(1); + when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(), + any())).thenReturn(1); fillBuffer(sFilesUri, TEST_BUFFER_SIZE); fillBuffer(sAudioUri, TEST_BUFFER_SIZE); fillBuffer(sVideoUri, TEST_BUFFER_SIZE); fillBuffer(sImagesUri, TEST_BUFFER_SIZE); - verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), any(), any()); + verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(), + any()); } @SmallTest public void testInsertContentsMoreThanBufferSize() throws Exception { - when(mMockProvider.bulkInsert(eq(mPackageName), any(), any())).thenReturn(1); + when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(), + any())).thenReturn(1); fillBuffer(sFilesUri, TEST_BUFFER_SIZE + 1); fillBuffer(sAudioUri, TEST_BUFFER_SIZE + 2); fillBuffer(sVideoUri, TEST_BUFFER_SIZE + 3); fillBuffer(sImagesUri, TEST_BUFFER_SIZE + 4); - verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), any(), any()); + verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(), + any()); } @SmallTest @@ -176,7 +183,8 @@ public class MediaInserterTest extends InstrumentationTestCase { @SmallTest public void testFlushAllWithSomeContents() throws Exception { - when(mMockProvider.bulkInsert(eq(mPackageName), any(), any())).thenReturn(1); + when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(), + any())).thenReturn(1); fillBuffer(sFilesUri, TEST_BUFFER_SIZE - 4); fillBuffer(sAudioUri, TEST_BUFFER_SIZE - 3); @@ -184,12 +192,14 @@ public class MediaInserterTest extends InstrumentationTestCase { fillBuffer(sImagesUri, TEST_BUFFER_SIZE - 1); mMediaInserter.flushAll(); - verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), any(), any()); + verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(), + any()); } @SmallTest public void testInsertContentsAfterFlushAll() throws Exception { - when(mMockProvider.bulkInsert(eq(mPackageName), any(), any())).thenReturn(1); + when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(), + any())).thenReturn(1); fillBuffer(sFilesUri, TEST_BUFFER_SIZE - 4); fillBuffer(sAudioUri, TEST_BUFFER_SIZE - 3); @@ -202,15 +212,20 @@ public class MediaInserterTest extends InstrumentationTestCase { fillBuffer(sVideoUri, TEST_BUFFER_SIZE + 3); fillBuffer(sImagesUri, TEST_BUFFER_SIZE + 4); - verify(mMockProvider, times(8)).bulkInsert(eq(mPackageName), any(), any()); + verify(mMockProvider, times(8)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(), + any()); } @SmallTest public void testInsertContentsWithDifferentSizePerContentType() throws Exception { - when(mMockProvider.bulkInsert(eq(mPackageName), eqUri(sFilesUri), any())).thenReturn(1); - when(mMockProvider.bulkInsert(eq(mPackageName), eqUri(sAudioUri), any())).thenReturn(1); - when(mMockProvider.bulkInsert(eq(mPackageName), eqUri(sVideoUri), any())).thenReturn(1); - when(mMockProvider.bulkInsert(eq(mPackageName), eqUri(sImagesUri), any())).thenReturn(1); + when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sFilesUri), + any())).thenReturn(1); + when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sAudioUri), + any())).thenReturn(1); + when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sVideoUri), + any())).thenReturn(1); + when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sImagesUri), + any())).thenReturn(1); for (int i = 0; i < TEST_BUFFER_SIZE; ++i) { fillBuffer(sFilesUri, 1); @@ -219,9 +234,13 @@ public class MediaInserterTest extends InstrumentationTestCase { fillBuffer(sImagesUri, 4); } - verify(mMockProvider, times(1)).bulkInsert(eq(mPackageName), eqUri(sFilesUri), any()); - verify(mMockProvider, times(2)).bulkInsert(eq(mPackageName), eqUri(sAudioUri), any()); - verify(mMockProvider, times(3)).bulkInsert(eq(mPackageName), eqUri(sVideoUri), any()); - verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eqUri(sImagesUri), any()); + verify(mMockProvider, times(1)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), + eqUri(sFilesUri), any()); + verify(mMockProvider, times(2)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), + eqUri(sAudioUri), any()); + verify(mMockProvider, times(3)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), + eqUri(sVideoUri), any()); + verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), + eqUri(sImagesUri), any()); } } diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp index 9791da63359b..45f42f1b5dc6 100644 --- a/native/android/system_fonts.cpp +++ b/native/android/system_fonts.cpp @@ -16,6 +16,8 @@ #include <jni.h> +#define LOG_TAG "SystemFont" + #include <android/font.h> #include <android/font_matcher.h> #include <android/system_fonts.h> @@ -47,9 +49,14 @@ struct XmlDocDeleter { using XmlCharUniquePtr = std::unique_ptr<xmlChar, XmlCharDeleter>; using XmlDocUniquePtr = std::unique_ptr<xmlDoc, XmlDocDeleter>; +struct ParserState { + xmlNode* mFontNode = nullptr; + XmlCharUniquePtr mLocale; +}; + struct ASystemFontIterator { XmlDocUniquePtr mXmlDoc; - xmlNode* mFontNode; + ParserState state; // The OEM customization XML. XmlDocUniquePtr mCustomizationXmlDoc; @@ -97,6 +104,7 @@ std::string xmlTrim(const std::string& in) { const xmlChar* FAMILY_TAG = BAD_CAST("family"); const xmlChar* FONT_TAG = BAD_CAST("font"); +const xmlChar* LOCALE_ATTR_NAME = BAD_CAST("lang"); xmlNode* firstElement(xmlNode* node, const xmlChar* tag) { for (xmlNode* child = node->children; child; child = child->next) { @@ -116,9 +124,9 @@ xmlNode* nextSibling(xmlNode* node, const xmlChar* tag) { return nullptr; } -void copyFont(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode, AFont* out, +void copyFont(const XmlDocUniquePtr& xmlDoc, const ParserState& state, AFont* out, const std::string& pathPrefix) { - const xmlChar* LOCALE_ATTR_NAME = BAD_CAST("lang"); + xmlNode* fontNode = state.mFontNode; XmlCharUniquePtr filePathStr( xmlNodeListGetString(xmlDoc.get(), fontNode->xmlChildrenNode, 1)); out->mFilePath = pathPrefix + xmlTrim( @@ -139,9 +147,10 @@ void copyFont(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode, AFont* out, out->mCollectionIndex = indexStr ? strtol(reinterpret_cast<const char*>(indexStr.get()), nullptr, 10) : 0; - XmlCharUniquePtr localeStr(xmlGetProp(xmlDoc->parent, LOCALE_ATTR_NAME)); out->mLocale.reset( - localeStr ? new std::string(reinterpret_cast<const char*>(localeStr.get())) : nullptr); + state.mLocale ? + new std::string(reinterpret_cast<const char*>(state.mLocale.get())) + : nullptr); const xmlChar* TAG_ATTR_NAME = BAD_CAST("tag"); const xmlChar* STYLEVALUE_ATTR_NAME = BAD_CAST("stylevalue"); @@ -178,25 +187,27 @@ bool isFontFileAvailable(const std::string& filePath) { return S_ISREG(st.st_mode); } -xmlNode* findFirstFontNode(const XmlDocUniquePtr& doc) { +bool findFirstFontNode(const XmlDocUniquePtr& doc, ParserState* state) { xmlNode* familySet = xmlDocGetRootElement(doc.get()); if (familySet == nullptr) { - return nullptr; + return false; } xmlNode* family = firstElement(familySet, FAMILY_TAG); if (family == nullptr) { - return nullptr; + return false; } + state->mLocale.reset(xmlGetProp(family, LOCALE_ATTR_NAME)); xmlNode* font = firstElement(family, FONT_TAG); while (font == nullptr) { family = nextSibling(family, FAMILY_TAG); if (family == nullptr) { - return nullptr; + return false; } font = firstElement(family, FONT_TAG); } - return font; + state->mFontNode = font; + return font != nullptr; } } // namespace @@ -272,38 +283,38 @@ AFont* _Nonnull AFontMatcher_match( return result.release(); } -xmlNode* findNextFontNode(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode) { - if (fontNode == nullptr) { +bool findNextFontNode(const XmlDocUniquePtr& xmlDoc, ParserState* state) { + if (state->mFontNode == nullptr) { if (!xmlDoc) { - return nullptr; // Already at the end. + return false; // Already at the end. } else { // First time to query font. - return findFirstFontNode(xmlDoc); + return findFirstFontNode(xmlDoc, state); } } else { - xmlNode* nextNode = nextSibling(fontNode, FONT_TAG); + xmlNode* nextNode = nextSibling(state->mFontNode, FONT_TAG); while (nextNode == nullptr) { - xmlNode* family = nextSibling(fontNode->parent, FAMILY_TAG); + xmlNode* family = nextSibling(state->mFontNode->parent, FAMILY_TAG); if (family == nullptr) { break; } + state->mLocale.reset(xmlGetProp(family, LOCALE_ATTR_NAME)); nextNode = firstElement(family, FONT_TAG); } - return nextNode; + state->mFontNode = nextNode; + return nextNode != nullptr; } } AFont* ASystemFontIterator_next(ASystemFontIterator* ite) { LOG_ALWAYS_FATAL_IF(ite == nullptr, "nullptr has passed as iterator argument"); if (ite->mXmlDoc) { - ite->mFontNode = findNextFontNode(ite->mXmlDoc, ite->mFontNode); - if (ite->mFontNode == nullptr) { + if (!findNextFontNode(ite->mXmlDoc, &ite->state)) { // Reached end of the XML file. Continue OEM customization. ite->mXmlDoc.reset(); - ite->mFontNode = nullptr; } else { std::unique_ptr<AFont> font = std::make_unique<AFont>(); - copyFont(ite->mXmlDoc, ite->mFontNode, font.get(), "/system/fonts/"); + copyFont(ite->mXmlDoc, ite->state, font.get(), "/system/fonts/"); if (!isFontFileAvailable(font->mFilePath)) { return ASystemFontIterator_next(ite); } @@ -312,15 +323,13 @@ AFont* ASystemFontIterator_next(ASystemFontIterator* ite) { } if (ite->mCustomizationXmlDoc) { // TODO: Filter only customizationType="new-named-family" - ite->mFontNode = findNextFontNode(ite->mCustomizationXmlDoc, ite->mFontNode); - if (ite->mFontNode == nullptr) { + if (!findNextFontNode(ite->mCustomizationXmlDoc, &ite->state)) { // Reached end of the XML file. Finishing ite->mCustomizationXmlDoc.reset(); - ite->mFontNode = nullptr; return nullptr; } else { std::unique_ptr<AFont> font = std::make_unique<AFont>(); - copyFont(ite->mCustomizationXmlDoc, ite->mFontNode, font.get(), "/product/fonts/"); + copyFont(ite->mCustomizationXmlDoc, ite->state, font.get(), "/product/fonts/"); if (!isFontFileAvailable(font->mFilePath)) { return ASystemFontIterator_next(ite); } @@ -351,7 +360,7 @@ bool AFont_isItalic(const AFont* font) { const char* AFont_getLocale(const AFont* font) { LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument"); - return font->mLocale ? nullptr : font->mLocale->c_str(); + return font->mLocale ? font->mLocale->c_str() : nullptr; } size_t AFont_getCollectionIndex(const AFont* font) { diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp index 672879ae6e9d..b2451c91057c 100644 --- a/packages/CarSystemUI/Android.bp +++ b/packages/CarSystemUI/Android.bp @@ -63,6 +63,64 @@ android_library { } +android_library { + name: "CarSystemUI-tests", + manifest: "tests/AndroidManifest.xml", + resource_dirs: [ + "tests/res", + "res-keyguard", + "res", + ], + srcs: [ + "tests/src/**/*.java", + "src/**/*.java", + "src/**/I*.aidl", + ], + static_libs: [ + "SystemUI-tests", + "CarNotificationLib", + "SystemUIPluginLib", + "SystemUISharedLib", + "SettingsLib", + "android.car.userlib", + "androidx.legacy_legacy-support-v4", + "androidx.recyclerview_recyclerview", + "androidx.preference_preference", + "androidx.appcompat_appcompat", + "androidx.mediarouter_mediarouter", + "androidx.palette_palette", + "androidx.legacy_legacy-preference-v14", + "androidx.leanback_leanback", + "androidx.slice_slice-core", + "androidx.slice_slice-view", + "androidx.slice_slice-builders", + "androidx.arch.core_core-runtime", + "androidx.lifecycle_lifecycle-extensions", + "SystemUI-tags", + "SystemUI-proto", + "metrics-helper-lib", + "androidx.test.rules", "hamcrest-library", + "mockito-target-inline-minus-junit4", + "testables", + "truth-prebuilt", + "dagger2-2.19", + "//external/kotlinc:kotlin-annotations", + ], + libs: [ + "android.test.runner", + "telephony-common", + "android.test.base", + "android.car", + ], + + aaptflags: [ + "--extra-packages", + "com.android.systemui", + ], + + plugins: ["dagger2-compiler-2.19"], +} + android_app { name: "CarSystemUI", diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml index fe042fe2e17f..329225cf94fb 100644 --- a/packages/CarSystemUI/res/values/config.xml +++ b/packages/CarSystemUI/res/values/config.xml @@ -40,6 +40,21 @@ slots that may be reused for things like IME control. --> <integer name="config_maxNotificationIcons">0</integer> + <!-- + Initial alpha percent value for the background when the notification + shade is open. Should be a number between, and inclusive, 0 and 100. + If the number is 0, then the background alpha starts off fully + transparent. If the number if 100, then the background alpha starts off + fully opaque. --> + <integer name="config_initialNotificationBackgroundAlpha">0</integer> + <!-- + Final alpha percent value for the background when the notification + shade is fully open. Should be a number between, and inclusive, 0 and + 100. If this value is smaller than + config_initialNotificationBackgroundAlpha, the background will default + to a constant alpha percent value using the initial alpha. --> + <integer name="config_finalNotificationBackgroundAlpha">100</integer> + <!-- SystemUI Services: The classes of the stuff to start. --> <string-array name="config_systemUIServiceComponents" translatable="false"> <item>com.android.systemui.util.NotificationChannels</item> diff --git a/packages/CarSystemUI/src/com/android/systemui/CarComponentBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarComponentBinder.java new file mode 100644 index 000000000000..c40eda9fb303 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/CarComponentBinder.java @@ -0,0 +1,32 @@ +/* + * 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; + +import com.android.systemui.dagger.DefaultActivityBinder; +import com.android.systemui.dagger.DefaultServiceBinder; + +import dagger.Module; + +/** + * Supply Activities, Services, and SystemUI Objects for CarSystemUI. + */ +@Module(includes = { + DefaultActivityBinder.class, + DefaultServiceBinder.class, + CarSystemUIBinder.class}) +public class CarComponentBinder { +} diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java index 8e0a3eb53e6f..59b1068fb7b2 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java @@ -16,19 +16,293 @@ package com.android.systemui; +import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; + +import android.content.Context; +import android.os.PowerManager; +import android.util.DisplayMetrics; + +import com.android.internal.logging.MetricsLogger; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.appops.AppOpsController; +import com.android.systemui.assist.AssistManager; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.bubbles.BubbleController; +import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.doze.DozeLog; +import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.navigationbar.car.CarNavigationBar; +import com.android.systemui.navigationbar.car.CarNavigationBarController; +import com.android.systemui.pip.PipUI; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.power.PowerUI; +import com.android.systemui.recents.Recents; +import com.android.systemui.recents.RecentsModule; +import com.android.systemui.statusbar.FeatureFlags; +import com.android.systemui.statusbar.NavigationBarController; +import com.android.systemui.statusbar.NotificationListener; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationViewHierarchyManager; +import com.android.systemui.statusbar.PulseExpansionHandler; +import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.VibratorHelper; +import com.android.systemui.statusbar.car.CarStatusBar; +import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier; +import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.NewNotifPipeline; +import com.android.systemui.statusbar.notification.NotificationAlertingManager; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; +import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.logging.NotifLog; +import com.android.systemui.statusbar.notification.logging.NotificationLogger; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.phone.AutoHideController; +import com.android.systemui.statusbar.phone.BiometricUnlockController; +import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.statusbar.phone.DozeScrimController; +import com.android.systemui.statusbar.phone.DozeServiceHost; +import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; +import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.phone.LightBarController; +import com.android.systemui.statusbar.phone.LockscreenWallpaper; +import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper; +import com.android.systemui.statusbar.phone.NotificationGroupManager; +import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarIconController; +import com.android.systemui.statusbar.phone.StatusBarWindowController; +import com.android.systemui.statusbar.phone.StatusBarWindowViewController; +import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; +import com.android.systemui.statusbar.policy.UserSwitcherController; +import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.util.InjectionInflationController; +import com.android.systemui.util.leak.GarbageMonitor; +import com.android.systemui.volume.VolumeUI; + +import javax.inject.Named; +import javax.inject.Singleton; import dagger.Binds; +import dagger.Lazy; import dagger.Module; +import dagger.Provides; import dagger.multibindings.ClassKey; import dagger.multibindings.IntoMap; /** Binder for car specific {@link SystemUI} modules. */ -@Module +@Module(includes = {RecentsModule.class}) public abstract class CarSystemUIBinder { /** */ @Binds @IntoMap @ClassKey(CarNavigationBar.class) public abstract SystemUI bindCarNavigationBar(CarNavigationBar sysui); + + /** Inject into GarbageMonitor.Service. */ + @Binds + @IntoMap + @ClassKey(GarbageMonitor.Service.class) + public abstract SystemUI bindGarbageMonitorService(GarbageMonitor.Service service); + + /** Inject into KeyguardViewMediator. */ + @Binds + @IntoMap + @ClassKey(KeyguardViewMediator.class) + public abstract SystemUI bindKeyguardViewMediator(KeyguardViewMediator sysui); + + /** Inject into LatencyTests. */ + @Binds + @IntoMap + @ClassKey(LatencyTester.class) + public abstract SystemUI bindLatencyTester(LatencyTester sysui); + + /** Inject into PipUI. */ + @Binds + @IntoMap + @ClassKey(PipUI.class) + public abstract SystemUI bindPipUI(PipUI sysui); + + /** Inject into PowerUI. */ + @Binds + @IntoMap + @ClassKey(PowerUI.class) + public abstract SystemUI bindPowerUI(PowerUI sysui); + + /** Inject into Recents. */ + @Binds + @IntoMap + @ClassKey(Recents.class) + public abstract SystemUI bindRecents(Recents sysui); + + /** Inject into ScreenDecorations. */ + @Binds + @IntoMap + @ClassKey(ScreenDecorations.class) + public abstract SystemUI bindScreenDecorations(ScreenDecorations sysui); + + /** Inject into StatusBar. */ + @Binds + @IntoMap + @ClassKey(StatusBar.class) + public abstract SystemUI bindsStatusBar(CarStatusBar sysui); + + /** Inject into StatusBarGoogle. */ + @Binds + @IntoMap + @ClassKey(CarStatusBar.class) + public abstract SystemUI bindsCarStatusBar(CarStatusBar sysui); + + /** Inject into VolumeUI. */ + @Binds + @IntoMap + @ClassKey(VolumeUI.class) + public abstract SystemUI bindVolumeUI(VolumeUI sysui); + + /** + * Provides our instance of StatusBar which is considered optional. + */ + @Provides + @Singleton + static CarStatusBar provideStatusBar( + Context context, + FeatureFlags featureFlags, + LightBarController lightBarController, + AutoHideController autoHideController, + KeyguardUpdateMonitor keyguardUpdateMonitor, + StatusBarIconController statusBarIconController, + DozeLog dozeLog, + InjectionInflationController injectionInflationController, + PulseExpansionHandler pulseExpansionHandler, + NotificationWakeUpCoordinator notificationWakeUpCoordinator, + KeyguardBypassController keyguardBypassController, + KeyguardStateController keyguardStateController, + HeadsUpManagerPhone headsUpManagerPhone, + DynamicPrivacyController dynamicPrivacyController, + BypassHeadsUpNotifier bypassHeadsUpNotifier, + @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowNotificationLongPress, + Lazy<NewNotifPipeline> newNotifPipeline, + FalsingManager falsingManager, + BroadcastDispatcher broadcastDispatcher, + RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler, + NotificationGutsManager notificationGutsManager, + NotificationLogger notificationLogger, + NotificationEntryManager notificationEntryManager, + NotificationInterruptionStateProvider notificationInterruptionStateProvider, + NotificationViewHierarchyManager notificationViewHierarchyManager, + ForegroundServiceController foregroundServiceController, + AppOpsController appOpsController, + KeyguardViewMediator keyguardViewMediator, + ZenModeController zenModeController, + NotificationAlertingManager notificationAlertingManager, + DisplayMetrics displayMetrics, + MetricsLogger metricsLogger, + UiOffloadThread uiOffloadThread, + NotificationMediaManager notificationMediaManager, + NotificationLockscreenUserManager lockScreenUserManager, + NotificationRemoteInputManager remoteInputManager, + UserSwitcherController userSwitcherController, + NetworkController networkController, + BatteryController batteryController, + SysuiColorExtractor colorExtractor, + ScreenLifecycle screenLifecycle, + WakefulnessLifecycle wakefulnessLifecycle, + SysuiStatusBarStateController statusBarStateController, + VibratorHelper vibratorHelper, + BubbleController bubbleController, + NotificationGroupManager groupManager, + NotificationGroupAlertTransferHelper groupAlertTransferHelper, + VisualStabilityManager visualStabilityManager, + DeviceProvisionedController deviceProvisionedController, + NavigationBarController navigationBarController, + AssistManager assistManager, + NotificationListener notificationListener, + ConfigurationController configurationController, + StatusBarWindowController statusBarWindowController, + StatusBarWindowViewController.Builder statusBarWindowViewControllerBuilder, + NotifLog notifLog, + DozeParameters dozeParameters, + ScrimController scrimController, + Lazy<LockscreenWallpaper> lockscreenWallpaperLazy, + Lazy<BiometricUnlockController> biometricUnlockControllerLazy, + DozeServiceHost dozeServiceHost, + PowerManager powerManager, + DozeScrimController dozeScrimController, + CarNavigationBarController carNavigationBarController) { + return new CarStatusBar( + context, + featureFlags, + lightBarController, + autoHideController, + keyguardUpdateMonitor, + statusBarIconController, + dozeLog, + injectionInflationController, + pulseExpansionHandler, + notificationWakeUpCoordinator, + keyguardBypassController, + keyguardStateController, + headsUpManagerPhone, + dynamicPrivacyController, + bypassHeadsUpNotifier, + allowNotificationLongPress, + newNotifPipeline, + falsingManager, + broadcastDispatcher, + remoteInputQuickSettingsDisabler, + notificationGutsManager, + notificationLogger, + notificationEntryManager, + notificationInterruptionStateProvider, + notificationViewHierarchyManager, + foregroundServiceController, + appOpsController, + keyguardViewMediator, + zenModeController, + notificationAlertingManager, + displayMetrics, + metricsLogger, + uiOffloadThread, + notificationMediaManager, + lockScreenUserManager, + remoteInputManager, + userSwitcherController, + networkController, + batteryController, + colorExtractor, + screenLifecycle, + wakefulnessLifecycle, + statusBarStateController, + vibratorHelper, + bubbleController, + groupManager, + groupAlertTransferHelper, + visualStabilityManager, + deviceProvisionedController, + navigationBarController, + assistManager, + notificationListener, + configurationController, + statusBarWindowController, + statusBarWindowViewControllerBuilder, + notifLog, + dozeParameters, + scrimController, + lockscreenWallpaperLazy, + biometricUnlockControllerLazy, + dozeServiceHost, + powerManager, + dozeScrimController, + carNavigationBarController); + } } diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java index 93e553f59dcf..d3a6cde66e84 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java @@ -46,8 +46,6 @@ import javax.inject.Singleton; import dagger.Binds; import dagger.Module; import dagger.Provides; -import dagger.multibindings.ClassKey; -import dagger.multibindings.IntoMap; @Module abstract class CarSystemUIModule { @@ -105,11 +103,6 @@ abstract class CarSystemUIModule { public abstract StatusBar bindStatusBar(CarStatusBar statusBar); @Binds - @IntoMap - @ClassKey(StatusBar.class) - public abstract SystemUI providesStatusBar(CarStatusBar statusBar); - - @Binds abstract VolumeDialogComponent bindVolumeDialogComponent( CarVolumeDialogComponent carVolumeDialogComponent); } diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java index c2847c88785b..51b263ebdb4e 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java @@ -29,6 +29,7 @@ import dagger.Component; @Singleton @Component( modules = { + CarComponentBinder.class, DependencyProvider.class, DependencyBinder.class, SystemUIFactory.ContextHolder.class, diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java index 63bc66afddf8..98b91ebd8038 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java @@ -25,6 +25,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.view.Display; import android.view.Gravity; +import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; @@ -291,7 +292,8 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks } boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0; - mCarNavigationBarController.setBottomWindowVisibility(!isKeyboardVisible); + mCarNavigationBarController.setBottomWindowVisibility( + isKeyboardVisible ? View.GONE : View.VISIBLE); } private void updateNavBarForKeyguardContent() { diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java index f59f886d487b..6bed69bdee88 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java +++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java @@ -98,31 +98,30 @@ public class CarNavigationBarController { } /** Toggles the bottom nav bar visibility. */ - public boolean setBottomWindowVisibility(boolean isVisible) { - return setWindowVisibility(getBottomWindow(), isVisible); + public boolean setBottomWindowVisibility(@View.Visibility int visibility) { + return setWindowVisibility(getBottomWindow(), visibility); } /** Toggles the left nav bar visibility. */ - public boolean setLeftWindowVisibility(boolean isVisible) { - return setWindowVisibility(getLeftWindow(), isVisible); + public boolean setLeftWindowVisibility(@View.Visibility int visibility) { + return setWindowVisibility(getLeftWindow(), visibility); } /** Toggles the right nav bar visibility. */ - public boolean setRightWindowVisibility(boolean isVisible) { - return setWindowVisibility(getRightWindow(), isVisible); + public boolean setRightWindowVisibility(@View.Visibility int visibility) { + return setWindowVisibility(getRightWindow(), visibility); } - private boolean setWindowVisibility(ViewGroup window, boolean isVisible) { + private boolean setWindowVisibility(ViewGroup window, @View.Visibility int visibility) { if (window == null) { return false; } - int newVisibility = isVisible ? View.VISIBLE : View.GONE; - if (window.getVisibility() == newVisibility) { + if (window.getVisibility() == visibility) { return false; } - window.setVisibility(newVisibility); + window.setVisibility(visibility); return true; } @@ -228,6 +227,63 @@ public class CarNavigationBarController { } } + /** + * Shows all of the keyguard specific buttons on the valid instances of + * {@link CarNavigationBarView}. + */ + public void showAllKeyguardButtons(boolean isSetUp) { + checkAllBars(isSetUp); + if (mTopView != null) { + mTopView.showKeyguardButtons(); + } + if (mBottomView != null) { + mBottomView.showKeyguardButtons(); + } + if (mLeftView != null) { + mLeftView.showKeyguardButtons(); + } + if (mRightView != null) { + mRightView.showKeyguardButtons(); + } + } + + /** + * Hides all of the keyguard specific buttons on the valid instances of + * {@link CarNavigationBarView}. + */ + public void hideAllKeyguardButtons(boolean isSetUp) { + checkAllBars(isSetUp); + if (mTopView != null) { + mTopView.hideKeyguardButtons(); + } + if (mBottomView != null) { + mBottomView.hideKeyguardButtons(); + } + if (mLeftView != null) { + mLeftView.hideKeyguardButtons(); + } + if (mRightView != null) { + mRightView.hideKeyguardButtons(); + } + } + + /** Toggles whether the notifications icon has an unseen indicator or not. */ + public void toggleAllNotificationsUnseenIndicator(boolean isSetUp, boolean hasUnseen) { + checkAllBars(isSetUp); + if (mTopView != null) { + mTopView.toggleNotificationUnseenIndicator(hasUnseen); + } + if (mBottomView != null) { + mBottomView.toggleNotificationUnseenIndicator(hasUnseen); + } + if (mLeftView != null) { + mLeftView.toggleNotificationUnseenIndicator(hasUnseen); + } + if (mRightView != null) { + mRightView.toggleNotificationUnseenIndicator(hasUnseen); + } + } + /** Interface for controlling the notifications shade. */ public interface NotificationsShadeController { /** Toggles the visibility of the notifications shade. */ @@ -244,4 +300,11 @@ public class CarNavigationBarController { } } } + + private void checkAllBars(boolean isSetUp) { + mTopView = getTopBar(isSetUp); + mBottomView = getBottomBar(isSetUp); + mLeftView = getLeftBar(isSetUp); + mRightView = getRightBar(isSetUp); + } } diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java index 24f8b74eed61..c2455088a52b 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java +++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java @@ -24,6 +24,7 @@ import android.widget.LinearLayout; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.navigationbar.car.CarNavigationBarController.NotificationsShadeController; import com.android.systemui.statusbar.phone.StatusBarIconController; /** @@ -35,7 +36,7 @@ import com.android.systemui.statusbar.phone.StatusBarIconController; public class CarNavigationBarView extends LinearLayout { private View mNavButtons; private CarNavigationButton mNotificationsButton; - private CarNavigationBarController.NotificationsShadeController mNotificationsShadeController; + private NotificationsShadeController mNotificationsShadeController; private Context mContext; private View mLockScreenButtons; // used to wire in open/close gestures for notifications @@ -81,13 +82,18 @@ public class CarNavigationBarView extends LinearLayout { return super.onInterceptTouchEvent(ev); } - public void setNotificationsPanelController( - CarNavigationBarController.NotificationsShadeController controller) { + /** Sets the notifications panel controller. */ + public void setNotificationsPanelController(NotificationsShadeController controller) { mNotificationsShadeController = controller; } + /** Gets the notifications panel controller. */ + public NotificationsShadeController getNotificationsPanelController() { + return mNotificationsShadeController; + } + /** - * Set a touch listener that will be called from onInterceptTouchEvent and onTouchEvent + * Sets a touch listener that will be called from onInterceptTouchEvent and onTouchEvent * * @param statusBarWindowTouchListener The listener to call from touch and intercept touch */ @@ -95,6 +101,11 @@ public class CarNavigationBarView extends LinearLayout { mStatusBarWindowTouchListener = statusBarWindowTouchListener; } + /** Gets the touch listener that will be called from onInterceptTouchEvent and onTouchEvent. */ + public OnTouchListener getStatusBarWindowTouchListener() { + return mStatusBarWindowTouchListener; + } + @Override public boolean onTouchEvent(MotionEvent event) { if (mStatusBarWindowTouchListener != null) { diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java index 40823abaaead..922bfffcfa22 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java +++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java @@ -150,6 +150,11 @@ public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImag updateImage(); } + /** Gets whether the icon is in an unseen state. */ + public boolean getUnseen() { + return mHasUnseen; + } + private void updateImage() { if (mHasUnseen) { setImageResource(mSelected ? UNSEEN_SELECTED_ICON_RESOURCE_ID diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 9d3362ee8c65..85472ff5ac34 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -27,11 +27,11 @@ import android.car.Car; import android.car.drivingstate.CarDrivingStateEvent; import android.car.drivingstate.CarUxRestrictionsManager; import android.car.hardware.power.CarPowerManager.CarPowerStateListener; -import android.car.trust.CarTrustAgentEnrollmentManager; import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.PowerManager; import android.util.DisplayMetrics; import android.util.Log; import android.view.GestureDetector; @@ -107,6 +107,8 @@ import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment; import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.statusbar.phone.DozeScrimController; +import com.android.systemui.statusbar.phone.DozeServiceHost; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.LightBarController; @@ -132,7 +134,6 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Map; -import javax.inject.Inject; import javax.inject.Named; import dagger.Lazy; @@ -153,6 +154,9 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt private float mOpeningVelocity = DEFAULT_FLING_VELOCITY; private float mClosingVelocity = DEFAULT_FLING_VELOCITY; + private float mBackgroundAlphaDiff; + private float mInitialBackgroundAlpha; + private FullscreenUserSwitcher mFullscreenUserSwitcher; private CarBatteryController mCarBatteryController; @@ -160,13 +164,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt private Drawable mNotificationPanelBackground; private ViewGroup mTopNavigationBarContainer; - private ViewGroup mNavigationBarWindow; - private ViewGroup mLeftNavigationBarWindow; - private ViewGroup mRightNavigationBarWindow; private CarNavigationBarView mTopNavigationBarView; - private CarNavigationBarView mNavigationBarView; - private CarNavigationBarView mLeftNavigationBarView; - private CarNavigationBarView mRightNavigationBarView; private final Object mQueueLock = new Object(); private final CarNavigationBarController mCarNavigationBarController; @@ -219,6 +217,8 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt // Whether heads-up notifications should be shown when shade is open. private boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen; + private CarUxRestrictionManagerWrapper mCarUxRestrictionManagerWrapper; + private final CarPowerStateListener mCarPowerStateListener = (int state) -> { // When the car powers on, clear all notifications and mute/unread states. @@ -233,7 +233,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt } }; - @Inject public CarStatusBar( Context context, FeatureFlags featureFlags, @@ -295,6 +294,9 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt ScrimController scrimController, Lazy<LockscreenWallpaper> lockscreenWallpaperLazy, Lazy<BiometricUnlockController> biometricUnlockControllerLazy, + DozeServiceHost dozeServiceHost, + PowerManager powerManager, + DozeScrimController dozeScrimController, /* Car Settings injected components. */ CarNavigationBarController carNavigationBarController) { @@ -358,7 +360,10 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt dozeParameters, scrimController, lockscreenWallpaperLazy, - biometricUnlockControllerLazy); + biometricUnlockControllerLazy, + dozeServiceHost, + powerManager, + dozeScrimController); mScrimController = scrimController; mCarNavigationBarController = carNavigationBarController; } @@ -375,6 +380,25 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mScreenLifecycle = Dependency.get(ScreenLifecycle.class); mScreenLifecycle.addObserver(mScreenObserver); + // Notification bar related setup. + mInitialBackgroundAlpha = (float) mContext.getResources().getInteger( + R.integer.config_initialNotificationBackgroundAlpha) / 100; + if (mInitialBackgroundAlpha < 0 || mInitialBackgroundAlpha > 100) { + throw new RuntimeException( + "Unable to setup notification bar due to incorrect initial background alpha" + + " percentage"); + } + float finalBackgroundAlpha = Math.max( + mInitialBackgroundAlpha, + (float) mContext.getResources().getInteger( + R.integer.config_finalNotificationBackgroundAlpha) / 100); + if (finalBackgroundAlpha < 0 || finalBackgroundAlpha > 100) { + throw new RuntimeException( + "Unable to setup notification bar due to incorrect final background alpha" + + " percentage"); + } + mBackgroundAlphaDiff = finalBackgroundAlpha - mInitialBackgroundAlpha; + super.start(); mNotificationPanel.setScrollingEnabled(true); @@ -392,30 +416,32 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt new DeviceProvisionedController.DeviceProvisionedListener() { @Override public void onUserSetupChanged() { - mHandler.post(() -> restartNavBarsIfNecessary()); + mHandler.post(() -> resetSystemBarsIfNecessary()); } @Override public void onUserSwitched() { - mHandler.post(() -> restartNavBarsIfNecessary()); + mHandler.post(() -> resetSystemBarsIfNecessary()); } }); + // Used by onDrivingStateChanged and it can be called inside + // DrivingStateHelper.connectToCarService() + mSwitchToGuestTimer = new SwitchToGuestTimer(mContext); + // Register a listener for driving state changes. mDrivingStateHelper = new DrivingStateHelper(mContext, this::onDrivingStateChanged); mDrivingStateHelper.connectToCarService(); mPowerManagerHelper = new PowerManagerHelper(mContext, mCarPowerStateListener); mPowerManagerHelper.connectToCarService(); - - mSwitchToGuestTimer = new SwitchToGuestTimer(mContext); } - private void restartNavBarsIfNecessary() { + private void resetSystemBarsIfNecessary() { boolean currentUserSetup = mDeviceProvisionedController.isCurrentUserSetup(); if (mDeviceIsSetUpForUser != currentUserSetup) { mDeviceIsSetUpForUser = currentUserSetup; - restartNavBars(); + resetSystemBars(); } } @@ -423,19 +449,9 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt * Remove all content from navbars and rebuild them. Used to allow for different nav bars * before and after the device is provisioned. . Also for change of density and font size. */ - private void restartNavBars() { + private void resetSystemBars() { mCarFacetButtonController.removeAll(); - if (mNavigationBarWindow != null) { - mNavigationBarView = null; - } - if (mLeftNavigationBarWindow != null) { - mLeftNavigationBarView = null; - } - if (mRightNavigationBarWindow != null) { - mRightNavigationBarView = null; - } - buildNavBarContent(); // CarFacetButtonController was reset therefore we need to re-add the status bar elements // to the controller. @@ -447,51 +463,22 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt * the full screen user selector is shown. */ void setNavBarVisibility(@View.Visibility int visibility) { - if (mNavigationBarWindow != null) { - mNavigationBarWindow.setVisibility(visibility); - } - if (mLeftNavigationBarWindow != null) { - mLeftNavigationBarWindow.setVisibility(visibility); - } - if (mRightNavigationBarWindow != null) { - mRightNavigationBarWindow.setVisibility(visibility); - } + mCarNavigationBarController.setBottomWindowVisibility(visibility); + mCarNavigationBarController.setLeftWindowVisibility(visibility); + mCarNavigationBarController.setRightWindowVisibility(visibility); } @Override public boolean hideKeyguard() { boolean result = super.hideKeyguard(); - if (mNavigationBarView != null) { - mNavigationBarView.hideKeyguardButtons(); - } - if (mLeftNavigationBarView != null) { - mLeftNavigationBarView.hideKeyguardButtons(); - } - if (mRightNavigationBarView != null) { - mRightNavigationBarView.hideKeyguardButtons(); - } + mCarNavigationBarController.hideAllKeyguardButtons(mDeviceIsSetUpForUser); return result; } @Override public void showKeyguard() { super.showKeyguard(); - updateNavBarForKeyguardContent(); - } - - /** - * Switch to the keyguard applicable content contained in the nav bars - */ - private void updateNavBarForKeyguardContent() { - if (mNavigationBarView != null) { - mNavigationBarView.showKeyguardButtons(); - } - if (mLeftNavigationBarView != null) { - mLeftNavigationBarView.showKeyguardButtons(); - } - if (mRightNavigationBarView != null) { - mRightNavigationBarView.showKeyguardButtons(); - } + mCarNavigationBarController.showAllKeyguardButtons(mDeviceIsSetUpForUser); } @Override @@ -584,31 +571,31 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt animateCollapsePanels(); } }); - Car car = Car.createCar(mContext); - CarUxRestrictionsManager carUxRestrictionsManager = (CarUxRestrictionsManager) - car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE); CarNotificationListener carNotificationListener = new CarNotificationListener(); - CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper = - new CarUxRestrictionManagerWrapper(); - carUxRestrictionManagerWrapper.setCarUxRestrictionsManager(carUxRestrictionsManager); + mCarUxRestrictionManagerWrapper = new CarUxRestrictionManagerWrapper(); + // This can take time if car service is not ready up to this time. + // TODO(b/142808072) Refactor CarUxRestrictionManagerWrapper to allow setting + // CarUxRestrictionsManager later and switch to Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT. + Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, + (car, ready) -> { + if (!ready) { + return; + } + CarUxRestrictionsManager carUxRestrictionsManager = + (CarUxRestrictionsManager) + car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE); + mCarUxRestrictionManagerWrapper.setCarUxRestrictionsManager( + carUxRestrictionsManager); + }); mNotificationDataManager = new NotificationDataManager(); mNotificationDataManager.setOnUnseenCountUpdateListener( () -> { - if (mNavigationBarView != null && mNotificationDataManager != null) { - Boolean hasUnseen = + if (mNotificationDataManager != null) { + boolean hasUnseen = mNotificationDataManager.getUnseenNotificationCount() > 0; - if (mNavigationBarView != null) { - mNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen); - } - - if (mLeftNavigationBarView != null) { - mLeftNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen); - } - - if (mRightNavigationBarView != null) { - mRightNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen); - } + mCarNavigationBarController.toggleAllNotificationsUnseenIndicator( + mDeviceIsSetUpForUser, hasUnseen); } }); @@ -619,7 +606,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mNotificationClickHandlerFactory, mNotificationDataManager); mNotificationClickHandlerFactory.setNotificationDataManager(mNotificationDataManager); - carNotificationListener.registerAsSystemService(mContext, carUxRestrictionManagerWrapper, + carNotificationListener.registerAsSystemService(mContext, mCarUxRestrictionManagerWrapper, carHeadsUpNotificationManager, mNotificationDataManager); mNotificationView = mStatusBarWindow.findViewById(R.id.notification_view); @@ -719,7 +706,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mNotificationView, PreprocessingManager.getInstance(mContext), carNotificationListener, - carUxRestrictionManagerWrapper, + mCarUxRestrictionManagerWrapper, mNotificationDataManager); mNotificationViewController.enable(); } @@ -862,37 +849,27 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt @Override protected void createNavigationBar(@Nullable RegisterStatusBarResult result) { - buildNavBarWindows(); + mTopNavigationBarContainer = mStatusBarWindow + .findViewById(R.id.car_top_navigation_bar_container); + buildNavBarContent(); } private void buildNavBarContent() { buildTopBar(); - mNavigationBarView = mCarNavigationBarController.getBottomBar(mDeviceIsSetUpForUser); mCarNavigationBarController.registerBottomBarTouchListener( mNavBarNotificationTouchListener); - mLeftNavigationBarView = mCarNavigationBarController.getLeftBar(mDeviceIsSetUpForUser); mCarNavigationBarController.registerLeftBarTouchListener( mNavBarNotificationTouchListener); - mRightNavigationBarView = mCarNavigationBarController.getLeftBar(mDeviceIsSetUpForUser); mCarNavigationBarController.registerRightBarTouchListener( mNavBarNotificationTouchListener); mCarNavigationBarController.registerNotificationController(() -> togglePanel()); } - private void buildNavBarWindows() { - mTopNavigationBarContainer = mStatusBarWindow - .findViewById(R.id.car_top_navigation_bar_container); - - mNavigationBarWindow = mCarNavigationBarController.getBottomWindow(); - mLeftNavigationBarWindow = mCarNavigationBarController.getLeftWindow(); - mRightNavigationBarWindow = mCarNavigationBarController.getRightWindow(); - } - private void buildTopBar() { mTopNavigationBarContainer.removeAllViews(); mTopNavigationBarView = mCarNavigationBarController.getTopBar(mDeviceIsSetUpForUser); @@ -978,12 +955,8 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt UserSwitcherController userSwitcherController = Dependency.get(UserSwitcherController.class); if (userSwitcherController.useFullscreenUserSwitcher()) { - Car car = Car.createCar(mContext); - CarTrustAgentEnrollmentManager enrollmentManager = (CarTrustAgentEnrollmentManager) car - .getCarManager(Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE); mFullscreenUserSwitcher = new FullscreenUserSwitcher(this, - mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub), - enrollmentManager, mContext); + mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub), mContext); } else { super.createUserSwitcher(); } @@ -1066,7 +1039,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt @Override public void onDensityOrFontScaleChanged() { super.onDensityOrFontScaleChanged(); - restartNavBars(); + resetSystemBars(); // Need to update the background on density changed in case the change was due to night // mode. mNotificationPanelBackground = getDefaultWallpaper(); @@ -1094,17 +1067,22 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mHandleBar.setTranslationY(height - mHandleBar.getHeight() - lp.bottomMargin); } if (mNotificationView.getHeight() > 0) { - // Calculates the alpha value for the background based on how much of the notification - // shade is visible to the user. When the notification shade is completely open then - // alpha value will be 1. - float alpha = (float) height / mNotificationView.getHeight(); Drawable background = mNotificationView.getBackground().mutate(); - - background.setAlpha((int) (alpha * 255)); + background.setAlpha((int) (getBackgroundAlpha(height) * 255)); mNotificationView.setBackground(background); } } + /** + * Calculates the alpha value for the background based on how much of the notification + * shade is visible to the user. When the notification shade is completely open then + * alpha value will be 1. + */ + private float getBackgroundAlpha(int height) { + return mInitialBackgroundAlpha + + ((float) height / mNotificationView.getHeight() * mBackgroundAlphaDiff); + } + @Override public void onConfigChanged(Configuration newConfig) { super.onConfigChanged(newConfig); diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java index a4424260fef5..cd87e78e4be9 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java @@ -17,14 +17,11 @@ package com.android.systemui.statusbar.car; import android.car.Car; -import android.car.CarNotConnectedException; +import android.car.Car.CarServiceLifecycleListener; import android.car.drivingstate.CarDrivingStateEvent; import android.car.drivingstate.CarDrivingStateManager; import android.car.drivingstate.CarDrivingStateManager.CarDrivingStateEventListener; -import android.content.ComponentName; import android.content.Context; -import android.content.ServiceConnection; -import android.os.IBinder; import android.util.Log; import androidx.annotation.NonNull; @@ -55,16 +52,11 @@ public class DrivingStateHelper { if (mDrivingStateManager == null) { return false; } - try { - CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState(); - if (currentState != null) { - return currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_IDLING - || currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING; - } - } catch (CarNotConnectedException e) { - Log.e(TAG, "Cannot determine current driving state. Car not connected", e); + CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState(); + if (currentState != null) { + return currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_IDLING + || currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING; } - return false; // Default to false. } @@ -72,55 +64,25 @@ public class DrivingStateHelper { * Establishes connection with the Car service. */ public void connectToCarService() { - mCar = Car.createCar(mContext, mCarConnectionListener); - if (mCar != null) { - mCar.connect(); - } + mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT, + mCarServiceLifecycleListener); } - /** - * Disconnects from Car service and cleans up listeners. - */ - public void disconnectFromCarService() { - if (mCar != null) { - mCar.disconnect(); + private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> { + if (!ready) { + return; } - } - - private final ServiceConnection mCarConnectionListener = - new ServiceConnection() { - public void onServiceConnected(ComponentName name, IBinder service) { - logD("Car Service connected"); - try { - mDrivingStateManager = (CarDrivingStateManager) mCar.getCarManager( - Car.CAR_DRIVING_STATE_SERVICE); - if (mDrivingStateManager != null) { - mDrivingStateManager.registerListener(mDrivingStateHandler); - mDrivingStateHandler.onDrivingStateChanged( - mDrivingStateManager.getCurrentCarDrivingState()); - } else { - Log.e(TAG, "CarDrivingStateService service not available"); - } - } catch (CarNotConnectedException e) { - Log.e(TAG, "Car not connected", e); - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - destroyDrivingStateManager(); - } - }; - - private void destroyDrivingStateManager() { - try { - if (mDrivingStateManager != null) { - mDrivingStateManager.unregisterListener(); - } - } catch (CarNotConnectedException e) { - Log.e(TAG, "Error unregistering listeners", e); + logD("Car Service connected"); + mDrivingStateManager = (CarDrivingStateManager) car.getCarManager( + Car.CAR_DRIVING_STATE_SERVICE); + if (mDrivingStateManager != null) { + mDrivingStateManager.registerListener(mDrivingStateHandler); + mDrivingStateHandler.onDrivingStateChanged( + mDrivingStateManager.getCurrentCarDrivingState()); + } else { + Log.e(TAG, "CarDrivingStateService service not available"); } - } + }; private void logD(String message) { if (Log.isLoggable(TAG, Log.DEBUG)) { diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java index 0f7c1ee8ea7e..31aced02b15e 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.car; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.car.Car; import android.car.trust.CarTrustAgentEnrollmentManager; import android.car.userlib.CarUserManagerHelper; import android.content.BroadcastReceiver; @@ -50,7 +51,7 @@ public class FullscreenUserSwitcher { private final CarStatusBar mStatusBar; private final Context mContext; private final UserManager mUserManager; - private final CarTrustAgentEnrollmentManager mEnrollmentManager; + private CarTrustAgentEnrollmentManager mEnrollmentManager; private CarTrustAgentUnlockDialogHelper mUnlockDialogHelper; private UserGridRecyclerView.UserRecord mSelectedUser; private CarUserManagerHelper mCarUserManagerHelper; @@ -64,13 +65,11 @@ public class FullscreenUserSwitcher { mContext.unregisterReceiver(mUserUnlockReceiver); } }; + private final Car mCar; - - public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub, - CarTrustAgentEnrollmentManager enrollmentManager, Context context) { + public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub, Context context) { mStatusBar = statusBar; mParent = containerStub.inflate(); - mEnrollmentManager = enrollmentManager; mContext = context; View container = mParent.findViewById(R.id.container); @@ -86,6 +85,15 @@ public class FullscreenUserSwitcher { mUnlockDialogHelper = new CarTrustAgentUnlockDialogHelper(mContext); mUserManager = mContext.getSystemService(UserManager.class); + mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT, + (car, ready) -> { + if (!ready) { + return; + } + mEnrollmentManager = (CarTrustAgentEnrollmentManager) car + .getCarManager(Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE); + }); + mShortAnimDuration = container.getResources() .getInteger(android.R.integer.config_shortAnimTime); IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED); @@ -201,6 +209,9 @@ public class FullscreenUserSwitcher { } private boolean hasTrustedDevice(int uid) { + if (mEnrollmentManager == null) { // car service not ready, so it cannot be available. + return false; + } return !mEnrollmentManager.getEnrolledDeviceInfoForUser(uid).isEmpty(); } diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java index 8de1439c3306..a27dd341d449 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java @@ -18,13 +18,10 @@ package com.android.systemui.statusbar.car; import android.annotation.NonNull; import android.car.Car; -import android.car.CarNotConnectedException; +import android.car.Car.CarServiceLifecycleListener; import android.car.hardware.power.CarPowerManager; import android.car.hardware.power.CarPowerManager.CarPowerStateListener; -import android.content.ComponentName; import android.content.Context; -import android.content.ServiceConnection; -import android.os.IBinder; import android.util.Log; /** @@ -39,55 +36,30 @@ public class PowerManagerHelper { private Car mCar; private CarPowerManager mCarPowerManager; - private final ServiceConnection mCarConnectionListener = - new ServiceConnection() { - public void onServiceConnected(ComponentName name, IBinder service) { - Log.d(TAG, "Car Service connected"); - try { - mCarPowerManager = (CarPowerManager) mCar.getCarManager(Car.POWER_SERVICE); - if (mCarPowerManager != null) { - mCarPowerManager.setListener(mCarPowerStateListener); - } else { - Log.e(TAG, "CarPowerManager service not available"); - } - } catch (CarNotConnectedException e) { - Log.e(TAG, "Car not connected", e); - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - destroyCarPowerManager(); - } - }; + private final CarServiceLifecycleListener mCarServiceLifecycleListener; PowerManagerHelper(Context context, @NonNull CarPowerStateListener listener) { mContext = context; mCarPowerStateListener = listener; + mCarServiceLifecycleListener = (car, ready) -> { + if (!ready) { + return; + } + Log.d(TAG, "Car Service connected"); + mCarPowerManager = (CarPowerManager) car.getCarManager(Car.POWER_SERVICE); + if (mCarPowerManager != null) { + mCarPowerManager.setListener(mCarPowerStateListener); + } else { + Log.e(TAG, "CarPowerManager service not available"); + } + }; } /** * Connect to Car service. */ void connectToCarService() { - mCar = Car.createCar(mContext, mCarConnectionListener); - if (mCar != null) { - mCar.connect(); - } - } - - /** - * Disconnects from Car service. - */ - void disconnectFromCarService() { - if (mCar != null) { - mCar.disconnect(); - } - } - - private void destroyCarPowerManager() { - if (mCarPowerManager != null) { - mCarPowerManager.clearListener(); - } + mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT, + mCarServiceLifecycleListener); } } diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java index 3b482599b2a0..fb1870a6ea42 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.car; import static android.content.DialogInterface.BUTTON_NEGATIVE; import static android.content.DialogInterface.BUTTON_POSITIVE; import static android.os.UserManager.DISALLOW_ADD_USER; +import static android.os.UserManager.SWITCHABILITY_STATUS_OK; import android.app.ActivityManager; import android.app.AlertDialog; @@ -32,7 +33,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.UserInfo; import android.content.res.Resources; -import android.graphics.Bitmap; import android.graphics.Rect; import android.os.AsyncTask; import android.os.UserHandle; @@ -67,6 +67,7 @@ public class UserGridRecyclerView extends RecyclerView { private CarUserManagerHelper mCarUserManagerHelper; private UserManager mUserManager; private Context mContext; + private UserIconProvider mUserIconProvider; private final BroadcastReceiver mUserUpdateReceiver = new BroadcastReceiver() { @Override @@ -80,6 +81,7 @@ public class UserGridRecyclerView extends RecyclerView { mContext = context; mCarUserManagerHelper = new CarUserManagerHelper(mContext); mUserManager = UserManager.get(mContext); + mUserIconProvider = new UserIconProvider(); addItemDecoration(new ItemSpacingDecoration(mContext.getResources().getDimensionPixelSize( R.dimen.car_user_switcher_vertical_spacing_between_users))); @@ -122,10 +124,12 @@ public class UserGridRecyclerView extends RecyclerView { } private List<UserRecord> createUserRecords(List<UserInfo> userInfoList) { + int fgUserId = ActivityManager.getCurrentUser(); + UserHandle fgUserHandle = UserHandle.of(fgUserId); List<UserRecord> userRecords = new ArrayList<>(); // If the foreground user CANNOT switch to other users, only display the foreground user. - if (!mCarUserManagerHelper.canForegroundUserSwitchUsers()) { + if (mUserManager.getUserSwitchability(fgUserHandle) != SWITCHABILITY_STATUS_OK) { userRecords.add(createForegroundUserRecord()); return userRecords; } @@ -136,7 +140,7 @@ public class UserGridRecyclerView extends RecyclerView { continue; } - boolean isForeground = ActivityManager.getCurrentUser() == userInfo.id; + boolean isForeground = fgUserId == userInfo.id; UserRecord record = new UserRecord(userInfo, false /* isStartGuestSession */, false /* isAddUser */, isForeground); userRecords.add(record); @@ -146,7 +150,6 @@ public class UserGridRecyclerView extends RecyclerView { userRecords.add(createStartGuestUserRecord()); // Add add user record if the foreground user can add users - UserHandle fgUserHandle = UserHandle.of(ActivityManager.getCurrentUser()); if (!mUserManager.hasUserRestriction(DISALLOW_ADD_USER, fgUserHandle)) { userRecords.add(createAddUserRecord()); } @@ -252,9 +255,7 @@ public class UserGridRecyclerView extends RecyclerView { @Override public void onBindViewHolder(UserAdapterViewHolder holder, int position) { UserRecord userRecord = mUsers.get(position); - RoundedBitmapDrawable circleIcon = RoundedBitmapDrawableFactory.create(mRes, - getUserRecordIcon(userRecord)); - circleIcon.setCircular(true); + RoundedBitmapDrawable circleIcon = getCircularUserRecordIcon(userRecord); holder.mUserAvatarImageView.setImageDrawable(circleIcon); holder.mUserNameTextView.setText(userRecord.mInfo.name); @@ -336,17 +337,20 @@ public class UserGridRecyclerView extends RecyclerView { } } - private Bitmap getUserRecordIcon(UserRecord userRecord) { + private RoundedBitmapDrawable getCircularUserRecordIcon(UserRecord userRecord) { + Resources resources = mContext.getResources(); + RoundedBitmapDrawable circleIcon; if (userRecord.mIsStartGuestSession) { - return mCarUserManagerHelper.getGuestDefaultIcon(); - } - - if (userRecord.mIsAddUser) { - return UserIcons.convertToBitmap(mContext - .getDrawable(R.drawable.car_add_circle_round)); + circleIcon = mUserIconProvider.getRoundedGuestDefaultIcon(resources); + } else if (userRecord.mIsAddUser) { + circleIcon = RoundedBitmapDrawableFactory.create(mRes, UserIcons.convertToBitmap( + mContext.getDrawable(R.drawable.car_add_circle_round))); + circleIcon.setCircular(true); + } else { + circleIcon = mUserIconProvider.getRoundedUserIcon(userRecord.mInfo, mContext); } - return mCarUserManagerHelper.getUserIcon(userRecord.mInfo); + return circleIcon; } @Override diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserIconProvider.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserIconProvider.java new file mode 100644 index 000000000000..9464eab2085b --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserIconProvider.java @@ -0,0 +1,116 @@ +/* + * 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.car; + +import android.annotation.UserIdInt; +import android.content.Context; +import android.content.pm.UserInfo; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; +import android.os.UserManager; + +import androidx.core.graphics.drawable.RoundedBitmapDrawable; +import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; + +import com.android.internal.util.UserIcons; +import com.android.systemui.R; + +/** + * Simple class for providing icons for users. + */ +public class UserIconProvider { + /** + * Gets a scaled rounded icon for the given user. If a user does not have an icon saved, this + * method will default to a generic icon and update UserManager to use that icon. + * + * @param userInfo User for which the icon is requested. + * @param context Context to use for resources + * @return {@link RoundedBitmapDrawable} representing the icon for the user. + */ + public RoundedBitmapDrawable getRoundedUserIcon(UserInfo userInfo, Context context) { + UserManager userManager = UserManager.get(context); + Resources res = context.getResources(); + Bitmap icon = userManager.getUserIcon(userInfo.id); + + if (icon == null) { + icon = assignDefaultIcon(userManager, res, userInfo); + } + + return createScaledRoundIcon(res, icon); + } + + /** Returns a scaled, rounded, default icon for the Guest user */ + public RoundedBitmapDrawable getRoundedGuestDefaultIcon(Resources resources) { + return createScaledRoundIcon(resources, getGuestUserDefaultIcon(resources)); + } + + private RoundedBitmapDrawable createScaledRoundIcon(Resources resources, Bitmap icon) { + BitmapDrawable scaledIcon = scaleUserIcon(resources, icon); + RoundedBitmapDrawable circleIcon = + RoundedBitmapDrawableFactory.create(resources, scaledIcon.getBitmap()); + circleIcon.setCircular(true); + return circleIcon; + } + + /** + * Returns a {@link Drawable} for the given {@code icon} scaled to the appropriate size. + */ + private static BitmapDrawable scaleUserIcon(Resources res, Bitmap icon) { + int desiredSize = res.getDimensionPixelSize(R.dimen.car_primary_icon_size); + Bitmap scaledIcon = + Bitmap.createScaledBitmap(icon, desiredSize, desiredSize, /*filter=*/ true); + return new BitmapDrawable(res, scaledIcon); + } + + /** + * Assigns a default icon to a user according to the user's id. Handles Guest icon and non-guest + * user icons. + * + * @param userManager {@link UserManager} to set user icon + * @param resources {@link Resources} to grab icons from + * @param userInfo User whose avatar is set to default icon. + * @return Bitmap of the user icon. + */ + private Bitmap assignDefaultIcon( + UserManager userManager, Resources resources, UserInfo userInfo) { + Bitmap bitmap = userInfo.isGuest() + ? getGuestUserDefaultIcon(resources) + : getUserDefaultIcon(resources, userInfo.id); + userManager.setUserIcon(userInfo.id, bitmap); + return bitmap; + } + + /** + * Gets a bitmap representing the user's default avatar. + * + * @param resources The resources to pull from + * @param id The id of the user to get the icon for. Pass {@link UserHandle#USER_NULL} for + * Guest user. + * @return Default user icon + */ + private Bitmap getUserDefaultIcon(Resources resources, @UserIdInt int id) { + return UserIcons.convertToBitmap( + UserIcons.getDefaultUserIcon(resources, id, /* light= */ false)); + } + + private Bitmap getGuestUserDefaultIcon(Resources resources) { + return getUserDefaultIcon(resources, UserHandle.USER_NULL); + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java index e81be1b0b186..41914d212ee9 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java @@ -20,15 +20,13 @@ import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL; import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS; import android.car.Car; +import android.car.Car.CarServiceLifecycleListener; import android.car.VehicleUnit; import android.car.hardware.CarPropertyValue; import android.car.hardware.hvac.CarHvacManager; import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback; -import android.content.ComponentName; import android.content.Context; -import android.content.ServiceConnection; import android.os.Handler; -import android.os.IBinder; import android.util.Log; import java.util.ArrayList; @@ -54,6 +52,7 @@ public class HvacController { private Car mCar; private CarHvacManager mHvacManager; private HashMap<HvacKey, List<TemperatureView>> mTempComponents = new HashMap<>(); + /** * Callback for getting changes from {@link CarHvacManager} and setting the UI elements to * match. @@ -85,39 +84,17 @@ public class HvacController { + " zone: " + zone); } }; - /** - * If the connection to car service goes away then restart it. - */ - private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() { - @Override - public void binderDied() { - Log.d(TAG, "Death of HVAC triggering a restart"); - if (mCar != null) { - mCar.disconnect(); - } - destroyHvacManager(); - mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY); - } - }; - /** - * Registers callbacks and initializes components upon connection. - */ - private ServiceConnection mServiceConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - try { - service.linkToDeath(mRestart, 0); - mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE); - mHvacManager.registerCallback(mHardwareCallback); - initComponents(); - } catch (Exception e) { - Log.e(TAG, "Failed to correctly connect to HVAC", e); - } - } - @Override - public void onServiceDisconnected(ComponentName name) { - destroyHvacManager(); + private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> { + if (!ready) { + return; + } + try { + mHvacManager = (CarHvacManager) car.getCarManager(Car.HVAC_SERVICE); + mHvacManager.registerCallback(mHardwareCallback); + initComponents(); + } catch (Exception e) { + Log.e(TAG, "Failed to correctly connect to HVAC", e); } }; @@ -132,18 +109,8 @@ public class HvacController { */ public void connectToCarService() { mHandler = new Handler(); - mCar = Car.createCar(mContext, mServiceConnection, mHandler); - if (mCar != null) { - // note: this connect call handles the retries - mCar.connect(); - } - } - - private void destroyHvacManager() { - if (mHvacManager != null) { - mHvacManager.unregisterCallback(mHardwareCallback); - mHvacManager = null; - } + mCar = Car.createCar(mContext, /* handler= */ mHandler, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT, + mCarServiceLifecycleListener); } /** diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java index 22c7c7a3d6af..ac2ba3d36bee 100644 --- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java +++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java @@ -24,12 +24,10 @@ import android.annotation.Nullable; import android.app.Dialog; import android.app.KeyguardManager; import android.car.Car; -import android.car.CarNotConnectedException; +import android.car.Car.CarServiceLifecycleListener; import android.car.media.CarAudioManager; -import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; -import android.content.ServiceConnection; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.Color; @@ -39,7 +37,6 @@ import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.os.Debug; import android.os.Handler; -import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.util.AttributeSet; @@ -65,7 +62,6 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; /** @@ -94,12 +90,21 @@ public class CarVolumeDialogImpl implements VolumeDialog { // Volume items in the RecyclerView. private final List<CarVolumeItem> mCarVolumeLineItems = new ArrayList<>(); private final KeyguardManager mKeyguard; + private Window mWindow; private CustomDialog mDialog; private RecyclerView mListView; private CarVolumeItemAdapter mVolumeItemsAdapter; private Car mCar; private CarAudioManager mCarAudioManager; + private boolean mHovering; + private int mCurrentlyDisplayingGroupId; + private int mPreviouslyDisplayingGroupId; + private boolean mShowing; + private boolean mDismissing; + private boolean mExpanded; + private View mExpandIcon; + private final CarAudioManager.CarVolumeCallback mVolumeChangeCallback = new CarAudioManager.CarVolumeCallback() { @Override @@ -129,6 +134,7 @@ public class CarVolumeDialogImpl implements VolumeDialog { volumeItem.progress = value; } if ((flags & AudioManager.FLAG_SHOW_UI) != 0) { + mPreviouslyDisplayingGroupId = mCurrentlyDisplayingGroupId; mCurrentlyDisplayingGroupId = groupId; mHandler.obtainMessage(H.SHOW, Events.SHOW_REASON_VOLUME_CHANGED).sendToTarget(); @@ -140,81 +146,43 @@ public class CarVolumeDialogImpl implements VolumeDialog { // ignored } }; - private boolean mHovering; - private int mCurrentlyDisplayingGroupId; - private boolean mShowing; - private boolean mDismissing; - private boolean mExpanded; - private View mExpandIcon; - private final ServiceConnection mServiceConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - try { - mExpanded = false; - mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE); - int volumeGroupCount = mCarAudioManager.getVolumeGroupCount(); - // Populates volume slider items from volume groups to UI. - for (int groupId = 0; groupId < volumeGroupCount; groupId++) { - VolumeItem volumeItem = getVolumeItemForUsages( - mCarAudioManager.getUsagesForVolumeGroupId(groupId)); - mAvailableVolumeItems.add(volumeItem); - // The first one is the default item. - if (groupId == 0) { - setuptListItem(0); - } - } - // If list is already initiated, update its content. - if (mVolumeItemsAdapter != null) { - mVolumeItemsAdapter.notifyDataSetChanged(); - } - mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback); - } catch (CarNotConnectedException e) { - Log.e(TAG, "Car is not connected!", e); + private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> { + if (!ready) { + return; + } + mExpanded = false; + mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE); + int volumeGroupCount = mCarAudioManager.getVolumeGroupCount(); + // Populates volume slider items from volume groups to UI. + for (int groupId = 0; groupId < volumeGroupCount; groupId++) { + VolumeItem volumeItem = getVolumeItemForUsages( + mCarAudioManager.getUsagesForVolumeGroupId(groupId)); + mAvailableVolumeItems.add(volumeItem); + // The first one is the default item. + if (groupId == 0) { + clearAllAndSetupDefaultCarVolumeLineItem(0); } } - /** - * This does not get called when service is properly disconnected. - * So we need to also handle cleanups in destroy(). - */ - @Override - public void onServiceDisconnected(ComponentName name) { - cleanupAudioManager(); + // If list is already initiated, update its content. + if (mVolumeItemsAdapter != null) { + mVolumeItemsAdapter.notifyDataSetChanged(); } + mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback); }; - private void setuptListItem(int groupId) { - mCarVolumeLineItems.clear(); - VolumeItem volumeItem = mAvailableVolumeItems.get(groupId); - volumeItem.defaultItem = true; - addCarVolumeListItem(volumeItem, /* volumeGroupId = */ groupId, - R.drawable.car_ic_keyboard_arrow_down, new ExpandIconListener() - ); - } - public CarVolumeDialogImpl(Context context) { mContext = context; mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); - mCar = Car.createCar(mContext, mServiceConnection); } private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) { - try { - return carAudioManager.getGroupVolume(volumeGroupId); - } catch (CarNotConnectedException e) { - Log.e(TAG, "Car is not connected!", e); - } - return 0; + return carAudioManager.getGroupVolume(volumeGroupId); } private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) { - try { - return carAudioManager.getGroupMaxVolume(volumeGroupId); - } catch (CarNotConnectedException e) { - Log.e(TAG, "Car is not connected!", e); - } - return 0; + return carAudioManager.getGroupMaxVolume(volumeGroupId); } /** @@ -224,18 +192,21 @@ public class CarVolumeDialogImpl implements VolumeDialog { @Override public void init(int windowType, Callback callback) { initDialog(); - - mCar.connect(); + mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT, + mCarServiceLifecycleListener); } @Override public void destroy() { - mHandler.removeCallbacksAndMessages(null); + mHandler.removeCallbacksAndMessages(/* token= */ null); cleanupAudioManager(); // unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup // audio manager beforehand. - mCar.disconnect(); + if (mCar != null) { + mCar.disconnect(); + mCar = null; + } } private void initDialog() { @@ -303,19 +274,36 @@ public class CarVolumeDialogImpl implements VolumeDialog { mHandler.removeMessages(H.SHOW); mHandler.removeMessages(H.DISMISS); + rescheduleTimeoutH(); + // Refresh the data set before showing. mVolumeItemsAdapter.notifyDataSetChanged(); + if (mShowing) { + if (mPreviouslyDisplayingGroupId == mCurrentlyDisplayingGroupId || mExpanded) { + return; + } + + clearAllAndSetupDefaultCarVolumeLineItem(mCurrentlyDisplayingGroupId); return; } + mShowing = true; - setuptListItem(mCurrentlyDisplayingGroupId); + clearAllAndSetupDefaultCarVolumeLineItem(mCurrentlyDisplayingGroupId); mDialog.show(); Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked()); } - private void rescheduleTimeoutH() { + private void clearAllAndSetupDefaultCarVolumeLineItem(int groupId) { + mCarVolumeLineItems.clear(); + VolumeItem volumeItem = mAvailableVolumeItems.get(groupId); + volumeItem.defaultItem = true; + addCarVolumeListItem(volumeItem, /* volumeGroupId = */ groupId, + R.drawable.car_ic_keyboard_arrow_down, new ExpandIconListener()); + } + + protected void rescheduleTimeoutH() { mHandler.removeMessages(H.DISMISS); final int timeout = computeTimeoutH(); mHandler.sendMessageDelayed(mHandler @@ -389,12 +377,13 @@ public class CarVolumeDialogImpl implements VolumeDialog { if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) { TypedArray item = mContext.getResources().obtainAttributes( attrs, R.styleable.carVolumeItems_item); - int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1); + int usage = item.getInt(R.styleable.carVolumeItems_item_usage, + /* defValue= */ -1); if (usage >= 0) { VolumeItem volumeItem = new VolumeItem(); volumeItem.rank = rank; - volumeItem.icon = item.getResourceId(R.styleable.carVolumeItems_item_icon, - 0); + volumeItem.icon = item.getResourceId( + R.styleable.carVolumeItems_item_icon, /* defValue= */ 0); mVolumeItems.put(usage, volumeItem); rank++; } @@ -419,22 +408,22 @@ public class CarVolumeDialogImpl implements VolumeDialog { return result; } - private CarVolumeItem addCarVolumeListItem(VolumeItem volumeItem, int volumeGroupId, - int supplementalIconId, + private CarVolumeItem createCarVolumeListItem(VolumeItem volumeItem, int volumeGroupId, + Drawable supplementalIcon, int seekbarProgressValue, @Nullable View.OnClickListener supplementalIconOnClickListener) { CarVolumeItem carVolumeItem = new CarVolumeItem(); carVolumeItem.setMax(getMaxSeekbarValue(mCarAudioManager, volumeGroupId)); - int color = mContext.getResources().getColor(R.color.car_volume_dialog_tint); - int progress = getSeekbarValue(mCarAudioManager, volumeGroupId); - carVolumeItem.setProgress(progress); + carVolumeItem.setProgress(seekbarProgressValue); carVolumeItem.setOnSeekBarChangeListener( new CarVolumeDialogImpl.VolumeSeekBarChangeListener(volumeGroupId, mCarAudioManager)); - Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon); + carVolumeItem.setGroupId(volumeGroupId); + + int color = mContext.getColor(R.color.car_volume_dialog_tint); + Drawable primaryIcon = mContext.getDrawable(volumeItem.icon); primaryIcon.mutate().setTint(color); carVolumeItem.setPrimaryIcon(primaryIcon); - if (supplementalIconId != 0) { - Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId); + if (supplementalIcon != null) { supplementalIcon.mutate().setTint(color); carVolumeItem.setSupplementalIcon(supplementalIcon, /* showSupplementalIconDivider= */ true); @@ -443,21 +432,23 @@ public class CarVolumeDialogImpl implements VolumeDialog { carVolumeItem.setSupplementalIcon(/* drawable= */ null, /* showSupplementalIconDivider= */ false); } - carVolumeItem.setGroupId(volumeGroupId); - mCarVolumeLineItems.add(carVolumeItem); + volumeItem.carVolumeItem = carVolumeItem; - volumeItem.progress = progress; + volumeItem.progress = seekbarProgressValue; + return carVolumeItem; } - private VolumeItem findVolumeItem(CarVolumeItem targetItem) { - for (int i = 0; i < mVolumeItems.size(); ++i) { - VolumeItem volumeItem = mVolumeItems.valueAt(i); - if (volumeItem.carVolumeItem == targetItem) { - return volumeItem; - } - } - return null; + private CarVolumeItem addCarVolumeListItem(VolumeItem volumeItem, int volumeGroupId, + int supplementalIconId, + @Nullable View.OnClickListener supplementalIconOnClickListener) { + int seekbarProgressValue = getSeekbarValue(mCarAudioManager, volumeGroupId); + Drawable supplementalIcon = supplementalIconId == 0 ? null : mContext.getDrawable( + supplementalIconId); + CarVolumeItem carVolumeItem = createCarVolumeListItem(volumeItem, volumeGroupId, + supplementalIcon, seekbarProgressValue, supplementalIconOnClickListener); + mCarVolumeLineItems.add(carVolumeItem); + return carVolumeItem; } private void cleanupAudioManager() { @@ -553,21 +544,15 @@ public class CarVolumeDialogImpl implements VolumeDialog { for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) { if (groupId != mCurrentlyDisplayingGroupId) { VolumeItem volumeItem = mAvailableVolumeItems.get(groupId); - addCarVolumeListItem(volumeItem, groupId, 0, null); + addCarVolumeListItem(volumeItem, groupId, /* supplementalIconId= */ 0, + /* supplementalIconOnClickListener= */ null); } } inAnimator = AnimatorInflater.loadAnimator( mContext, R.anim.car_arrow_fade_in_rotate_up); } else { - // Only keeping the default stream if it is not expended. - Iterator itr = mCarVolumeLineItems.iterator(); - while (itr.hasNext()) { - CarVolumeItem carVolumeItem = (CarVolumeItem) itr.next(); - if (carVolumeItem.getGroupId() != mCurrentlyDisplayingGroupId) { - itr.remove(); - } - } + clearAllAndSetupDefaultCarVolumeLineItem(mCurrentlyDisplayingGroupId); inAnimator = AnimatorInflater.loadAnimator( mContext, R.anim.car_arrow_fade_in_rotate_down); } @@ -605,18 +590,14 @@ public class CarVolumeDialogImpl implements VolumeDialog { // sent back down again. return; } - try { - if (mCarAudioManager == null) { - Log.w(TAG, "Ignoring volume change event because the car isn't connected"); - return; - } - mAvailableVolumeItems.get(mVolumeGroupId).progress = progress; - mAvailableVolumeItems.get( - mVolumeGroupId).carVolumeItem.setProgress(progress); - mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0); - } catch (CarNotConnectedException e) { - Log.e(TAG, "Car is not connected!", e); + if (mCarAudioManager == null) { + Log.w(TAG, "Ignoring volume change event because the car isn't connected"); + return; } + mAvailableVolumeItems.get(mVolumeGroupId).progress = progress; + mAvailableVolumeItems.get( + mVolumeGroupId).carVolumeItem.setProgress(progress); + mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0); } @Override diff --git a/packages/CarSystemUI/tests/Android.mk b/packages/CarSystemUI/tests/Android.mk new file mode 100644 index 000000000000..1366568c3a66 --- /dev/null +++ b/packages/CarSystemUI/tests/Android.mk @@ -0,0 +1,88 @@ +# 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. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_USE_AAPT2 := true +LOCAL_MODULE_TAGS := tests + +LOCAL_JACK_FLAGS := --multi-dex native +LOCAL_DX_FLAGS := --multi-dex + +LOCAL_PACKAGE_NAME := CarSystemUITests +LOCAL_PRIVATE_PLATFORM_APIS := true +LOCAL_COMPATIBILITY_SUITE := device-tests + +LOCAL_STATIC_ANDROID_LIBRARIES := \ + CarSystemUI-tests + +LOCAL_MULTILIB := both + +LOCAL_JNI_SHARED_LIBRARIES := \ + libdexmakerjvmtiagent \ + libmultiplejvmtiagentsinterferenceagent + +LOCAL_JAVA_LIBRARIES := \ + android.test.runner \ + telephony-common \ + android.test.base \ + +LOCAL_AAPT_FLAGS := --extra-packages com.android.systemui + +# sign this with platform cert, so this test is allowed to inject key events into +# UI it doesn't own. This is necessary to allow screenshots to be taken +LOCAL_CERTIFICATE := platform + +# Provide jack a list of classes to exclude from code coverage. +# This is needed because the CarSystemUITests compile CarSystemUI source directly, rather than using +# LOCAL_INSTRUMENTATION_FOR := CarSystemUI. +# +# We want to exclude the test classes from code coverage measurements, but they share the same +# package as the rest of SystemUI so they can't be easily filtered by package name. +# +# Generate a comma separated list of patterns based on the test source files under src/ +# SystemUI classes are in ../src/ so they won't be excluded. +# Example: +# Input files: src/com/android/systemui/Test.java src/com/android/systemui/AnotherTest.java +# Generated exclude list: com.android.systemui.Test*,com.android.systemui.AnotherTest* + +# Filter all src files under src/ to just java files +local_java_files := $(filter %.java,$(call all-java-files-under, src)) + +# Transform java file names into full class names. +# This only works if the class name matches the file name and the directory structure +# matches the package. +local_classes := $(subst /,.,$(patsubst src/%.java,%,$(local_java_files))) +local_comma := , +local_empty := +local_space := $(local_empty) $(local_empty) + +# Convert class name list to jacoco exclude list +# This appends a * to all classes and replace the space separators with commas. +jacoco_exclude := $(subst $(space),$(comma),$(patsubst %,%*,$(local_classes))) + +LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.systemui.*,com.android.keyguard.* +LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := $(jacoco_exclude) + +ifeq ($(EXCLUDE_SYSTEMUI_TESTS),) + include $(BUILD_PACKAGE) +endif + +# Reset variables +local_java_files := +local_classes := +local_comma := +local_space := +jacoco_exclude := diff --git a/packages/CarSystemUI/tests/AndroidManifest.xml b/packages/CarSystemUI/tests/AndroidManifest.xml new file mode 100644 index 000000000000..a74bb56d8d75 --- /dev/null +++ b/packages/CarSystemUI/tests/AndroidManifest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:sharedUserId="android.uid.system" + package="com.android.systemui.tests"> + + <application android:debuggable="true" android:largeHeap="true"> + <uses-library android:name="android.test.runner" /> + + <provider + android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer" + tools:replace="android:authorities" + android:authorities="${applicationId}.lifecycle-tests" + android:exported="false" + android:enabled="false" + android:multiprocess="true" /> + </application> + + <instrumentation android:name="android.testing.TestableInstrumentation" + android:targetPackage="com.android.systemui.tests" + android:label="Tests for CarSystemUI"> + </instrumentation> +</manifest> diff --git a/packages/CarSystemUI/tests/AndroidTest.xml b/packages/CarSystemUI/tests/AndroidTest.xml new file mode 100644 index 000000000000..8685632f2b63 --- /dev/null +++ b/packages/CarSystemUI/tests/AndroidTest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> +<configuration description="Runs Tests for CarSystemUI."> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="CarSystemUITests.apk" /> + </target_preparer> + + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="framework-base-presubmit" /> + <option name="test-tag" value="CarSystemUITests" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.systemui.tests" /> + <option name="runner" value="android.testing.TestableInstrumentation" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/packages/CarSystemUI/tests/res/values/config.xml b/packages/CarSystemUI/tests/res/values/config.xml new file mode 100644 index 000000000000..0d08ac26d962 --- /dev/null +++ b/packages/CarSystemUI/tests/res/values/config.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> +<resources> + <!-- Configure which system ui bars should be displayed. + These can be overwritten within the tests. --> + <bool name="config_enableLeftNavigationBar">false</bool> + <bool name="config_enableRightNavigationBar">false</bool> + <bool name="config_enableBottomNavigationBar">false</bool> +</resources> diff --git a/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java b/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java new file mode 100644 index 000000000000..fe59cbf20a13 --- /dev/null +++ b/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java @@ -0,0 +1,214 @@ +/* + * 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; + +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import android.testing.AndroidTestingRunner; +import android.text.TextUtils; +import android.util.Log; + +import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; +import androidx.test.filters.SmallTest; +import androidx.test.internal.runner.ClassPathScanner; +import androidx.test.internal.runner.ClassPathScanner.ChainedClassNameFilter; +import androidx.test.internal.runner.ClassPathScanner.ExternalClassNameFilter; + +import com.android.systemui.SysuiBaseFragmentTest; +import com.android.systemui.SysuiTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +/** + * This is named AAAPlusPlusVerifySysuiRequiredTestPropertiesTest for two reasons. + * a) Its so awesome it deserves an AAA++ + * b) It should run first to draw attention to itself. + * + * For trues though: this test verifies that all the sysui tests extend the right classes. + * This matters because including tests with different context implementations in the same + * test suite causes errors, such as the incorrect settings provider being cached. + * For an example, see {@link com.android.systemui.DependencyTest}. + */ +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class AAAPlusPlusVerifySysuiRequiredTestPropertiesTest extends SysuiTestCase { + + private static final String TAG = "AAA++VerifyTest"; + + private static final Class[] BASE_CLS_WHITELIST = { + SysuiTestCase.class, + SysuiBaseFragmentTest.class, + }; + + private static final Class[] SUPPORTED_SIZES = { + SmallTest.class, + MediumTest.class, + LargeTest.class, + android.test.suitebuilder.annotation.SmallTest.class, + android.test.suitebuilder.annotation.MediumTest.class, + android.test.suitebuilder.annotation.LargeTest.class, + }; + + @Test + public void testAllClassInheritance() throws Throwable { + ArrayList<String> fails = new ArrayList<>(); + for (String className : getClassNamesFromClassPath()) { + Class<?> cls = Class.forName(className, false, this.getClass().getClassLoader()); + if (!isTestClass(cls)) continue; + + boolean hasParent = false; + for (Class<?> parent : BASE_CLS_WHITELIST) { + if (parent.isAssignableFrom(cls)) { + hasParent = true; + break; + } + } + boolean hasSize = hasSize(cls); + if (!hasSize) { + fails.add(cls.getName() + " does not have size annotation, such as @SmallTest"); + } + if (!hasParent) { + fails.add(cls.getName() + " does not extend any of " + getClsStr()); + } + } + + assertThat("All sysui test classes must have size and extend one of " + getClsStr(), + fails, is(empty())); + } + + private boolean hasSize(Class<?> cls) { + for (int i = 0; i < SUPPORTED_SIZES.length; i++) { + if (cls.getDeclaredAnnotation(SUPPORTED_SIZES[i]) != null) return true; + } + return false; + } + + private Collection<String> getClassNamesFromClassPath() { + ClassPathScanner scanner = new ClassPathScanner(mContext.getPackageCodePath()); + + ChainedClassNameFilter filter = new ChainedClassNameFilter(); + + filter.add(new ExternalClassNameFilter()); + filter.add(s -> s.startsWith("com.android.systemui") + || s.startsWith("com.android.keyguard")); + + try { + return scanner.getClassPathEntries(filter); + } catch (IOException e) { + Log.e(TAG, "Failed to scan classes", e); + } + return Collections.emptyList(); + } + + private String getClsStr() { + return TextUtils.join(",", Arrays.asList(BASE_CLS_WHITELIST) + .stream().map(cls -> cls.getSimpleName()).toArray()); + } + + /** + * Determines if given class is a valid test class. + * + * @return <code>true</code> if loadedClass is a test + */ + private boolean isTestClass(Class<?> loadedClass) { + try { + if (Modifier.isAbstract(loadedClass.getModifiers())) { + logDebug(String.format("Skipping abstract class %s: not a test", + loadedClass.getName())); + return false; + } + // TODO: try to find upstream junit calls to replace these checks + if (junit.framework.Test.class.isAssignableFrom(loadedClass)) { + // ensure that if a TestCase, it has at least one test method otherwise + // TestSuite will throw error + if (junit.framework.TestCase.class.isAssignableFrom(loadedClass)) { + return hasJUnit3TestMethod(loadedClass); + } + return true; + } + // TODO: look for a 'suite' method? + if (loadedClass.isAnnotationPresent(RunWith.class)) { + return true; + } + for (Method testMethod : loadedClass.getMethods()) { + if (testMethod.isAnnotationPresent(Test.class)) { + return true; + } + } + logDebug(String.format("Skipping class %s: not a test", loadedClass.getName())); + return false; + } catch (Exception e) { + // Defensively catch exceptions - Will throw runtime exception if it cannot load + // methods. + // For earlier versions of Android (Pre-ICS), Dalvik might try to initialize a class + // during getMethods(), fail to do so, hide the error and throw a NoSuchMethodException. + // Since the java.lang.Class.getMethods does not declare such an exception, resort to a + // generic catch all. + // For ICS+, Dalvik will throw a NoClassDefFoundException. + Log.w(TAG, String.format("%s in isTestClass for %s", e.toString(), + loadedClass.getName())); + return false; + } catch (Error e) { + // defensively catch Errors too + Log.w(TAG, String.format("%s in isTestClass for %s", e.toString(), + loadedClass.getName())); + return false; + } + } + + private boolean hasJUnit3TestMethod(Class<?> loadedClass) { + for (Method testMethod : loadedClass.getMethods()) { + if (isPublicTestMethod(testMethod)) { + return true; + } + } + return false; + } + + // copied from junit.framework.TestSuite + private boolean isPublicTestMethod(Method m) { + return isTestMethod(m) && Modifier.isPublic(m.getModifiers()); + } + + // copied from junit.framework.TestSuite + private boolean isTestMethod(Method m) { + return m.getParameterTypes().length == 0 && m.getName().startsWith("test") + && m.getReturnType().equals(Void.TYPE); + } + + /** + * Utility method for logging debug messages. Only actually logs a message if TAG is marked + * as loggable to limit log spam during normal use. + */ + private void logDebug(String msg) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, msg); + } + } +} diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarControllerTest.java new file mode 100644 index 000000000000..901d2006eb12 --- /dev/null +++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarControllerTest.java @@ -0,0 +1,408 @@ +/* + * 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.navigationbar.car; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.testing.TestableResources; +import android.view.View; +import android.view.ViewGroup; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.DarkIconDispatcher; +import com.android.systemui.statusbar.car.hvac.HvacController; +import com.android.systemui.statusbar.phone.StatusBarIconController; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import dagger.Lazy; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class CarNavigationBarControllerTest extends SysuiTestCase { + + private CarNavigationBarController mCarNavigationBar; + private NavigationBarViewFactory mNavigationBarViewFactory; + private Lazy<HvacController> mHvacControllerLazy; + private TestableResources mTestableResources; + + @Mock + private HvacController mHvacController; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mNavigationBarViewFactory = new NavigationBarViewFactory(mContext); + mHvacControllerLazy = () -> mHvacController; + mTestableResources = mContext.getOrCreateTestableResources(); + + // Needed to inflate top navigation bar. + mDependency.injectMockDependency(DarkIconDispatcher.class); + mDependency.injectMockDependency(StatusBarIconController.class); + } + + @Test + public void testConnectToHvac_callsConnect() { + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + mCarNavigationBar.connectToHvac(); + + verify(mHvacController).connectToCarService(); + } + + @Test + public void testRemoveAllFromHvac_callsRemoveAll() { + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + mCarNavigationBar.removeAllFromHvac(); + + verify(mHvacController).removeAllComponents(); + } + + @Test + public void testGetBottomWindow_bottomDisabled_returnsNull() { + mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, false); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + ViewGroup window = mCarNavigationBar.getBottomWindow(); + + assertThat(window).isNull(); + } + + @Test + public void testGetBottomWindow_bottomEnabled_returnsWindow() { + mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + ViewGroup window = mCarNavigationBar.getBottomWindow(); + + assertThat(window).isNotNull(); + } + + @Test + public void testGetBottomWindow_bottomEnabled_calledTwice_returnsSameWindow() { + mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + ViewGroup window1 = mCarNavigationBar.getBottomWindow(); + ViewGroup window2 = mCarNavigationBar.getBottomWindow(); + + assertThat(window1).isEqualTo(window2); + } + + @Test + public void testGetLeftWindow_leftDisabled_returnsNull() { + mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, false); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + ViewGroup window = mCarNavigationBar.getLeftWindow(); + assertThat(window).isNull(); + } + + @Test + public void testGetLeftWindow_leftEnabled_returnsWindow() { + mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + ViewGroup window = mCarNavigationBar.getLeftWindow(); + + assertThat(window).isNotNull(); + } + + @Test + public void testGetLeftWindow_leftEnabled_calledTwice_returnsSameWindow() { + mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + ViewGroup window1 = mCarNavigationBar.getLeftWindow(); + ViewGroup window2 = mCarNavigationBar.getLeftWindow(); + + assertThat(window1).isEqualTo(window2); + } + + @Test + public void testGetRightWindow_rightDisabled_returnsNull() { + mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, false); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + ViewGroup window = mCarNavigationBar.getRightWindow(); + + assertThat(window).isNull(); + } + + @Test + public void testGetRightWindow_rightEnabled_returnsWindow() { + mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + ViewGroup window = mCarNavigationBar.getRightWindow(); + + assertThat(window).isNotNull(); + } + + @Test + public void testGetRightWindow_rightEnabled_calledTwice_returnsSameWindow() { + mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + ViewGroup window1 = mCarNavigationBar.getRightWindow(); + ViewGroup window2 = mCarNavigationBar.getRightWindow(); + + assertThat(window1).isEqualTo(window2); + } + + @Test + public void testSetBottomWindowVisibility_setTrue_isVisible() { + mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + ViewGroup window = mCarNavigationBar.getBottomWindow(); + mCarNavigationBar.setBottomWindowVisibility(View.VISIBLE); + + assertThat(window.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void testSetBottomWindowVisibility_setFalse_isGone() { + mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + ViewGroup window = mCarNavigationBar.getBottomWindow(); + mCarNavigationBar.setBottomWindowVisibility(View.GONE); + + assertThat(window.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void testSetLeftWindowVisibility_setTrue_isVisible() { + mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + ViewGroup window = mCarNavigationBar.getLeftWindow(); + mCarNavigationBar.setLeftWindowVisibility(View.VISIBLE); + + assertThat(window.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void testSetLeftWindowVisibility_setFalse_isGone() { + mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + ViewGroup window = mCarNavigationBar.getLeftWindow(); + mCarNavigationBar.setLeftWindowVisibility(View.GONE); + + assertThat(window.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void testSetRightWindowVisibility_setTrue_isVisible() { + mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + ViewGroup window = mCarNavigationBar.getRightWindow(); + mCarNavigationBar.setRightWindowVisibility(View.VISIBLE); + + assertThat(window.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void testSetRightWindowVisibility_setFalse_isGone() { + mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + ViewGroup window = mCarNavigationBar.getRightWindow(); + mCarNavigationBar.setRightWindowVisibility(View.GONE); + + assertThat(window.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void testRegisterBottomBarTouchListener_createViewFirst_registrationSuccessful() { + mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true); + View.OnTouchListener controller = bottomBar.getStatusBarWindowTouchListener(); + assertThat(controller).isNull(); + mCarNavigationBar.registerBottomBarTouchListener(mock(View.OnTouchListener.class)); + controller = bottomBar.getStatusBarWindowTouchListener(); + + assertThat(controller).isNotNull(); + } + + @Test + public void testRegisterBottomBarTouchListener_registerFirst_registrationSuccessful() { + mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + mCarNavigationBar.registerBottomBarTouchListener(mock(View.OnTouchListener.class)); + CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true); + View.OnTouchListener controller = bottomBar.getStatusBarWindowTouchListener(); + + assertThat(controller).isNotNull(); + } + + @Test + public void testRegisterNotificationController_createViewFirst_registrationSuccessful() { + mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true); + CarNavigationBarController.NotificationsShadeController controller = + bottomBar.getNotificationsPanelController(); + assertThat(controller).isNull(); + mCarNavigationBar.registerNotificationController( + mock(CarNavigationBarController.NotificationsShadeController.class)); + controller = bottomBar.getNotificationsPanelController(); + + assertThat(controller).isNotNull(); + } + + @Test + public void testRegisterNotificationController_registerFirst_registrationSuccessful() { + mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + + mCarNavigationBar.registerNotificationController( + mock(CarNavigationBarController.NotificationsShadeController.class)); + CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true); + CarNavigationBarController.NotificationsShadeController controller = + bottomBar.getNotificationsPanelController(); + + assertThat(controller).isNotNull(); + } + + @Test + public void testShowAllKeyguardButtons_bottomEnabled_bottomKeyguardButtonsVisible() { + mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true); + View bottomKeyguardButtons = bottomBar.findViewById(R.id.lock_screen_nav_buttons); + + mCarNavigationBar.showAllKeyguardButtons(/* isSetUp= */ true); + + assertThat(bottomKeyguardButtons.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void testShowAllKeyguardButtons_bottomEnabled_bottomNavButtonsGone() { + mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true); + View bottomButtons = bottomBar.findViewById(R.id.nav_buttons); + + mCarNavigationBar.showAllKeyguardButtons(/* isSetUp= */ true); + + assertThat(bottomButtons.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void testHideAllKeyguardButtons_bottomEnabled_bottomKeyguardButtonsGone() { + mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true); + View bottomKeyguardButtons = bottomBar.findViewById(R.id.lock_screen_nav_buttons); + + mCarNavigationBar.showAllKeyguardButtons(/* isSetUp= */ true); + assertThat(bottomKeyguardButtons.getVisibility()).isEqualTo(View.VISIBLE); + mCarNavigationBar.hideAllKeyguardButtons(/* isSetUp= */ true); + + assertThat(bottomKeyguardButtons.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void testHideAllKeyguardButtons_bottomEnabled_bottomNavButtonsVisible() { + mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true); + View bottomButtons = bottomBar.findViewById(R.id.nav_buttons); + + mCarNavigationBar.showAllKeyguardButtons(/* isSetUp= */ true); + assertThat(bottomButtons.getVisibility()).isEqualTo(View.GONE); + mCarNavigationBar.hideAllKeyguardButtons(/* isSetUp= */ true); + + assertThat(bottomButtons.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void testToggleAllNotificationsUnseenIndicator_bottomEnabled_hasUnseen_setCorrectly() { + mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true); + CarNavigationButton notifications = bottomBar.findViewById(R.id.notifications); + + boolean hasUnseen = true; + mCarNavigationBar.toggleAllNotificationsUnseenIndicator(/* isSetUp= */ true, + hasUnseen); + + assertThat(notifications.getUnseen()).isTrue(); + } + + @Test + public void testToggleAllNotificationsUnseenIndicator_bottomEnabled_noUnseen_setCorrectly() { + mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true); + mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory, + mHvacControllerLazy); + CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true); + CarNavigationButton notifications = bottomBar.findViewById(R.id.notifications); + + boolean hasUnseen = false; + mCarNavigationBar.toggleAllNotificationsUnseenIndicator(/* isSetUp= */ true, + hasUnseen); + + assertThat(notifications.getUnseen()).isFalse(); + } +} diff --git a/packages/CtsShim/Android.bp b/packages/CtsShim/Android.bp new file mode 100644 index 000000000000..7728464f8652 --- /dev/null +++ b/packages/CtsShim/Android.bp @@ -0,0 +1,74 @@ +// +// 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. +// + +//########################################################## +// Variant: Privileged app + +android_app_import { + name: "CtsShimPrivPrebuilt", + + // this needs to be a privileged application + privileged: true, + + // Make sure the build system doesn't try to resign the APK + dex_preopt: { + enabled: false, + }, + + arch: { + arm: { + apk: "apk/arm/CtsShimPriv.apk", + }, + arm64: { + apk: "apk/arm/CtsShimPriv.apk", + }, + x86: { + apk: "apk/x86/CtsShimPriv.apk", + }, + x86_64: { + apk: "apk/x86/CtsShimPriv.apk", + }, + }, + presigned: true, +} + +//########################################################## +// Variant: System app + +android_app_import { + name: "CtsShimPrebuilt", + + // Make sure the build system doesn't try to resign the APK + dex_preopt: { + enabled: false, + }, + + arch: { + arm: { + apk: "apk/arm/CtsShim.apk", + }, + arm64: { + apk: "apk/arm/CtsShim.apk", + }, + x86: { + apk: "apk/x86/CtsShim.apk", + }, + x86_64: { + apk: "apk/x86/CtsShim.apk", + }, + }, + presigned: true, +} diff --git a/packages/CtsShim/Android.mk b/packages/CtsShim/Android.mk deleted file mode 100644 index 12972f14514b..000000000000 --- a/packages/CtsShim/Android.mk +++ /dev/null @@ -1,64 +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. -# - -LOCAL_PATH := $(call my-dir) - -########################################################### -# Variant: Privileged app - -include $(CLEAR_VARS) - -LOCAL_MODULE := CtsShimPrivPrebuilt -LOCAL_MODULE_TAGS := optional -# this needs to be a privileged application -LOCAL_PRIVILEGED_MODULE := true -LOCAL_MODULE_CLASS := APPS -LOCAL_BUILT_MODULE_STEM := package.apk -# Make sure the build system doesn't try to resign the APK -LOCAL_CERTIFICATE := PRESIGNED -LOCAL_DEX_PREOPT := false -LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64 - -LOCAL_SRC_FILES_arm := apk/arm/CtsShimPriv.apk -LOCAL_SRC_FILES_arm64 := apk/arm/CtsShimPriv.apk -LOCAL_SRC_FILES_x86 := apk/x86/CtsShimPriv.apk -LOCAL_SRC_FILES_x86_64 := apk/x86/CtsShimPriv.apk - -include $(BUILD_PREBUILT) - - -########################################################### -# Variant: System app - -include $(CLEAR_VARS) - -LOCAL_MODULE := CtsShimPrebuilt -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := APPS -LOCAL_BUILT_MODULE_STEM := package.apk -# Make sure the build system doesn't try to resign the APK -LOCAL_CERTIFICATE := PRESIGNED -LOCAL_DEX_PREOPT := false -LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64 - -LOCAL_SRC_FILES_arm := apk/arm/CtsShim.apk -LOCAL_SRC_FILES_arm64 := apk/arm/CtsShim.apk -LOCAL_SRC_FILES_x86 := apk/x86/CtsShim.apk -LOCAL_SRC_FILES_x86_64 := apk/x86/CtsShim.apk - -include $(BUILD_PREBUILT) - -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/packages/CtsShim/build/Android.bp b/packages/CtsShim/build/Android.bp new file mode 100644 index 000000000000..ede1fab64973 --- /dev/null +++ b/packages/CtsShim/build/Android.bp @@ -0,0 +1,117 @@ +// 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. + +// Build rules to build shim apks. + +//########################################################## +// Variant: Privileged app upgrade + +android_app { + name: "CtsShimPrivUpgrade", + // this needs to be a privileged application + privileged: true, + + sdk_version: "current", + optimize: { + enabled: false, + }, + dex_preopt: { + enabled: false, + }, + + manifest: "shim_priv_upgrade/AndroidManifest.xml", + + compile_multilib: "both", + jni_libs: ["libshim_jni"], +} + +genrule { + name: "generate_priv_manifest", + srcs: [ + "shim_priv/AndroidManifest.xml", + ":CtsShimPrivUpgrade" + ], + out: ["AndroidManifest.xml"], + cmd: "sed -e s/__HASH__/`sha512sum -b $(location :CtsShimPrivUpgrade) | cut -d' ' -f1`/ $(location shim_priv/AndroidManifest.xml) > $(out)", +} + +//########################################################## +// Variant: Privileged app + +android_app { + name: "CtsShimPriv", + // this needs to be a privileged application + privileged: true, + + sdk_version: "current", + optimize: { + enabled: false, + }, + dex_preopt: { + enabled: false, + }, + + manifest: ":generate_priv_manifest", + + compile_multilib: "both", + jni_libs: ["libshim_jni"], + // Explicitly uncompress native libs rather than letting the build system doing it and destroy the + // v2/v3 signature. + use_embedded_native_libs: true, +} + +//########################################################## +// Variant: Privileged app upgrade w/ the wrong SHA + +android_app { + name: "CtsShimPrivUpgradeWrongSHA", + // this needs to be a privileged application + privileged: true, + + sdk_version: "current", + optimize: { + enabled: false, + }, + dex_preopt: { + enabled: false, + }, + // anything to make this package's SHA different from CtsShimPrivUpgrade + aaptflags: [ + "--version-name", + "WrongSHA", + ], + + manifest: "shim_priv_upgrade/AndroidManifest.xml", + + compile_multilib: "both", + jni_libs: ["libshim_jni"], + +} + +//########################################################## +// Variant: System app + +android_app { + name: "CtsShim", + + sdk_version: "current", + optimize: { + enabled: false, + }, + dex_preopt: { + enabled: false, + }, + + manifest: "shim/AndroidManifest.xml", +} diff --git a/packages/CtsShim/build/Android.mk b/packages/CtsShim/build/Android.mk deleted file mode 100644 index 0ef4654d72e7..000000000000 --- a/packages/CtsShim/build/Android.mk +++ /dev/null @@ -1,119 +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. -# - -LOCAL_PATH := $(my-dir) - -########################################################### -# Variant: Privileged app upgrade - -include $(CLEAR_VARS) -# this needs to be a privileged application -LOCAL_PRIVILEGED_MODULE := true - -LOCAL_MODULE_TAGS := optional -LOCAL_SDK_VERSION := current -LOCAL_PROGUARD_ENABLED := disabled -LOCAL_DEX_PREOPT := false - -LOCAL_PACKAGE_NAME := CtsShimPrivUpgrade - -LOCAL_MANIFEST_FILE := shim_priv_upgrade/AndroidManifest.xml - -LOCAL_MULTILIB := both -LOCAL_JNI_SHARED_LIBRARIES := libshim_jni - -include $(BUILD_PACKAGE) -my_shim_priv_upgrade_apk := $(LOCAL_BUILT_MODULE) - -########################################################### -# Variant: Privileged app - -include $(CLEAR_VARS) -# this needs to be a privileged application -LOCAL_PRIVILEGED_MODULE := true - -LOCAL_MODULE_TAGS := optional -LOCAL_SDK_VERSION := current -LOCAL_PROGUARD_ENABLED := disabled -LOCAL_DEX_PREOPT := false - -LOCAL_PACKAGE_NAME := CtsShimPriv - -# Generate the upgrade key by taking the hash of the built CtsShimPrivUpgrade apk -gen := $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),,true)/AndroidManifest.xml -$(gen): PRIVATE_CUSTOM_TOOL = sed -e "s/__HASH__/`sha512sum $(PRIVATE_INPUT_APK) | cut -d' ' -f1`/" $< >$@ -$(gen): PRIVATE_INPUT_APK := $(my_shim_priv_upgrade_apk) -$(gen): $(LOCAL_PATH)/shim_priv/AndroidManifest.xml $(my_shim_priv_upgrade_apk) - $(transform-generated-source) - -my_shim_priv_upgrade_apk := - -LOCAL_FULL_MANIFEST_FILE := $(gen) - -LOCAL_MULTILIB := both -LOCAL_JNI_SHARED_LIBRARIES := libshim_jni -# Explicitly uncompress native libs rather than letting the build system doing it and destroy the -# v2/v3 signature. -LOCAL_USE_EMBEDDED_NATIVE_LIBS := true - -LOCAL_USE_AAPT2 := true - -include $(BUILD_PACKAGE) - -########################################################### -# Variant: Privileged app upgrade w/ the wrong SHA - -include $(CLEAR_VARS) -# this needs to be a privileged application -LOCAL_PRIVILEGED_MODULE := true - -LOCAL_MODULE_TAGS := optional -LOCAL_SDK_VERSION := current -LOCAL_PROGUARD_ENABLED := disabled -LOCAL_DEX_PREOPT := false -# anything to make this package's SHA different from CtsShimPrivUpgrade -LOCAL_AAPT_FLAGS := --version-name WrongSHA - -LOCAL_PACKAGE_NAME := CtsShimPrivUpgradeWrongSHA - -LOCAL_MANIFEST_FILE := shim_priv_upgrade/AndroidManifest.xml - -LOCAL_MULTILIB := both -LOCAL_JNI_SHARED_LIBRARIES := libshim_jni - -include $(BUILD_PACKAGE) - - -########################################################### -# Variant: System app - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional -LOCAL_SDK_VERSION := current -LOCAL_PROGUARD_ENABLED := disabled -LOCAL_DEX_PREOPT := false - -LOCAL_PACKAGE_NAME := CtsShim - -LOCAL_MANIFEST_FILE := shim/AndroidManifest.xml - -LOCAL_USE_AAPT2 := true - -include $(BUILD_PACKAGE) - -########################################################### -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index 48d34ae7ba47..af96982f5426 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -129,17 +129,17 @@ public class ExternalStorageProvider extends FileSystemProvider { } @Override - protected int enforceReadPermissionInner(Uri uri, String callingPkg, IBinder callerToken) - throws SecurityException { + protected int enforceReadPermissionInner(Uri uri, String callingPkg, + @Nullable String featureId, IBinder callerToken) throws SecurityException { enforceShellRestrictions(); - return super.enforceReadPermissionInner(uri, callingPkg, callerToken); + return super.enforceReadPermissionInner(uri, callingPkg, featureId, callerToken); } @Override - protected int enforceWritePermissionInner(Uri uri, String callingPkg, IBinder callerToken) - throws SecurityException { + protected int enforceWritePermissionInner(Uri uri, String callingPkg, + @Nullable String featureId, IBinder callerToken) throws SecurityException { enforceShellRestrictions(); - return super.enforceWritePermissionInner(uri, callingPkg, callerToken); + return super.enforceWritePermissionInner(uri, callingPkg, featureId, callerToken); } public void updateVolumes() { diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java index 0a37cc6cb6ca..99f6a92206b9 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java @@ -16,7 +16,6 @@ package com.android.packageinstaller.handheld; -import static android.os.storage.StorageManager.convert; import static android.text.format.Formatter.formatFileSize; import android.annotation.NonNull; @@ -34,8 +33,6 @@ import android.content.pm.UserInfo; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; -import android.os.storage.StorageManager; -import android.os.storage.StorageVolume; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -64,29 +61,18 @@ public class UninstallAlertDialogFragment extends DialogFragment implements * @return The number of bytes. */ private long getAppDataSizeForUser(@NonNull String pkg, @NonNull UserHandle user) { - StorageManager storageManager = getContext().getSystemService(StorageManager.class); StorageStatsManager storageStatsManager = getContext().getSystemService(StorageStatsManager.class); - - List<StorageVolume> volumes = storageManager.getStorageVolumes(); - long appDataSize = 0; - - int numVolumes = volumes.size(); - for (int i = 0; i < numVolumes; i++) { - StorageStats stats; - try { - stats = storageStatsManager.queryStatsForPackage(convert(volumes.get(i).getUuid()), - pkg, user); - } catch (PackageManager.NameNotFoundException | IOException e) { - Log.e(LOG_TAG, "Cannot determine amount of app data for " + pkg + " on " - + volumes.get(i) + " (user " + user + ")", e); - continue; - } - - appDataSize += stats.getDataBytes(); + try { + StorageStats stats = storageStatsManager.queryStatsForPackage( + getContext().getPackageManager().getApplicationInfo(pkg, 0).storageUuid, + pkg, user); + return stats.getDataBytes(); + } catch (PackageManager.NameNotFoundException | IOException e) { + Log.e(LOG_TAG, "Cannot determine amount of app data for " + pkg, e); } - return appDataSize; + return 0; } /** diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java index c4df2e8860b4..b9daf7fcab4e 100644 --- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java @@ -406,8 +406,8 @@ public class TileUtils { return null; } try { - return provider.call(context.getPackageName(), uri.getAuthority(), - method, uri.toString(), bundle); + return provider.call(context.getPackageName(), context.getFeatureId(), + uri.getAuthority(), method, uri.toString(), bundle); } catch (RemoteException e) { return null; } diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 245ca140b4f0..f25b5eb294e0 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Beskikbaar via %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Tik om aan te meld"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Gekoppel, geen internet nie"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Daar kan nie by private DNS-bediener ingegaan word nie"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Beperkte verbinding"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Geen internet nie"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Aanmelding word vereis"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index bfd31968bd6c..6332c848c5a6 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"በ%1$s በኩል የሚገኝ"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"ለመመዝገብ መታ ያድርጉ"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"ተገናኝቷል፣ ምንም በይነመረብ የለም"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"የግል ዲኤንኤስ አገልጋይ ሊደረስበት አይችልም"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"የተገደበ ግንኙነት"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"ምንም በይነመረብ የለም"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"ወደ መለያ መግባት ያስፈልጋል"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 8b67b0b1347e..8c72527604a4 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"متوفرة عبر %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"انقر للاشتراك."</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"متصلة ولكن بلا إنترنت"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"لا يمكن الوصول إلى خادم أسماء نظام نطاقات خاص"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"اتصال محدود"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"لا يتوفر اتصال إنترنت."</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"يلزم تسجيل الدخول"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index f3ca337b261d..a7ea1e020f03 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -43,6 +43,8 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$sৰ মাধ্যমেৰে উপলব্ধ"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"ছাইন আপ কৰিবলৈ টিপক"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"সংযোজিত, ইণ্টাৰনেট নাই"</string> + <!-- no translation found for private_dns_broken (7356676011023412490) --> + <skip /> <string name="wifi_limited_connection" msgid="7717855024753201527">"ইণ্টাৰনেট সংযোগ সীমিত"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"ইণ্টাৰনেট সংযোগ নাই"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"ছাইন ইন কৰা দৰকাৰী"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index d35dfe8255d2..cb7db78fd018 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s vasitəsilə əlçatandır"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Qeydiyyatdan keçmək üçün klikləyin"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Qoşuludur, internet yoxdur"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Özəl DNS serverinə giriş mümkün deyil"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Məhdud bağlantı"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"İnternet yoxdur"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Giriş tələb olunur"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 448de4b8b7dd..75feb326cd77 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Dostupna je preko pristupne tačke %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Dodirnite da biste se registrovali"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Veza je uspostavljena, nema interneta"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Pristup privatnom DNS serveru nije uspeo"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Ograničena veza"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Nema interneta"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Treba da se prijavite"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index d68c0f3e02ab..677aa24ac43d 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Даступна праз %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Націсніце, каб зарэгістравацца"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Падключана, без доступу да інтэрнэту"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Не ўдалося атрымаць доступ да прыватнага DNS-сервера"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Абмежаваныя магчымасці падключэння"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Не падключана да інтэрнэту"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Трэба выканаць уваход"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index cb99f64ba498..162042209c46 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Мрежата е достъпна през „%1$s“"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Докоснете, за да се регистрирате"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Установена е връзка – няма достъп до интернет"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Не може да се осъществи достъп до частния DNS сървър"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Ограничена връзка"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Няма връзка с интернет"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Изисква се вход в профила"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index f2f4f5249b74..b1e37a66fa82 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s এর মাধ্যমে উপলব্ধ"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"সাইন-আপ করতে ট্যাপ করুন"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"কানেক্ট, ইন্টারনেট নেই"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"ব্যক্তিগত ডিএনএস সার্ভার অ্যাক্সেস করা যাবে না"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"সীমিত কানেকশন"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"ইন্টারনেট কানেকশন নেই"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"সাইন-ইন করা দরকার"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 45b8dd99ec70..911a8315523f 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Dostupan preko %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Dodirnite za prijavu"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Povezano, nema interneta"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Nije moguće pristupiti privatnom DNS serveru"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Ograničena veza"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Nema internetske veze"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Potrebna je prijava"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index b624df056bec..58c2b670630d 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Disponible mitjançant %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Toca per registrar-te"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Connectada, sense Internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"No es pot accedir al servidor DNS privat"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Connexió limitada"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Sense connexió a Internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Cal iniciar la sessió"</string> @@ -237,7 +238,7 @@ <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="7234956835280563341">"Activa el còdec d\'àudio per Bluetooth\nSelecció: mode de canal"</string> <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Còdec LDAC d\'àudio per Bluetooth: qualitat de reproducció"</string> <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="6893955536658137179">"Activa l\'LDAC d\'àudio per Bluetooth\nSelecció de còdec: qualitat de reproducció"</string> - <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"S\'està reproduint en temps real: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string> + <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Reproducció en continu: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string> <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privat"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selecciona el mode de DNS privat"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Desactivat"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 042e12a14967..3c3d5b811d26 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Dostupné prostřednictvím %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Klepnutím se zaregistrujete"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Připojeno, není k dispozici internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Nelze získat přístup k soukromému serveru DNS"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Omezené připojení"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Nejste připojeni k internetu"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Je vyžadováno přihlášení"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 4723293f302d..bf5d6cfd472d 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Tilgængelig via %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Tryk for at registrere dig"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Tilsluttet – intet internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Der er ikke adgang til den private DNS-server"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Begrænset forbindelse"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Intet internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Login er påkrævet"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 4f5a965f9ce0..a6dbd5ae215a 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Verfügbar über %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Zum Anmelden tippen"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Verbunden, kein Internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Auf den privaten DNS-Server kann nicht zugegriffen werden"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Eingeschränkte Verbindung"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Kein Internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Anmeldung erforderlich"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 753dea8855dd..fdba74a4dfd8 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Διαθέσιμο μέσω %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Πατήστε για εγγραφή"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Συνδέθηκε, χωρίς σύνδεση στο διαδίκτυο"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Δεν είναι δυνατή η πρόσβαση στον ιδιωτικό διακομιστή DNS."</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Περιορισμένη σύνδεση"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Δεν υπάρχει σύνδεση στο διαδίκτυο"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Απαιτείται σύνδεση"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index dd3d27803db3..581adf86d062 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Available via %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Tap to sign up"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Connected, no Internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Private DNS server cannot be accessed"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Limited connection"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"No Internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Sign-in required"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index dd3d27803db3..581adf86d062 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Available via %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Tap to sign up"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Connected, no Internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Private DNS server cannot be accessed"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Limited connection"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"No Internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Sign-in required"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index dd3d27803db3..581adf86d062 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Available via %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Tap to sign up"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Connected, no Internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Private DNS server cannot be accessed"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Limited connection"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"No Internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Sign-in required"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index dd3d27803db3..581adf86d062 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Available via %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Tap to sign up"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Connected, no Internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Private DNS server cannot be accessed"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Limited connection"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"No Internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Sign-in required"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index d9f61d8188f1..e75d7bc19bbf 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Available via %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Tap to sign up"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Connected, no internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Private DNS server cannot be accessed"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Limited connection"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"No internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Sign in required"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 30cb0a1f113f..97cce55c11d8 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Disponible a través de %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Presiona para registrarte"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Conectado pero sin conexión a Internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"No se puede acceder al servidor DNS privado"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Conexión limitada"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Sin Internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Acceso obligatorio"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 32905dfb9213..7ba1a9435be2 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Disponible a través de %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Toca para registrarte"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Conexión sin Internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"No se ha podido acceder al servidor DNS privado"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Conexión limitada"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Sin Internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Debes iniciar sesión"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 79b8a848f870..0e987528d73a 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Saadaval üksuse %1$s kaudu"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Puudutage registreerumiseks"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Ühendatud, Interneti-ühendus puudub"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Privaatsele DNS-serverile ei pääse juurde"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Piiratud ühendus"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Interneti-ühendus puudub"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Nõutav on sisselogimine"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 38ae9c243f6e..872e9a56ed87 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s bidez erabilgarri"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Sakatu erregistratzeko"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Konektatuta; ezin da atzitu Internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Ezin da atzitu DNS zerbitzari pribatua"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Konexio mugatua"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Ez dago Interneteko konexiorik"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Saioa hasi behar da"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index a883d6cddf47..6e281fe05486 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"در دسترس از طریق %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"برای ثبتنام ضربه بزنید"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"متصل، بدون اینترنت"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"سرور DNS خصوصی قابل دسترسی نیست"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"اتصال محدود"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"عدم دسترسی به اینترنت"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"ورود به سیستم لازم است"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 929e101bf610..8c3630a08c9b 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Käytettävissä seuraavan kautta: %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Rekisteröidy napauttamalla"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Yhdistetty, ei internetyhteyttä"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Ei pääsyä yksityiselle DNS-palvelimelle"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Rajallinen yhteys"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Ei internetyhteyttä"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Sisäänkirjautuminen vaaditaan"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index d3bfc96ea473..6c5834ae793a 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Accessible par %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Toucher pour vous connecter"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Connecté, aucun accès à Internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Impossible d\'accéder au serveur DNS privé"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Connexion limitée"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Aucune connexion Internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Connexion requise"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index b5706cea932d..508556ece88a 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Disponible via %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Appuyez ici pour vous connecter"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Connecté, aucun accès à Internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Impossible d\'accéder au serveur DNS privé"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Connexion limitée"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Aucun accès à Internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Connexion requise"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 2aa1604cca21..1a3ae3d601e1 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Dispoñible a través de %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Toca para rexistrarte"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Conexión sen Internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Non se puido acceder ao servidor DNS privado"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Pouca conexión"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Non hai conexión a Internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"É obrigatorio iniciar sesión"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index c2cc4c5d9410..b3f518541c71 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s દ્વારા ઉપલબ્ધ"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"સાઇન અપ કરવા માટે ટૅપ કરો"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"કનેક્ટ કર્યું, કોઈ ઇન્ટરનેટ નથી"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"ખાનગી DNS સર્વર ઍક્સેસ કરી શકાતા નથી"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"મર્યાદિત કનેક્શન"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"ઇન્ટરનેટ ઍક્સેસ નથી"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"સાઇન ઇન આવશ્યક"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 66c7a16e1bf6..454773ca3538 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s के द्वारा उपलब्ध"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"साइन अप करने के लिए टैप करें"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"कनेक्ट हो गया है, लेकिन इंटरनेट नहीं है"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"निजी डीएनएस सर्वर को ऐक्सेस नहीं किया जा सकता"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"सीमित कनेक्शन"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"इंटरनेट कनेक्शन नहीं है"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"साइन इन करना ज़रूरी है"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 0fc16f847822..0ec154baa0e9 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Dostupno putem %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Dodirnite da biste se registrirali"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Povezano, bez interneta"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Nije moguće pristupiti privatnom DNS poslužitelju"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Ograničena veza"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Nema interneta"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Obavezna prijava"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 1388dbb684fa..4a0af7d27ab5 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Elérhető a következőn keresztül: %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Koppintson a regisztrációhoz"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Csatlakozva, nincs internet-hozzáférés"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"A privát DNS-kiszolgálóhoz nem lehet hozzáférni"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Korlátozott kapcsolat"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Nincs internetkapcsolat"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Bejelentkezést igényel"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 2145adb1036e..cc0ecb8125b6 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Հասանելի է %1$s-ի միջոցով"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Հպեք՝ գրանցվելու համար"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Միացված է, սակայն ինտերնետ կապ չկա"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Մասնավոր DNS սերվերն անհասանելի է"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Սահմանափակ կապ"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Ինտերնետ կապ չկա"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Անհրաժեշտ է մուտք գործել"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 7ebe6b7f8180..0733c1f76a66 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Tersedia melalui %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Ketuk untuk mendaftar"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Tersambung, tidak ada internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Server DNS pribadi tidak dapat diakses"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Koneksi terbatas"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Tidak ada internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Perlu login"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index eede11756e9e..28ed8fc6224a 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Í boði í gegnum %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Ýttu til að skrá þig"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Tengt, enginn netaðgangur"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Ekki næst í DNS-einkaþjón"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Takmörkuð tenging"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Engin nettenging"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Innskráningar krafist"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 2c64ec2e6705..b0569b0f96f2 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Disponibile tramite %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Tocca per registrarti"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Connesso, senza Internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Non è possibile accedere al server DNS privato"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Connessione limitata"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Nessuna connessione a Internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Accesso richiesto"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 30d6a4aa1d5e..f7d4fcd317ad 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -43,6 +43,8 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"זמינה דרך %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"יש להקיש כדי להירשם"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"מחובר. אין אינטרנט"</string> + <!-- no translation found for private_dns_broken (7356676011023412490) --> + <skip /> <string name="wifi_limited_connection" msgid="7717855024753201527">"חיבור מוגבל"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"אין אינטרנט"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"נדרשת כניסה"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 6cb5514cc582..4d5c86cafe3a 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s経由で使用可能"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"タップして登録してください"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"接続済み、インターネット接続なし"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"プライベート DNS サーバーにアクセスできません"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"接続が制限されています"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"インターネット未接続"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"ログインが必要"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 4c78a3864562..20342bc011cd 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"ხელმისაწვდომია %1$s-ით"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"შეეხეთ რეგისტრაციისთვის"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"დაკავშირებულია, ინტერნეტის გარეშე"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"პირად DNS სერვერზე წვდომა შეუძლებელია"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"შეზღუდული კავშირი"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"ინტერნეტ-კავშირი არ არის"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"აუცილებელია სისტემაში შესვლა"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index ce086577ec00..7b6d29eb43ad 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s арқылы қолжетімді"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Тіркелу үшін түртіңіз."</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Қосылған, интернет жоқ"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Жеке DNS серверіне кіру мүмкін емес."</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Шектеулі байланыс"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Интернетпен байланыс жоқ"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Есептік жазбаға кіру керек"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 43c92824015b..115be8e07f59 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"មានតាមរយៈ %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"ចុចដើម្បីចុះឈ្មោះ"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"បានភ្ជាប់ ប៉ុន្តែគ្មានអ៊ីនធឺណិតទេ"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"មិនអាចចូលប្រើម៉ាស៊ីនមេ DNS ឯកជនបានទេ"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"ការតភ្ជាប់មានកម្រិត"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"គ្មានអ៊ីនធឺណិតទេ"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"តម្រូវឱ្យចូលគណនី"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 253104b38661..ac8bfb1654cb 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -43,6 +43,8 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ಮೂಲಕ ಲಭ್ಯವಿದೆ"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"ಸೈನ್ ಅಪ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ, ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string> + <!-- no translation found for private_dns_broken (7356676011023412490) --> + <skip /> <string name="wifi_limited_connection" msgid="7717855024753201527">"ಸೀಮಿತ ಸಂಪರ್ಕ"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"ಸೈನ್ ಇನ್ ಮಾಡುವ ಅಗತ್ಯವಿದೆ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index ac44c0db62a3..4e543103fe60 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s을(를) 통해 사용 가능"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"탭하여 가입"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"연결됨, 인터넷 사용 불가"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"비공개 DNS 서버에 액세스할 수 없습니다."</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"제한된 연결"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"인터넷 연결 없음"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"로그인 필요"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index dd1ff30faf44..e891b5a902cb 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s аркылуу жеткиликтүү"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Катталуу үчүн таптап коюңуз"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Туташып турат, Интернет жок"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Жеке DNS сервери жеткиликсиз"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Байланыш чектелген"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Интернет жок"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Аккаунтка кирүү талап кылынат"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 28e811153ea1..406a42b2b041 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"ມີໃຫ້ຜ່ານ %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"ແຕະເພື່ອສະໝັກ"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"ເຊື່ອມຕໍ່ແລ້ວ, ບໍ່ມີອິນເຕີເນັດ"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"ບໍ່ສາມາດເຂົ້າເຖິງເຊີບເວີ DNS ສ່ວນຕົວໄດ້"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"ການເຊື່ອມຕໍ່ຈຳກັດ"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"ບໍ່ມີອິນເຕີເນັດ"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"ຈຳເປັນຕ້ອງເຂົ້າສູ່ລະບົບ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 93fcaa798998..b305fd90d1cc 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Pasiekiama naudojant „%1$s“"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Palieskite, kad prisiregistruotumėte"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Prisijungta, nėra interneto"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Privataus DNS serverio negalima pasiekti"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Ribotas ryšys"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Nėra interneto ryšio"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Reikia prisijungti"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index c12d097232f4..c9e2c1114556 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Pieejams, izmantojot %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Pieskarieties, lai reģistrētos"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Savienojums izveidots, nav piekļuves internetam"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Nevar piekļūt privātam DNS serverim."</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Ierobežots savienojums"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Nav piekļuves internetam"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Nepieciešama pierakstīšanās"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 9a76a0a8e04c..e06d414c2453 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Достапно преку %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Допрете за да се регистрирате"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Поврзана, нема интернет"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Не може да се пристапи до приватниот DNS-сервер"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Ограничена врска"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Нема интернет"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Потребно е најавување"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index bb762951d437..36e632a917f5 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s വഴി ലഭ്യം"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"സൈൻ അപ്പ് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"കണക്റ്റ് ചെയ്തു, ഇന്റർനെറ്റ് ഇല്ല"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"സ്വകാര്യ DNS സെർവർ ആക്സസ് ചെയ്യാനാവില്ല"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"പരിമിത കണക്ഷൻ"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"ഇന്റർനെറ്റ് ഇല്ല"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"സൈൻ ഇൻ ചെയ്യേണ്ടത് ആവശ്യമാണ്"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 65a8ca6c1253..1a5a0af7bd5c 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s-р боломжтой"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Бүртгүүлэхийн тулд товшино уу"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Холбогдсон хэдий ч интернет алга"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Хувийн DNS серверт хандах боломжгүй байна"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Хязгаарлагдмал холболт"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Интернэт алга"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Нэвтрэх шаардлагатай"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 3d75ad6d158f..751010759fd0 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -43,6 +43,8 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s द्वारे उपलब्ध"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"साइन अप करण्यासाठी टॅप करा"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"कनेक्ट केले, इंटरनेट नाही"</string> + <!-- no translation found for private_dns_broken (7356676011023412490) --> + <skip /> <string name="wifi_limited_connection" msgid="7717855024753201527">"मर्यादित कनेक्शन"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"इंटरनेट नाही"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"साइन इन करणे आवश्यक आहे"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 0b2a4b0879c4..82bd697a6a52 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Tersedia melalui %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Ketik untuk daftar"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Disambungkan, tiada Internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Pelayan DNS peribadi tidak boleh diakses"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Sambungan terhad"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Tiada Internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Log masuk diperlukan"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 6fde69a38767..9636f0675dd4 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s မှတစ်ဆင့်ရနိုင်သည်"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"အကောင့်ဖွင့်ရန် တို့ပါ"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"ချိတ်ဆက်ထားသည်၊ အင်တာနက်မရှိ"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"သီးသန့် ဒီအန်အက်စ် (DNS) ဆာဗာကို သုံး၍မရပါ။"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"ချိတ်ဆက်မှု ကန့်သတ်ထားသည်"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"အင်တာနက် မရှိပါ"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"လက်မှတ်ထိုးဝင်ရန် လိုအပ်သည်"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 66ad20e18839..99af7c88598a 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Tilgjengelig via %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Trykk for å registrere deg"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Tilkoblet – ingen Internett-tilgang"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Den private DNS-tjeneren kan ikke nås"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Begrenset tilkobling"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Ingen internettilkobling"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Pålogging kreves"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 860e9bf6b1b7..cdf2c7d27134 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s मार्फत उपलब्ध"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"साइन अप गर्न ट्याप गर्नुहोस्"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"जडान गरियो तर इन्टरनेट छैन"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"निजी DNS सर्भरमाथि पहुँच प्राप्त गर्न सकिँदैन"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"सीमित जडान"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"इन्टरनेटमाथिको पहुँच छैन"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"साइन इन गर्न आवश्यक छ"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 2fff3283d5fd..709e98d5b2c7 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Beschikbaar via %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Tik om aan te melden"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Verbonden, geen internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Geen toegang tot privé-DNS-server"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Beperkte verbinding"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Geen internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Inloggen vereist"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index ff2a5349f40f..abe01984e9ef 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -43,6 +43,8 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ମାଧ୍ୟମରେ ଉପଲବ୍ଧ"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"ସାଇନ୍ ଅପ୍ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"ସଂଯୁକ୍ତ, ଇଣ୍ଟର୍ନେଟ୍ ନାହିଁ"</string> + <!-- no translation found for private_dns_broken (7356676011023412490) --> + <skip /> <string name="wifi_limited_connection" msgid="7717855024753201527">"ସୀମିତ ସଂଯୋଗ"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"କୌଣସି ଇଣ୍ଟରନେଟ୍ ନାହିଁ"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"ସାଇନ୍-ଇନ୍ ଆବଶ୍ୟକ"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 0cf95753ae18..b372185be7f2 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ਰਾਹੀਂ ਉਪਲਬਧ"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"ਸਾਈਨ-ਅੱਪ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"ਕਨੈਕਟ ਕੀਤਾ, ਕੋਈ ਇੰਟਰਨੈੱਟ ਨਹੀਂ"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"ਨਿੱਜੀ ਡੋਮੇਨ ਨਾਮ ਪ੍ਰਣਾਲੀ (DNS) ਸਰਵਰ \'ਤੇ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"ਸੀਮਤ ਕਨੈਕਸ਼ਨ"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"ਇੰਟਰਨੈੱਟ ਨਹੀਂ"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"ਸਾਈਨ-ਇਨ ਲੋੜੀਂਦਾ ਹੈ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index caed8c349af7..e7a8f22b52a5 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -43,6 +43,8 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Dostępne przez %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Kliknij, by się zarejestrować"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Połączono, brak internetu"</string> + <!-- no translation found for private_dns_broken (7356676011023412490) --> + <skip /> <string name="wifi_limited_connection" msgid="7717855024753201527">"Ograniczone połączenie"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Brak internetu"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Musisz się zalogować"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 04a24f1a9175..6e75fbaa9d3f 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Disponível via %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Toque para se inscrever"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Conectada, sem Internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Não é possível acessar o servidor DNS privado"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Conexão limitada"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Sem Internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"É necessário fazer login"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index a206d1ad787c..4ee0a17cec74 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Disponível através de %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Toque para se inscrever"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Ligado, sem Internet."</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Não é possível aceder ao servidor DNS."</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Ligação limitada"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Sem Internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"É necessário iniciar sessão"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 04a24f1a9175..6e75fbaa9d3f 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Disponível via %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Toque para se inscrever"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Conectada, sem Internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Não é possível acessar o servidor DNS privado"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Conexão limitada"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Sem Internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"É necessário fazer login"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 76a56e51ef88..c5725d3262d7 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Disponibilă prin %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Atingeți pentru a vă înscrie"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Conectată, fără internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Serverul DNS privat nu poate fi accesat"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Conexiune limitată"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Fără conexiune la internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Trebuie să vă conectați"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 6e1d29ad13d8..6e98601fbcd9 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Доступно через %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Нажмите, чтобы зарегистрироваться"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Подключено, без доступа к Интернету"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Доступа к частному DNS-серверу нет."</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Подключение к сети ограничено."</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Нет подключения к Интернету"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Требуется выполнить вход."</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 186c23adaec1..0f67a6517ccc 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s හරහා ලබා ගැනීමට හැකිය"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"ලියාපදිංචි වීමට තට්ටු කරන්න"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"සම්බන්ධයි, අන්තර්ජාලය නැත"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"පුද්ගලික DNS සේවාදායකයට ප්රවේශ වීමට නොහැකිය"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"සීමිත සම්බන්ධතාව"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"අන්තර්ජාලය නැත"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"පිරීම අවශ්යයි"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 9559975cb156..e4b48487688d 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"K dispozícii prostredníctvom %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Prihláste sa klepnutím"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Pripojené, žiadny internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"K súkromnému serveru DNS sa nepodarilo získať prístup"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Obmedzené pripojenie"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Žiadny internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Vyžaduje sa prihlásenie"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 62e4ad1f91f4..3e181c2cacb0 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Na voljo prek: %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Dotaknite se, če se želite registrirati"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Vzpostavljena povezava, brez interneta"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Do zasebnega strežnika DNS ni mogoče dostopati"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Omejena povezava"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Brez internetne povezave"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Zahtevana je prijava"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 0498f3123543..53803022f388 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"E mundshme përmes %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Trokit për t\'u regjistruar"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"U lidh, por nuk ka internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Serveri privat DNS nuk mund të qaset"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Lidhje e kufizuar"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Nuk ka internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Kërkohet identifikimi"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 3157deed7127..d68c9e8a1f22 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Доступна је преко приступне тачке %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Додирните да бисте се регистровали"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Веза је успостављена, нема интернета"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Приступ приватном DNS серверу није успео"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Ограничена веза"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Нема интернета"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Треба да се пријавите"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 31517b8ab00a..45841f0ee816 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Tillgängligt via %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Tryck för att logga in"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Ansluten, inget internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Det går inte att komma åt den privata DNS-servern."</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Begränsad anslutning"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Inget internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Inloggning krävs"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index d73084ffd3e8..a8e73d75dad2 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Inapatikana kupitia %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Gusa ili ujisajili"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Imeunganishwa, hakuna intaneti"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Seva ya faragha ya DNS haiwezi kufikiwa"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Muunganisho hafifu"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Hakuna intaneti"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Unahitaji kuingia katika akaunti"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index de70d085d491..83fe954dcac8 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s வழியாகக் கிடைக்கிறது"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"பதிவு செய்யத் தட்டவும்"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"இணைக்கப்பட்டுள்ளது, ஆனால் இண்டர்நெட் இல்லை"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"தனிப்பட்ட DNS சேவையகத்தை அணுக இயலாது"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"வரம்பிற்கு உட்பட்ட இணைப்பு"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"இணைய இணைப்பு இல்லை"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"உள்நுழைய வேண்டும்"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 03ae94f39aef..ad0f673886c1 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ద్వారా అందుబాటులో ఉంది"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"సైన్ అప్ చేయడానికి నొక్కండి"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"కనెక్ట్ చేయబడింది, ఇంటర్నెట్ లేదు"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"ప్రైవేట్ DNS సర్వర్ను యాక్సెస్ చేయడం సాధ్యపడదు"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"పరిమిత కనెక్షన్"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"ఇంటర్నెట్ లేదు"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"సైన్ ఇన్ చేయాలి"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 1cfe45e72a9e..c9e232d45a85 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"พร้อมใช้งานผ่านทาง %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"แตะเพื่อลงชื่อสมัครใช้"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"เชื่อมต่อแล้ว ไม่พบอินเทอร์เน็ต"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"เข้าถึงเซิร์ฟเวอร์ DNS ไม่ได้"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"การเชื่อมต่อที่จำกัด"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"ไม่มีอินเทอร์เน็ต"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"ต้องลงชื่อเข้าใช้"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 8b3118b333d1..74307f8e3912 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Available sa pamamagitan ng %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"I-tap para mag-sign up"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Nakakonekta, walang internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Hindi ma-access ang pribadong DNS server"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Limitadong koneksyon"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Walang internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Kinakailangang mag-sign in"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 5891c791cec9..b95d941a83b3 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s üzerinden kullanılabilir"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Kaydolmak için dokunun"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Bağlı, internet yok"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Gizli DNS sunucusuna erişilemiyor"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Sınırlı bağlantı"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"İnternet yok"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Oturum açılması gerekiyor"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 0bca3077a6ad..61a0c3e6128e 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Доступ через %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Торкніться, щоб увійти"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Під’єднано, але немає доступу до Інтернету"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Немає доступу до приватного DNS-сервера"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Обмежене з’єднання"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Немає Інтернету"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Потрібно ввійти в обліковий запис"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 1e27b487ca27..f21e891fb9ae 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -43,6 +43,8 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"دستیاب بذریعہ %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"سائن اپ کے لیے تھپتھپائیں"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"منسلک، انٹرنیٹ نہیں ہے"</string> + <!-- no translation found for private_dns_broken (7356676011023412490) --> + <skip /> <string name="wifi_limited_connection" msgid="7717855024753201527">"محدود کنکشن"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"انٹرنیٹ نہیں ہے"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"سائن ان درکار ہے"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 487100c94feb..a0a79e38422f 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s orqali ishlaydi"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Yozilish uchun bosing"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Ulangan, lekin internet aloqasi yo‘q"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Xususiy DNS server ishlamayapti"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Cheklangan aloqa"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Internet yo‘q"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Hisob bilan kirish zarur"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index c247617b31dc..3723b83db79d 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Có sẵn qua %1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Nhấn để đăng ký"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Đã kết nối, không có Internet"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Không thể truy cập máy chủ DNS riêng tư"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Kết nối giới hạn"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Không có Internet"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Yêu cầu đăng nhập"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index dddc1071dd9b..f1200ee13404 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"可通过%1$s连接"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"点按即可注册"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"已连接,但无法访问互联网"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"无法访问私人 DNS 服务器"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"网络连接受限"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"无法访问互联网"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"必须登录"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 8560e22c587b..57ab472b5d3f 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"可透過 %1$s 連線"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"輕按即可登入"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"已連線,但沒有互聯網"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"無法存取私人 DNS 伺服器"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"連線受限"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"沒有互聯網連線"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"必須登入"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 404aa19c87c6..630619bba7ab 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"可透過 %1$s 使用"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"輕觸即可註冊"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"已連線,沒有網際網路"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"無法存取私人 DNS 伺服器"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"連線能力受限"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"沒有網際網路連線"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"必須登入"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 542332f10454..ede336e831f9 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -43,6 +43,7 @@ <string name="available_via_passpoint" msgid="1617440946846329613">"Iyatholakala nge-%1$s"</string> <string name="tap_to_sign_up" msgid="6449724763052579434">"Thepha ukuze ubhalisele"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"Kuxhunyiwe, ayikho i-inthanethi"</string> + <string name="private_dns_broken" msgid="7356676011023412490">"Iseva eyimfihlo ye-DNS ayikwazi ukufinyelelwa"</string> <string name="wifi_limited_connection" msgid="7717855024753201527">"Iqoqo elikhawulelwe"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"Ayikho i-inthanethi"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Ukungena ngemvume kuyadingeka"</string> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index a855741c4a27..66e89237ac6d 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -171,6 +171,9 @@ <!-- Speed label for very fast network speed --> <string name="speed_label_very_fast">Very Fast</string> + <!-- Passpoint summary for an expired passpoint [CHAR LIMIT=40] --> + <string name="wifi_passpoint_expired">Expired</string> + <!-- Summary text separator for preferences including a short description (eg. "Fast / Connected"). --> <string name="preference_summary_default_combination"><xliff:g id="state" example="ON">%1$s</xliff:g> / <xliff:g id="description" example="High accuracy mode">%2$s</xliff:g></string> diff --git a/packages/SettingsLib/search/src/com/android/settingslib/search/Indexable.java b/packages/SettingsLib/search/src/com/android/settingslib/search/Indexable.java index e68b0d1d6798..8b17ddf4c58d 100644 --- a/packages/SettingsLib/search/src/com/android/settingslib/search/Indexable.java +++ b/packages/SettingsLib/search/src/com/android/settingslib/search/Indexable.java @@ -56,6 +56,16 @@ public interface Indexable { List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled); /** + * Return a list of dynamic raw data for indexing. See {@link SearchIndexableRaw} + * + * @param context the context. + * @param enabled hint telling if the data needs to be considered into the search results + * or not. + * @return a list of {@link SearchIndexableRaw} references. Can be null. + */ + List<SearchIndexableRaw> getDynamicRawDataToIndex(Context context, boolean enabled); + + /** * Return a list of data keys that cannot be indexed. See {@link SearchIndexableRaw} * * @param context the context. diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java index 4e052f1aeca4..69f1c17f97b1 100644 --- a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java @@ -18,6 +18,7 @@ package com.android.settingslib.utils; import android.os.Handler; import android.os.Looper; +import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -64,11 +65,16 @@ public class ThreadUtils { * @Return A future of the task that can be monitored for updates or cancelled. */ public static Future postOnBackgroundThread(Runnable runnable) { - if (sThreadExecutor == null) { - sThreadExecutor = Executors.newFixedThreadPool( - Runtime.getRuntime().availableProcessors()); - } - return sThreadExecutor.submit(runnable); + return getThreadExecutor().submit(runnable); + } + + /** + * Posts callable in background using shared background thread pool. + * + * @Return A future of the task that can be monitored for updates or cancelled. + */ + public static Future postOnBackgroundThread(Callable callable) { + return getThreadExecutor().submit(callable); } /** @@ -78,4 +84,11 @@ public class ThreadUtils { getUiThreadHandler().post(runnable); } + private static synchronized ExecutorService getThreadExecutor() { + if (sThreadExecutor == null) { + sThreadExecutor = Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors()); + } + return sThreadExecutor; + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 16fd51fca0a8..ab274b5ba6c7 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -143,6 +143,16 @@ public class AccessPoint implements Comparable<AccessPoint> { int VERY_FAST = 30; } + @IntDef({PasspointConfigurationVersion.INVALID, + PasspointConfigurationVersion.NO_OSU_PROVISIONED, + PasspointConfigurationVersion.OSU_PROVISIONED}) + @Retention(RetentionPolicy.SOURCE) + public @interface PasspointConfigurationVersion { + int INVALID = 0; + int NO_OSU_PROVISIONED = 1; // R1. + int OSU_PROVISIONED = 2; // R2 or R3. + } + /** The underlying set of scan results comprising this AccessPoint. */ @GuardedBy("mLock") private final ArraySet<ScanResult> mScanResults = new ArraySet<>(); @@ -177,6 +187,9 @@ public class AccessPoint implements Comparable<AccessPoint> { static final String KEY_CARRIER_AP_EAP_TYPE = "key_carrier_ap_eap_type"; static final String KEY_CARRIER_NAME = "key_carrier_name"; static final String KEY_EAPTYPE = "eap_psktype"; + static final String KEY_SUBSCRIPTION_EXPIRATION_TIME_IN_MILLIS = + "key_subscription_expiration_time_in_millis"; + static final String KEY_PASSPOINT_CONFIGURATION_VERSION = "key_passpoint_configuration_version"; static final AtomicInteger sLastId = new AtomicInteger(0); /* @@ -251,6 +264,9 @@ public class AccessPoint implements Comparable<AccessPoint> { private String mFqdn; private String mProviderFriendlyName; private boolean mIsRoaming = false; + private long mSubscriptionExpirationTimeInMillis; + @PasspointConfigurationVersion private int mPasspointConfigurationVersion = + PasspointConfigurationVersion.INVALID; private boolean mIsCarrierAp = false; @@ -323,6 +339,13 @@ public class AccessPoint implements Comparable<AccessPoint> { if (savedState.containsKey(KEY_CARRIER_NAME)) { mCarrierName = savedState.getString(KEY_CARRIER_NAME); } + if (savedState.containsKey(KEY_SUBSCRIPTION_EXPIRATION_TIME_IN_MILLIS)) { + mSubscriptionExpirationTimeInMillis = + savedState.getLong(KEY_SUBSCRIPTION_EXPIRATION_TIME_IN_MILLIS); + } + if (savedState.containsKey(KEY_PASSPOINT_CONFIGURATION_VERSION)) { + mPasspointConfigurationVersion = savedState.getInt(KEY_PASSPOINT_CONFIGURATION_VERSION); + } update(mConfig, mInfo, mNetworkInfo); // Calculate required fields @@ -348,6 +371,12 @@ public class AccessPoint implements Comparable<AccessPoint> { mContext = context; mFqdn = config.getHomeSp().getFqdn(); mProviderFriendlyName = config.getHomeSp().getFriendlyName(); + mSubscriptionExpirationTimeInMillis = config.getSubscriptionExpirationTimeInMillis(); + if (config.isOsuProvisioned()) { + mPasspointConfigurationVersion = PasspointConfigurationVersion.OSU_PROVISIONED; + } else { + mPasspointConfigurationVersion = PasspointConfigurationVersion.NO_OSU_PROVISIONED; + } updateKey(); } @@ -991,6 +1020,10 @@ public class AccessPoint implements Comparable<AccessPoint> { return mContext.getString(R.string.saved_network, appInfo.loadLabel(pm)); } } + + if (isPasspointConfigurationR1() && isExpired()) { + return mContext.getString(R.string.wifi_passpoint_expired); + } return ""; } @@ -1021,6 +1054,10 @@ public class AccessPoint implements Comparable<AccessPoint> { * Returns the summary for the AccessPoint. */ public String getSettingsSummary(boolean convertSavedAsDisconnected) { + if (isPasspointConfigurationR1() && isExpired()) { + return mContext.getString(R.string.wifi_passpoint_expired); + } + // Update to new summary StringBuilder summary = new StringBuilder(); @@ -1167,6 +1204,30 @@ public class AccessPoint implements Comparable<AccessPoint> { } /** + * Return true if this AccessPoint is expired. + */ + public boolean isExpired() { + if (mSubscriptionExpirationTimeInMillis <= 0) { + // Expiration time not specified. + return false; + } else { + return System.currentTimeMillis() >= mSubscriptionExpirationTimeInMillis; + } + } + + public boolean isPasspointConfigurationR1() { + return mPasspointConfigurationVersion == PasspointConfigurationVersion.NO_OSU_PROVISIONED; + } + + /** + * Return true if {@link PasspointConfiguration#isOsuProvisioned} is true, this may refer to R2 + * or R3. + */ + public boolean isPasspointConfigurationOsuProvisioned() { + return mPasspointConfigurationVersion == PasspointConfigurationVersion.OSU_PROVISIONED; + } + + /** * Starts the OSU Provisioning flow. */ public void startOsuProvisioning(@Nullable WifiManager.ActionListener connectListener) { @@ -1264,6 +1325,9 @@ public class AccessPoint implements Comparable<AccessPoint> { savedState.putBoolean(KEY_IS_CARRIER_AP, mIsCarrierAp); savedState.putInt(KEY_CARRIER_AP_EAP_TYPE, mCarrierApEapType); savedState.putString(KEY_CARRIER_NAME, mCarrierName); + savedState.putLong(KEY_SUBSCRIPTION_EXPIRATION_TIME_IN_MILLIS, + mSubscriptionExpirationTimeInMillis); + savedState.putInt(KEY_PASSPOINT_CONFIGURATION_VERSION, mPasspointConfigurationVersion); } public void setListener(AccessPointListener listener) { diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index f7fc0c5fdc49..0c3254a5ed02 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -228,5 +228,6 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.GLOBAL_ACTIONS_PANEL_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.AWARE_LOCK_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.DISPLAY_DENSITY_FORCED, NON_NEGATIVE_INTEGER_VALIDATOR); + VALIDATORS.put(Secure.TAP_GESTURE, BOOLEAN_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java index 8fb879d3606b..1e75fe70becd 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java @@ -248,7 +248,7 @@ public final class DeviceConfigService extends Binder { Bundle args = new Bundle(); args.putInt(Settings.CALL_METHOD_USER_KEY, ActivityManager.getService().getCurrentUser().id); - Bundle b = provider.call(resolveCallingPackage(), Settings.AUTHORITY, + Bundle b = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY, Settings.CALL_METHOD_DELETE_CONFIG, compositeKey, args); success = (b != null && b.getInt(SettingsProvider.RESULT_ROWS_DELETED) == 1); } catch (RemoteException e) { @@ -264,7 +264,7 @@ public final class DeviceConfigService extends Binder { Bundle args = new Bundle(); args.putInt(Settings.CALL_METHOD_USER_KEY, ActivityManager.getService().getCurrentUser().id); - Bundle b = provider.call(resolveCallingPackage(), Settings.AUTHORITY, + Bundle b = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY, Settings.CALL_METHOD_LIST_CONFIG, null, args); if (b != null) { Map<String, String> flagsToValues = diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 80faf4766e36..776593590a67 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -1080,6 +1080,9 @@ public class SettingsProvider extends ContentProvider { Slog.v(LOG_TAG, "getAllConfigFlags() for " + prefix); } + DeviceConfig.enforceReadPermission(getContext(), + prefix != null ? prefix.split("/")[0] : null); + synchronized (mLock) { // Get the settings. SettingsState settingsState = mSettingsRegistry.getSettingsLocked( @@ -2112,10 +2115,7 @@ public class SettingsProvider extends ContentProvider { } private static String getSettingPrefix(Bundle args) { - String prefix = (args != null) ? args.getString(Settings.CALL_METHOD_PREFIX_KEY) : null; - // Append '/' to ensure we only match properties with this exact prefix. - // i.e. "foo" should match "foo/property" but not "foobar/property" - return prefix != null ? prefix + "/" : null; + return (args != null) ? args.getString(Settings.CALL_METHOD_PREFIX_KEY) : null; } private static boolean getSettingMakeDefault(Bundle args) { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java index 36360a31a4b7..3b3ca5b02417 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java @@ -309,7 +309,7 @@ final public class SettingsService extends Binder { try { Bundle arg = new Bundle(); arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle); - Bundle result = provider.call(resolveCallingPackage(), Settings.AUTHORITY, + Bundle result = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY, callListCommand, null, arg); lines.addAll(result.getStringArrayList(SettingsProvider.RESULT_SETTINGS_LIST)); Collections.sort(lines); @@ -334,7 +334,7 @@ final public class SettingsService extends Binder { try { Bundle arg = new Bundle(); arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle); - Bundle b = provider.call(resolveCallingPackage(), Settings.AUTHORITY, + Bundle b = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY, callGetCommand, key, arg); if (b != null) { result = b.getPairValue(); @@ -372,7 +372,7 @@ final public class SettingsService extends Binder { if (makeDefault) { arg.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true); } - provider.call(resolveCallingPackage(), Settings.AUTHORITY, + provider.call(resolveCallingPackage(), null, Settings.AUTHORITY, callPutCommand, key, arg); } catch (RemoteException e) { throw new RuntimeException("Failed in IPC", e); @@ -396,7 +396,7 @@ final public class SettingsService extends Binder { try { Bundle arg = new Bundle(); arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle); - Bundle result = provider.call(resolveCallingPackage(), Settings.AUTHORITY, + Bundle result = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY, callDeleteCommand, key, arg); return result.getInt(SettingsProvider.RESULT_ROWS_DELETED); } catch (RemoteException e) { @@ -423,7 +423,7 @@ final public class SettingsService extends Binder { } String packageName = mPackageName != null ? mPackageName : resolveCallingPackage(); arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle); - provider.call(packageName, Settings.AUTHORITY, callResetCommand, null, arg); + provider.call(packageName, null, Settings.AUTHORITY, callResetCommand, null, arg); } catch (RemoteException e) { throw new RuntimeException("Failed in IPC", e); } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 9255c8728392..10d990a3d07f 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -732,7 +732,8 @@ public class SettingsBackupTest { Settings.Secure.SILENCE_GESTURE, Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE, - Settings.Secure.FACE_UNLOCK_RE_ENROLL); + Settings.Secure.FACE_UNLOCK_RE_ENROLL, + Settings.Secure.TAP_GESTURE); @Test public void systemSettingsBackedUpOrBlacklisted() { diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 54e291f904eb..c59f342d81e1 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -180,6 +180,8 @@ <uses-permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED" /> <!-- Permission needed to invoke DynamicSystem (AOT) --> <uses-permission android:name="android.permission.INSTALL_DYNAMIC_SYSTEM" /> + <!-- Used to clean up heap dumps on boot. --> + <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.CONTROL_KEYGUARD" /> @@ -209,7 +211,7 @@ <!-- Permission required to test ExplicitHealthCheckServiceImpl. --> <uses-permission android:name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE" /> - + <!-- Permission required for CTS test - CrossProfileAppsHostSideTest --> <uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES"/> @@ -239,6 +241,11 @@ </intent-filter> </provider> + <provider android:name=".HeapDumpProvider" + android:authorities="com.android.shell.heapdump" + android:grantUriPermissions="true" + android:exported="true" /> + <activity android:name=".BugreportWarningActivity" android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight" @@ -246,6 +253,14 @@ android:excludeFromRecents="true" android:exported="false" /> + <activity android:name=".HeapDumpActivity" + android:theme="@*android:style/Theme.Translucent.NoTitleBar" + android:label="@*android:string/dump_heap_title" + android:finishOnCloseSystemDialogs="true" + android:noHistory="true" + android:excludeFromRecents="true" + android:exported="false" /> + <receiver android:name=".BugreportRequestedReceiver" android:permission="android.permission.TRIGGER_SHELL_BUGREPORT"> @@ -254,6 +269,16 @@ </intent-filter> </receiver> + <receiver + android:name=".HeapDumpReceiver" + android:permission="android.permission.DUMP"> + <intent-filter> + <action android:name="android.intent.action.BOOT_COMPLETED" /> + <action android:name="com.android.internal.intent.action.HEAP_DUMP_FINISHED" /> + <action android:name="com.android.shell.action.DELETE_HEAP_DUMP" /> + </intent-filter> + </receiver> + <service android:name=".BugreportProgressService" android:exported="false"/> diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index 1b35770ccbd7..30ad9c5d658c 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -1560,7 +1560,7 @@ public class BugreportProgressService extends Service { return false; } - private static boolean isTv(Context context) { + static boolean isTv(Context context) { return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK); } diff --git a/packages/Shell/src/com/android/shell/HeapDumpActivity.java b/packages/Shell/src/com/android/shell/HeapDumpActivity.java new file mode 100644 index 000000000000..0ff0d3353041 --- /dev/null +++ b/packages/Shell/src/com/android/shell/HeapDumpActivity.java @@ -0,0 +1,142 @@ +/* + * 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.shell; + +import static com.android.shell.HeapDumpProvider.makeUri; +import static com.android.shell.HeapDumpReceiver.ACTION_DELETE_HEAP_DUMP; +import static com.android.shell.HeapDumpReceiver.EXTRA_IS_USER_INITIATED; +import static com.android.shell.HeapDumpReceiver.EXTRA_PROCESS_NAME; +import static com.android.shell.HeapDumpReceiver.EXTRA_REPORT_PACKAGE; +import static com.android.shell.HeapDumpReceiver.EXTRA_SIZE_BYTES; + +import android.app.Activity; +import android.app.ActivityManager; +import android.app.AlertDialog; +import android.content.ActivityNotFoundException; +import android.content.ClipData; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Process; +import android.util.DebugUtils; +import android.util.Log; + +import com.android.internal.R; + +/** + * This activity is displayed when the system has collected a heap dump. + */ +public class HeapDumpActivity extends Activity { + private static final String TAG = "HeapDumpActivity"; + + static final String KEY_URI = "uri"; + + private AlertDialog mDialog; + private Uri mDumpUri; + private boolean mHandled = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + String process = getIntent().getStringExtra(EXTRA_PROCESS_NAME); + long size = getIntent().getLongExtra(EXTRA_SIZE_BYTES, 0); + final boolean isUserInitiated = getIntent().getBooleanExtra(EXTRA_IS_USER_INITIATED, false); + final int uid = getIntent().getIntExtra(Intent.EXTRA_UID, 0); + final boolean isSystemProcess = uid == Process.SYSTEM_UID; + mDumpUri = makeUri(process); + final String procDisplayName = isSystemProcess + ? getString(com.android.internal.R.string.android_system_label) + : process; + + final Intent sendIntent = new Intent(); + ClipData clip = ClipData.newUri(getContentResolver(), "Heap Dump", mDumpUri); + sendIntent.setClipData(clip); + sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + sendIntent.setType(clip.getDescription().getMimeType(0)); + sendIntent.putExtra(Intent.EXTRA_STREAM, mDumpUri); + + String directLaunchPackage = getIntent().getStringExtra(EXTRA_REPORT_PACKAGE); + if (directLaunchPackage != null) { + sendIntent.setAction(ActivityManager.ACTION_REPORT_HEAP_LIMIT); + sendIntent.setPackage(directLaunchPackage); + try { + startActivity(sendIntent); + mHandled = true; + finish(); + return; + } catch (ActivityNotFoundException e) { + Log.e(TAG, "Unable to direct launch to " + directLaunchPackage, e); + } + } + + final int messageId; + if (isUserInitiated) { + messageId = com.android.internal.R.string.dump_heap_ready_text; + } else if (isSystemProcess) { + messageId = com.android.internal.R.string.dump_heap_system_text; + } else { + messageId = com.android.internal.R.string.dump_heap_text; + } + mDialog = new AlertDialog.Builder(this, android.R.style.Theme_Material_Light_Dialog_Alert) + .setTitle(com.android.internal.R.string.dump_heap_title) + .setMessage(getString(messageId, procDisplayName, + DebugUtils.sizeValueToString(size, null))) + .setNegativeButton(android.R.string.cancel, (dialog, which) -> { + mHandled = true; + finish(); + }) + .setNeutralButton(R.string.delete, (dialog, which) -> { + mHandled = true; + Intent deleteIntent = new Intent(ACTION_DELETE_HEAP_DUMP); + deleteIntent.setClass(getApplicationContext(), HeapDumpReceiver.class); + deleteIntent.putExtra(KEY_URI, mDumpUri.toString()); + sendBroadcast(deleteIntent); + finish(); + }) + .setPositiveButton(android.R.string.ok, (dialog, which) -> { + mHandled = true; + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.setPackage(null); + startActivity(Intent.createChooser(sendIntent, + getText(com.android.internal.R.string.dump_heap_title))); + finish(); + }) + .show(); + } + + @Override + protected void onStop() { + super.onStop(); + if (!isChangingConfigurations()) { + if (!mHandled) { + Intent deleteIntent = new Intent(ACTION_DELETE_HEAP_DUMP); + deleteIntent.setClass(getApplicationContext(), HeapDumpReceiver.class); + deleteIntent.putExtra(KEY_URI, mDumpUri.toString()); + sendBroadcast(deleteIntent); + } + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (mDialog != null) { + mDialog.dismiss(); + } + } +} diff --git a/packages/Shell/src/com/android/shell/HeapDumpProvider.java b/packages/Shell/src/com/android/shell/HeapDumpProvider.java new file mode 100644 index 000000000000..3eceb9118b12 --- /dev/null +++ b/packages/Shell/src/com/android/shell/HeapDumpProvider.java @@ -0,0 +1,101 @@ +/* + * 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.shell; + +import android.annotation.NonNull; +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; +import android.os.Binder; +import android.os.ParcelFileDescriptor; +import android.os.Process; + +import java.io.File; +import java.io.FileNotFoundException; + +/** ContentProvider to write and access heap dumps. */ +public class HeapDumpProvider extends ContentProvider { + private static final String FILENAME_SUFFIX = "_javaheap.bin"; + private static final Object sLock = new Object(); + + private File mRoot; + + @Override + public boolean onCreate() { + synchronized (sLock) { + mRoot = new File(getContext().createCredentialProtectedStorageContext().getFilesDir(), + "heapdumps"); + return mRoot.mkdir(); + } + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + @Override + public String getType(Uri uri) { + return "application/octet-stream"; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + throw new UnsupportedOperationException("Insert not allowed."); + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + String path = sanitizePath(uri.getEncodedPath()); + String tag = Uri.decode(path); + return (new File(mRoot, tag)).delete() ? 1 : 0; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + throw new UnsupportedOperationException("Update not allowed."); + } + + @Override + public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { + String path = sanitizePath(uri.getEncodedPath()); + String tag = Uri.decode(path); + final int pMode; + if (Binder.getCallingUid() == Process.SYSTEM_UID) { + pMode = ParcelFileDescriptor.MODE_CREATE + | ParcelFileDescriptor.MODE_TRUNCATE + | ParcelFileDescriptor.MODE_WRITE_ONLY; + } else { + pMode = ParcelFileDescriptor.MODE_READ_ONLY; + } + + synchronized (sLock) { + return ParcelFileDescriptor.open(new File(mRoot, tag), pMode); + } + } + + @NonNull + static Uri makeUri(@NonNull String procName) { + return Uri.parse("content://com.android.shell.heapdump/" + procName + FILENAME_SUFFIX); + } + + private String sanitizePath(String path) { + return path.replaceAll("[^a-zA-Z0-9_.]", ""); + } +} diff --git a/packages/Shell/src/com/android/shell/HeapDumpReceiver.java b/packages/Shell/src/com/android/shell/HeapDumpReceiver.java new file mode 100644 index 000000000000..858c521eaed5 --- /dev/null +++ b/packages/Shell/src/com/android/shell/HeapDumpReceiver.java @@ -0,0 +1,188 @@ +/* + * 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.shell; + +import static com.android.shell.BugreportProgressService.isTv; + +import android.annotation.Nullable; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.FileUtils; +import android.os.Process; +import android.text.format.DateUtils; +import android.util.Log; + +import java.io.File; + +/** + * Receiver that handles finished heap dumps. + */ +public class HeapDumpReceiver extends BroadcastReceiver { + private static final String TAG = "HeapDumpReceiver"; + + /** + * Broadcast action to determine when to delete a specific dump heap. Must include a {@link + * HeapDumpActivity#KEY_URI} String extra. + */ + static final String ACTION_DELETE_HEAP_DUMP = "com.android.shell.action.DELETE_HEAP_DUMP"; + + /** Broadcast sent when heap dump collection has been completed. */ + private static final String ACTION_HEAP_DUMP_FINISHED = + "com.android.internal.intent.action.HEAP_DUMP_FINISHED"; + + /** The process we are reporting */ + static final String EXTRA_PROCESS_NAME = "com.android.internal.extra.heap_dump.PROCESS_NAME"; + + /** The size limit the process reached. */ + static final String EXTRA_SIZE_BYTES = "com.android.internal.extra.heap_dump.SIZE_BYTES"; + + /** Whether the user initiated the dump or not. */ + static final String EXTRA_IS_USER_INITIATED = + "com.android.internal.extra.heap_dump.IS_USER_INITIATED"; + + /** Optional name of package to directly launch. */ + static final String EXTRA_REPORT_PACKAGE = + "com.android.internal.extra.heap_dump.REPORT_PACKAGE"; + + private static final String NOTIFICATION_CHANNEL_ID = "heapdumps"; + private static final int NOTIFICATION_ID = 2019; + + /** + * Always keep heap dumps taken in the last week. + */ + private static final long MIN_KEEP_AGE_MS = DateUtils.WEEK_IN_MILLIS; + + @Override + public void onReceive(Context context, Intent intent) { + Log.d(TAG, "onReceive(): " + intent); + final String action = intent.getAction(); + if (action == null) { + Log.e(TAG, "null action received"); + return; + } + switch (action) { + case Intent.ACTION_BOOT_COMPLETED: + cleanupOldFiles(context); + break; + case ACTION_DELETE_HEAP_DUMP: + deleteHeapDump(context, intent.getStringExtra(HeapDumpActivity.KEY_URI)); + break; + case ACTION_HEAP_DUMP_FINISHED: + showDumpNotification(context, intent); + break; + } + } + + private void cleanupOldFiles(Context context) { + final PendingResult result = goAsync(); + new AsyncTask<Void, Void, Void>() { + @Override + protected Void doInBackground(Void... params) { + try { + Log.d(TAG, "Deleting from " + new File(context.getFilesDir(), "heapdumps")); + FileUtils.deleteOlderFiles(new File(context.getFilesDir(), "heapdumps"), 0, + MIN_KEEP_AGE_MS); + } catch (RuntimeException e) { + Log.e(TAG, "Couldn't delete old files", e); + } + result.finish(); + return null; + } + }.execute(); + } + + private void deleteHeapDump(Context context, @Nullable final String uri) { + if (uri == null) { + Log.e(TAG, "null URI for delete heap dump intent"); + return; + } + final PendingResult result = goAsync(); + new AsyncTask<Void, Void, Void>() { + @Override + protected Void doInBackground(Void... params) { + context.getContentResolver().delete(Uri.parse(uri), null, null); + result.finish(); + return null; + } + }.execute(); + } + + private void showDumpNotification(Context context, Intent intent) { + final boolean isUserInitiated = intent.getBooleanExtra( + EXTRA_IS_USER_INITIATED, false); + final String procName = intent.getStringExtra(EXTRA_PROCESS_NAME); + final int uid = intent.getIntExtra(Intent.EXTRA_UID, 0); + + final String reportPackage = intent.getStringExtra( + EXTRA_REPORT_PACKAGE); + final long size = intent.getLongExtra(EXTRA_SIZE_BYTES, 0); + + if (procName == null) { + Log.e(TAG, "No process name sent over"); + return; + } + + NotificationManager nm = NotificationManager.from(context); + nm.createNotificationChannel( + new NotificationChannel(NOTIFICATION_CHANNEL_ID, + "Heap dumps", + NotificationManager.IMPORTANCE_DEFAULT)); + + final int titleId = isUserInitiated + ? com.android.internal.R.string.dump_heap_ready_notification + : com.android.internal.R.string.dump_heap_notification; + final String procDisplayName = uid == Process.SYSTEM_UID + ? context.getString(com.android.internal.R.string.android_system_label) + : procName; + String text = context.getString(titleId, procDisplayName); + + Intent shareIntent = new Intent(); + shareIntent.setClassName(context, HeapDumpActivity.class.getName()); + shareIntent.putExtra(EXTRA_PROCESS_NAME, procName); + shareIntent.putExtra(EXTRA_SIZE_BYTES, size); + shareIntent.putExtra(EXTRA_IS_USER_INITIATED, isUserInitiated); + shareIntent.putExtra(Intent.EXTRA_UID, uid); + if (reportPackage != null) { + shareIntent.putExtra(EXTRA_REPORT_PACKAGE, reportPackage); + } + final Notification.Builder builder = new Notification.Builder(context, + NOTIFICATION_CHANNEL_ID) + .setSmallIcon( + isTv(context) ? R.drawable.ic_bug_report_black_24dp + : com.android.internal.R.drawable.stat_sys_adb) + .setLocalOnly(true) + .setColor(context.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setContentTitle(text) + .setTicker(text) + .setAutoCancel(true) + .setContentText(context.getText( + com.android.internal.R.string.dump_heap_notification_detail)) + .setContentIntent(PendingIntent.getActivity(context, 2, shareIntent, + PendingIntent.FLAG_UPDATE_CURRENT)); + + Log.v(TAG, "Creating share heap dump notification"); + NotificationManager.from(context).notify(NOTIFICATION_ID, builder.build()); + } +} diff --git a/packages/SystemUI/plugin/ExamplePlugin/AndroidManifest.xml b/packages/SystemUI/plugin/ExamplePlugin/AndroidManifest.xml index ff89bbcb455f..e9e844124e43 100644 --- a/packages/SystemUI/plugin/ExamplePlugin/AndroidManifest.xml +++ b/packages/SystemUI/plugin/ExamplePlugin/AndroidManifest.xml @@ -22,14 +22,16 @@ <application> <activity android:name=".PluginSettings" - android:label="@string/plugin_label"> + android:label="@string/plugin_label" + android:exported="false"> <intent-filter> <action android:name="com.android.systemui.action.PLUGIN_SETTINGS" /> </intent-filter> </activity> <service android:name=".SampleOverlayPlugin" - android:label="@string/plugin_label"> + android:label="@string/plugin_label" + android:exported="false"> <intent-filter> <action android:name="com.android.systemui.action.PLUGIN_OVERLAY" /> </intent-filter> diff --git a/packages/SystemUI/res/layout/home_controls.xml b/packages/SystemUI/res/layout/home_controls.xml index b9a6a482cf1a..69a0e872dc8c 100644 --- a/packages/SystemUI/res/layout/home_controls.xml +++ b/packages/SystemUI/res/layout/home_controls.xml @@ -3,7 +3,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/home_controls_layout" android:layout_width="match_parent" - android:layout_height="125dp" + android:layout_height="wrap_content" android:layout_gravity="@integer/notification_panel_layout_gravity" android:visibility="gone" android:padding="8dp" diff --git a/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml b/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml index 7d6ff3b16db6..69beffe0798c 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml @@ -20,7 +20,7 @@ android:id="@+id/plugin_frame" android:theme="@style/qs_theme" android:layout_width="@dimen/qs_panel_width" - android:layout_height="105dp" + android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="@dimen/notification_side_paddings" android:layout_marginLeft="@dimen/notification_side_paddings" diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index e86fb70b5bd6..66a8aded496b 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Swerwing"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index fed9b0528f7e..3f02c95f38fa 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5ጂ"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5ጂ+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"በማዛወር ላይ"</string> <string name="data_connection_edge" msgid="871835227939216682">"ኤጅ"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index b04df54b84a9..8c765ac61c0f 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"شبكة الجيل الرابع أو أحدث"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"شبكة 5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"شبكة 5G والأحدث"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"التجوال"</string> <string name="data_connection_edge" msgid="871835227939216682">"شبكة EDGE"</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 7efb03cda804..87be3eb0b4cd 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -50,8 +50,7 @@ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g>ক <xliff:g id="USB_DEVICE">%2$s</xliff:g> এক্সেছ কৰিবলৈ অনুমতি দিবনে?\nএই এপ্টোক ৰেকর্ড কৰাৰ অনুমতি দিয়া হোৱা নাই কিন্তু ই এই ইউএছবি ডিভাইচটোৰ জৰিয়তে অডিঅ\' ৰেকর্ড কৰিব পাৰে।"</string> <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g>ক <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>ত প্ৰৱেশ কৰিবলৈ অনুমতি দিবনে?"</string> <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>ক ব্যৱহাৰ কৰিবলৈ <xliff:g id="APPLICATION">%1$s</xliff:g>ক খোলেনে?"</string> - <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) --> - <skip /> + <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ব্যৱহাৰ কৰিবলৈ <xliff:g id="APPLICATION">%1$s</xliff:g>ক খুলিবনে?\nএই এপ্টোক ৰেকর্ড কৰাৰ অনুমতি দিয়া হোৱা নাই কিন্তু ই এই ইউএছবি ডিভাইচটোৰ জৰিয়তে অডিঅ\' ৰেকর্ড কৰিব পাৰে।"</string> <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>ক ব্যৱহাৰ কৰিবলৈ <xliff:g id="APPLICATION">%1$s</xliff:g>ক খোলেনে?"</string> <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ইনষ্টল হৈ থকা কোনো এপে ইউএছবি সহায়ক সামগ্ৰীটো চলাব নোৱাৰে। এই সহায়ক সামগ্ৰীৰ বিষয়ে <xliff:g id="URL">%1$s</xliff:g>ৰ জৰিয়তে অধিক জানক৷"</string> <string name="title_usb_accessory" msgid="4966265263465181372">"ইউএছবিৰ সহায়ক সামগ্ৰী"</string> @@ -189,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"এলটিই"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"এলটিই+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"ৰ\'মিং"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 9d851264cf72..a756ac080033 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Rominq"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 07c321d68565..a3232e1a36f1 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index ec73200afa3f..f97d1ec184de 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Роўмінг"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 6eeae8f00bfe..856c020fb5bf 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Роуминг"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 8e4e9a18f27c..7c3f250f997e 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"রোমিং"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index c5f94bdb83e4..62cb309f3594 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 2ae91dd9bebc..18dbd2ca4aef 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -50,8 +50,7 @@ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Vols permetre que <xliff:g id="APPLICATION">%1$s</xliff:g> accedeixi a <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nAquesta aplicació no té permís de gravació, però pot capturar àudio a través d\'aquest dispositiu USB."</string> <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Vols permetre que <xliff:g id="APPLICATION">%1$s</xliff:g> accedeixi a <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string> <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Vols obrir <xliff:g id="APPLICATION">%1$s</xliff:g> per gestionar <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string> - <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) --> - <skip /> + <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Vols permetre que <xliff:g id="APPLICATION">%1$s</xliff:g> accedeixi a <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nAquesta aplicació no té permís de gravació, però pot capturar àudio a través d\'aquest dispositiu USB."</string> <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Vols obrir <xliff:g id="APPLICATION">%1$s</xliff:g> per gestionar <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string> <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Les aplicacions instal·lades no funcionen amb l\'accessori USB. Més informació: <xliff:g id="URL">%1$s</xliff:g>"</string> <string name="title_usb_accessory" msgid="4966265263465181372">"Accessori USB"</string> @@ -111,8 +110,8 @@ <string name="accessibility_phone_button" msgid="6738112589538563574">"Telèfon"</string> <string name="accessibility_voice_assist_button" msgid="487611083884852965">"Assistència per veu"</string> <string name="accessibility_unlock_button" msgid="128158454631118828">"Desbloqueja"</string> - <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"S\'està esperant l\'empremta dactilar"</string> - <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Desbloqueja sense utilitzar l\'empremta dactilar"</string> + <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"S\'està esperant l\'empremta digital"</string> + <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Desbloqueja sense utilitzar l\'empremta digital"</string> <string name="accessibility_scanning_face" msgid="769545173211758586">"S\'està escanejant la cara"</string> <string name="accessibility_send_smart_reply" msgid="7766727839703044493">"Envia"</string> <string name="accessibility_manage_notification" msgid="2026361503393549753">"Gestiona les notificacions"</string> @@ -137,7 +136,7 @@ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Contrasenya incorrecta"</string> <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Has superat el nombre d\'intents incorrectes permesos.\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER">%d</xliff:g> segons."</string> <string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toca el sensor d\'empremtes dactilars"</string> - <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icona d\'empremta dactilar"</string> + <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icona d\'empremta digital"</string> <string name="face_dialog_looking_for_face" msgid="7049276266074494689">"S\'està cercant la teva cara…"</string> <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Icona facial"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botó de zoom de compatibilitat."</string> @@ -189,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Itinerància"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> @@ -536,7 +532,7 @@ <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"El dispositiu continuarà bloquejat fins que no el desbloquegis manualment."</string> <string name="hidden_notifications_title" msgid="7139628534207443290">"Rep notificacions més ràpidament"</string> <string name="hidden_notifications_text" msgid="2326409389088668981">"Mostra-les abans de desbloquejar"</string> - <string name="hidden_notifications_cancel" msgid="3690709735122344913">"No"</string> + <string name="hidden_notifications_cancel" msgid="3690709735122344913">"No, gràcies"</string> <string name="hidden_notifications_setup" msgid="41079514801976810">"Configura"</string> <string name="zen_mode_and_condition" msgid="4462471036429759903">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string> <string name="volume_zen_end_now" msgid="6930243045593601084">"Desactiva ara"</string> @@ -916,7 +912,7 @@ <string name="slice_permission_deny" msgid="7683681514008048807">"Denega"</string> <string name="auto_saver_title" msgid="1217959994732964228">"Toca per programar l\'estalvi de bateria"</string> <string name="auto_saver_text" msgid="2563289953551438248">"Activa\'l quan sigui probable que et quedis sense bateria"</string> - <string name="no_auto_saver_action" msgid="8086002101711328500">"No"</string> + <string name="no_auto_saver_action" msgid="8086002101711328500">"No, gràcies"</string> <string name="auto_saver_enabled_title" msgid="6726474226058316862">"S\'ha activat la programació de l\'estalvi de bateria"</string> <string name="auto_saver_enabled_text" msgid="874711029884777579">"L\'estalvi de bateria s\'activarà automàticament quan el nivell de bateria sigui inferior al <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string> <string name="open_saver_setting_action" msgid="8314624730997322529">"Configuració"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 916986f2c0a4..68800f046c6a 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 99fa80e3e77d..d3dbc6e6dcb0 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index aced5d7f56b9..15f7876c6128 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 044d39644ef2..d30aec936d5f 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Περιαγωγή"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 5bf1b0f6d94f..1e50b5a7fba4 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index a327151d984f..b2a6e6a3c237 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 5bf1b0f6d94f..1e50b5a7fba4 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 5bf1b0f6d94f..1e50b5a7fba4 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 15617b392497..568fdb551ece 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index a69de3ba4e9b..46b402eaee79 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index f96f7b96d4b1..d45bf9fa7fdb 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -50,8 +50,7 @@ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"¿Quieres que <xliff:g id="APPLICATION">%1$s</xliff:g> pueda acceder a <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nEsta aplicación no tiene permisos para grabar, pero podría captar audio a través de este dispositivo USB."</string> <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"¿Quieres permitir que <xliff:g id="APPLICATION">%1$s</xliff:g> acceda a <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string> <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"¿Quieres abrir <xliff:g id="APPLICATION">%1$s</xliff:g> para utilizar <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string> - <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) --> - <skip /> + <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"¿Quieres abrir <xliff:g id="APPLICATION">%1$s</xliff:g> para gestionar <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nEsta aplicación no tiene permisos para grabar, pero puede capturar audio mediante este dispositivo USB."</string> <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"¿Quieres abrir <xliff:g id="APPLICATION">%1$s</xliff:g> para utilizar <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string> <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Ninguna aplicación instalada funciona con este accesorio USB. Más información: <xliff:g id="URL">%1$s</xliff:g>"</string> <string name="title_usb_accessory" msgid="4966265263465181372">"Accesorio USB"</string> @@ -189,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5G E"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Itinerancia"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index b106a0101ec7..3f7c8358e207 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Rändlus"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 4e9fe24509dc..b6730cfb2dd1 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -62,7 +62,7 @@ <string name="usb_debugging_always" msgid="303335496705863070">"Eman beti ordenagailu honetatik arazteko baimena"</string> <string name="usb_debugging_allow" msgid="2272145052073254852">"Baimendu"</string> <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Ez da onartzen USB arazketa"</string> - <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Gailu honetan saioa hasita duen erabiltzaileak ezin du aktibatu USB arazketa. Eginbide hori erabiltzeko, aldatu erabiltzaile nagusira."</string> + <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Gailu honetan saioa hasita daukan erabiltzaileak ezin du aktibatu USB arazketa. Eginbide hori erabiltzeko, aldatu erabiltzaile nagusira."</string> <string name="usb_contaminant_title" msgid="206854874263058490">"Desgaitu egin da USB ataka"</string> <string name="usb_contaminant_message" msgid="7379089091591609111">"USB ataka desgaitu egin da gailua likido edo zikinkeriengandik babesteko, eta ez du hautemango osagarririk.\n\nJakinarazpen bat jasoko duzu USB ataka berriz erabiltzeko moduan dagoenean."</string> <string name="usb_port_enabled" msgid="7906141351687694867">"USB ataka gaitu da kargagailuak eta osagarriak hautemateko"</string> @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Ibiltaritza"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 1751cc9f7ba5..4f0422057fc1 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"فراگردی"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index c6c90b38b5c6..63bc925683fe 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 01427c957381..df8b8e4bd373 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Itinérance"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 852bd119151d..12e06fd05fdb 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Itinérance"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 885302e6e30b..85f8cf416629 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Itinerancia"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index b3ec665762da..95d7f413bd3d 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"રોમિંગ"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 56117ed0751e..c32ec3824389 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -50,8 +50,7 @@ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"आप <xliff:g id="APPLICATION">%1$s</xliff:g> को <xliff:g id="USB_DEVICE">%2$s</xliff:g> ऐक्सेस करने की अनुमति देना चाहते हैं?\nइस ऐप्लिकेशन को रिकॉर्ड करने की अनुमति नहीं दी गई है. हालांकि, ऐप्लिकेशन इस यूएसबी डिवाइस से ऑडियो कैप्चर कर सकता है."</string> <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> को <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> के ऐक्सेस की अनुमति दें?"</string> <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> के लिए <xliff:g id="APPLICATION">%1$s</xliff:g> खोलें?"</string> - <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) --> - <skip /> + <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> का इस्तेमाल करने के लिए <xliff:g id="APPLICATION">%1$s</xliff:g> को खोलना चाहते हैं?\n इस ऐप्लिकेशन को रिकॉर्ड करने की अनुमति नहीं दी गई है. हालांकि, ऐप्लिकेशन इस यूएसबी डिवाइस से ऑडियो कैप्चर कर सकता है."</string> <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> के लिए <xliff:g id="APPLICATION">%1$s</xliff:g> खोलें?"</string> <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"इस USB सहायक डिवाइस के साथ कोई भी इंस्टॉल ऐप्स काम नहीं करता. इस सहायक डिवाइस के बारे में यहां ज़्यादा जानें: <xliff:g id="URL">%1$s</xliff:g>"</string> <string name="title_usb_accessory" msgid="4966265263465181372">"USB सहायक साधन"</string> @@ -189,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"एलटीई"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"रोमिंग"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 96b1623b2ba3..e6338785acdf 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G i više"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index bd23d4ddc0e5..299e31857947 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Barangolás"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 2647f2f3748e..6291d5de7b23 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Ռոումինգ"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index d0b1045fd721..cf9c4e0b68ea 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 8985d2752d5a..f98b9434148e 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Reiki"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 68169ab67dd5..c08cdd3defd1 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index d0594899e9cc..fc0c64027b06 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -50,8 +50,7 @@ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"האם לאפשר לאפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g> גישה אל <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nאפליקציה זו לא קיבלה הרשאה להקליט אך יכולה לתעד אודיו באמצעות מכשיר USB זה."</string> <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"האם לתת לאפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g> גישה אל <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string> <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"האם לפתוח את <xliff:g id="APPLICATION">%1$s</xliff:g> כדי לעבוד עם <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string> - <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) --> - <skip /> + <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"לפתוח את <xliff:g id="APPLICATION">%1$s</xliff:g> לטיפול במכשיר <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nאפליקציה זו לא קיבלה הרשאה להקליט אך יכולה לתעד אודיו באמצעות מכשיר USB זה."</string> <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"האם לפתוח את <xliff:g id="APPLICATION">%1$s</xliff:g> כדי לעבוד עם <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string> <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"אין אפליקציות מותקנות הפועלות עם אביזר ה-USB. למידע נוסף על אביזר זה היכנס לכתובת <xliff:g id="URL">%1$s</xliff:g>"</string> <string name="title_usb_accessory" msgid="4966265263465181372">"אביזר USB"</string> @@ -189,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"+4G"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"+LTE"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"+G5"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"נדידה"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index e89ae5bf6bcd..5759e4b1f600 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"ローミング"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 50006dd31cfe..8c10ea4e6b70 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"როუმინგი"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 05e521ccbbca..c64671572d3b 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Роуминг"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index a628383beafc..4f87cf179f8b 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"រ៉ូមីង"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index dc4674a540a4..f208298791c0 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"ರೋಮಿಂಗ್"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 714af0c0fc6c..c57d98fc7774 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G 이상"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"로밍"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 820c6b32c210..b712d6299649 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Роуминг"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 89d37794cdf1..7440be7563d8 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"ໂຣມມິງ"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 208f49b3d55d..4a2281d1e25b 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5GE"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Tarptinklinis ryšys"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index a96fc3b57863..250563632c86 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Viesabonēšana"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index f8dea7925309..d2eab61a3afe 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Роаминг"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 66265cbbf2b3..ca79e128da3b 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -50,8 +50,7 @@ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ആക്സസ് ചെയ്യാൻ <xliff:g id="APPLICATION">%1$s</xliff:g> എന്നതിനെ അനുവദിക്കണോ?\nഈ ആപ്പിന് റെക്കോർഡ് അനുമതി നൽകിയിട്ടില്ല, എന്നാൽ ഈ USB ഉപകരണത്തിലൂടെ ഓഡിയോ ക്യാപ്ചർ ചെയ്യാനാവും."</string> <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ആക്സസ് ചെയ്യാൻ <xliff:g id="APPLICATION">%1$s</xliff:g>-നെ അനുവദിക്കണോ?"</string> <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> കൈകാര്യം ചെയ്യാൻ <xliff:g id="APPLICATION">%1$s</xliff:g> തുറക്കണോ?"</string> - <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) --> - <skip /> + <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="APPLICATION">%1$s</xliff:g> തുറന്ന് <xliff:g id="USB_DEVICE">%2$s</xliff:g> കൈകാര്യം ചെയ്യണോ?\nഈ ആപ്പിന് റെക്കോർഡ് അനുമതി നൽകിയിട്ടില്ല, എന്നാൽ ഈ USB ഉപകരണത്തിലൂടെ ഓഡിയോ ക്യാപ്ചർ ചെയ്യാനാവും."</string> <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> കൈകാര്യം ചെയ്യാൻ <xliff:g id="APPLICATION">%1$s</xliff:g> തുറക്കണോ?"</string> <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ഈ USB ആക്സസ്സറിയിൽ ഇൻസ്റ്റാളുചെയ്തവയൊന്നും പ്രവർത്തിക്കുന്നില്ല. <xliff:g id="URL">%1$s</xliff:g>-ൽ ഇതേക്കുറിച്ച് കൂടുതലറിയുക"</string> <string name="title_usb_accessory" msgid="4966265263465181372">"USB ആക്സസ്സറി"</string> @@ -189,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"റോമിംഗ്"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDG"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 00e6dcbd8871..50bb578a56d2 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Роуминг"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 671060b9feff..7324c51a8c80 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -50,8 +50,7 @@ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g> ला <xliff:g id="USB_DEVICE">%2$s</xliff:g> अॅक्सेस करण्याची अनुमती द्यायची का?\nया अॅपला रेकॉर्ड करण्याची परवानगी दिलेली नाही पण या USB डिव्हाइसद्वारे ऑडिओ कॅप्चर केला जाऊ शकतो."</string> <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> ला <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> अॅक्सेस करण्याची अनुमती द्यायची का?"</string> <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> हाताळण्यासाठी <xliff:g id="APPLICATION">%1$s</xliff:g> उघडायचे का?"</string> - <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) --> - <skip /> + <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> हँडल करण्यासाठी <xliff:g id="APPLICATION">%1$s</xliff:g> उघडायचे आहे का? \n या अॅपला रेकॉर्ड करण्याची परवानगी दिलेली नाही पण या USB डिव्हाइसद्वारे ऑडिओ कॅप्चर केला जाऊ शकतो."</string> <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> हाताळण्यासाठी <xliff:g id="APPLICATION">%1$s</xliff:g> उघडायचे का?"</string> <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"इंस्टॉल केलेली अॅप्स या USB उपसाधनासह कार्य करत नाहीत. <xliff:g id="URL">%1$s</xliff:g> येथे या उपसाधनाविषयी अधिक जाणून घ्या"</string> <string name="title_usb_accessory" msgid="4966265263465181372">"USB उपसाधन"</string> @@ -189,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"४G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"५Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"१X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"रोमिंग"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 265f1617c279..0faaa5f64356 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Perayauan"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index c06bb8cace60..566dc6632405 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်ခြင်း"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index c213b1026836..aabdd8b9cc3f 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 8006279f7ebd..ebffa2f117e7 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -50,8 +50,7 @@ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g> लाई <xliff:g id="USB_DEVICE">%2$s</xliff:g> माथि पहुँच राख्न अनुमति दिने हो?\nयो अनुप्रयोगलाई रेकर्ड गर्ने अनुमति प्रदान गरिएको छैन तर यसले USB यन्त्रमार्फत अडियो क्याप्चर गर्न सक्छ।"</string> <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> लाई <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> माथि पहुँच राख्ने अनुमति दिने हो?"</string> <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> को व्यवस्थापन गर्न <xliff:g id="APPLICATION">%1$s</xliff:g> खोल्ने हो?"</string> - <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) --> - <skip /> + <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="APPLICATION">%1$s</xliff:g> लाई <xliff:g id="USB_DEVICE">%2$s</xliff:g> सञ्चालन गर्न खोल्ने हो?\nयो अनुप्रयोगलाई रेकर्ड गर्ने अनुमति प्रदान गरिएको छैन तर यसले USB यन्त्रमार्फत अडियो क्याप्चर गर्न सक्छ।"</string> <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> को व्यवस्थापन गर्न <xliff:g id="APPLICATION">%1$s</xliff:g> खोल्ने हो?"</string> <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"यस USB उपकरणसँग स्थापित अनुप्रयोग काम गर्दैन। यस उपकरणको बारेमा <xliff:g id="URL">%1$s</xliff:g> मा धेरै जान्नुहोस्"</string> <string name="title_usb_accessory" msgid="4966265263465181372">"USB सहयोगी"</string> @@ -189,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"रोमिङ"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 61e4c33ebedb..aa68dcee0c28 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5GE"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 527d69f41150..098519654a9a 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -50,8 +50,7 @@ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ଆକ୍ସେସ୍ କରିବାକୁ <xliff:g id="APPLICATION">%1$s</xliff:g>କୁ ଅନୁମତି ଦେବେ କି?\nଏହି ଆପ୍କୁ ରେକର୍ଡ କରିବାକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ କିନ୍ତୁ ଏହି USB ଡିଭାଇସ୍ ଜରିଆରେ ଅଡିଓ କ୍ୟାପ୍ଟର୍ କରିପାରିବ।"</string> <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ଆକ୍ସେସ୍ କରିବାକୁ <xliff:g id="APPLICATION">%1$s</xliff:g>କୁ ଅନୁମତି ଦେବେ?"</string> <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ନିୟନ୍ତ୍ରଣ କରିବାକୁ <xliff:g id="APPLICATION">%1$s</xliff:g> ଖୋଲିବେ?"</string> - <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) --> - <skip /> + <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ପରିଚାଳନା କରିବାକୁ <xliff:g id="APPLICATION">%1$s</xliff:g>କୁ ଖୋଲିବେ?\nଏହି ଆପ୍କୁ ରେକର୍ଡ କରିବାକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ କିନ୍ତୁ ଏହି USB ଡିଭାଇସ୍ ଜରିଆରେ, ଏହା ଅଡିଓ କ୍ୟାପ୍ଟର୍ କରିପାରିବ।"</string> <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ନିୟନ୍ତ୍ରଣ କରିବାକୁ <xliff:g id="APPLICATION">%1$s</xliff:g> ଖୋଲିବେ?"</string> <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ଇନଷ୍ଟଲ୍ ହୋଇଥିବା କୌଣସି ଆପ୍ ଏହି USB ଆକ୍ସେସୋରୀରେ କାମ କରେନାହିଁ। ଏହି ଆକ୍ସେସୋରୀ ବିଷୟରେ <xliff:g id="URL">%1$s</xliff:g>ରେ ଅଧିକ ଜାଣନ୍ତୁ"</string> <string name="title_usb_accessory" msgid="4966265263465181372">"USB ଆକ୍ସେସରୀ"</string> @@ -189,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"ରୋମିଙ୍ଗ"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 18ddddf14ce5..cf7f3e99d433 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -50,8 +50,7 @@ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"ਕੀ <xliff:g id="APPLICATION">%1$s</xliff:g> ਨੂੰ <xliff:g id="USB_DEVICE">%2$s</xliff:g> ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?\nਇਸ ਐਪ ਨੂੰ ਰਿਕਾਰਡ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੱਤੀ ਗਈ ਪਰ ਇਹ USB ਡੀਵਾਈਸ ਰਾਹੀਂ ਆਡੀਓ ਕੈਪਚਰ ਕਰ ਸਕਦੀ ਹੈ।"</string> <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"ਕੀ <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ਤੱਕ <xliff:g id="APPLICATION">%1$s</xliff:g> ਨੂੰ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?"</string> <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"ਕੀ <xliff:g id="USB_DEVICE">%2$s</xliff:g> ਨੂੰ ਵਰਤਣ ਲਈ <xliff:g id="APPLICATION">%1$s</xliff:g> ਖੋਲ੍ਹਣੀ ਹੈ?"</string> - <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) --> - <skip /> + <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ਨੂੰ ਸੰਭਾਲਣ ਲਈ <xliff:g id="APPLICATION">%1$s</xliff:g> ਖੋਲ੍ਹੋ?\nਇਸ ਐਪ ਨੂੰ ਰਿਕਾਰਡ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੱਤੀ ਗਈ ਪਰ ਇਹ USB ਡੀਵਾਈਸ ਰਾਹੀਂ ਆਡੀਓ ਕੈਪਚਰ ਕਰ ਸਕਦੀ ਹੈ।"</string> <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"ਕੀ <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ਨੂੰ ਵਰਤਣ ਲਈ <xliff:g id="APPLICATION">%1$s</xliff:g> ਖੋਲ੍ਹਣੀ ਹੈ?"</string> <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ਕੋਈ ਇੰਸਟੌਲ ਕੀਤੇ ਐਪਸ ਇਸ USB ਐਕਸੈਸਰੀ ਨਾਲ ਕੰਮ ਨਹੀਂ ਕਰਦੇ। <xliff:g id="URL">%1$s</xliff:g> ਤੇ ਇਸ ਐਕਸੈਸਰੀ ਬਾਰੇ ਹੋਰ ਜਾਣੋ"</string> <string name="title_usb_accessory" msgid="4966265263465181372">"USB ਐਕਸੈਸਰੀ"</string> @@ -189,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"ਰੋਮਿੰਗ"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 44b41128d61d..dc79963fd92d 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -188,11 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <!-- String.format failed for translation --> - <!-- no translation found for data_connection_5ge (4699478963278829331) --> - <skip /> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 57d64f53504e..a9a03094ff99 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 6c8021182833..ef59a5e91713 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 57d64f53504e..a9a03094ff99 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index a6bd29bbec58..a4618d3e8ba4 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 3459440251a1..9574f0c91635 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5GE"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Роуминг"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index ab3dc222b8ef..2876eb90c4f0 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"රෝමිං"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index aa004b4e2f8d..a969932d6f38 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -50,7 +50,7 @@ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Povoliť aplikácii <xliff:g id="APPLICATION">%1$s</xliff:g> pristupovať k zariadeniu <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nTejto aplikácii nebolo udelené povolenie na nahrávanie, môže však snímať zvuk cez toto zariadenie USB."</string> <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Povoliť aplikácii <xliff:g id="APPLICATION">%1$s</xliff:g> prístup k zariadeniu <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string> <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Otvoriť aplikáciu <xliff:g id="APPLICATION">%1$s</xliff:g> na použitie zariadenia <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string> - <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Chcete spracovávať zariadenie <xliff:g id="USB_DEVICE">%2$s</xliff:g> pomocou aplikácie <xliff:g id="APPLICATION">%1$s</xliff:g>?\nTejto aplikácii nebolo udelené povolenie na nahrávanie, ale môže nasnímať zvuk cez toto zariadenie USB."</string> + <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Chcete otvoriť aplikáciu <xliff:g id="APPLICATION">%1$s</xliff:g> na správu zariadenia <xliff:g id="USB_DEVICE">%2$s</xliff:g> ?\nTejto aplikácii nebolo udelené povolenie na nahrávanie, ale môže nasnímať zvuk cez toto zariadenie USB."</string> <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Otvoriť aplikáciu <xliff:g id="APPLICATION">%1$s</xliff:g> na použitie zariadenia <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string> <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"S týmto zariad. USB nefunguje žiadna nainštal. aplikácia. Ďalšie informácie na <xliff:g id="URL">%1$s</xliff:g>"</string> <string name="title_usb_accessory" msgid="4966265263465181372">"Periférne zariadenie USB"</string> @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 03fa75142c29..9e4045f588d2 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Gostovanje"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 21915c9ce677..6ce621c92d5c 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 64ab59b0135c..50775d94090f 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Роминг"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 0a65cc792382..c7fb27c580df 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5GE"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 48c1fab43934..12851eb23ca4 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Mitandao ya ng\'ambo"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 7263bf642be3..0d49f273c0c9 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"ரோமிங்"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index af330f18fe21..1600df790362 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -50,8 +50,7 @@ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>యాక్సెస్ చేయడానికి <xliff:g id="APPLICATION">%1$s</xliff:g>ను అనుమతించాలా?\nఈ యాప్నకు రికార్డ్ చేసే అనుమతి మంజూరు చేయబడలేదు, కానీ ఈ USB పరికరం ద్వారా ఆడియోను క్యాప్చర్ చేయగలదు."</string> <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>ని యాక్సెస్ చేయడానికి <xliff:g id="APPLICATION">%1$s</xliff:g>ని అనుమతించాలా?"</string> <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>ని నిర్వహించడానికి <xliff:g id="APPLICATION">%1$s</xliff:g>ని తెరవాలా?"</string> - <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) --> - <skip /> + <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>ని హ్యాండిల్ చేయడానికి <xliff:g id="APPLICATION">%1$s</xliff:g>ను తెరవాలా?\nఈ యాప్కు రికార్డ్ చేసే అనుమతి మంజూరు కాలేదు, అయినా ఈ USB పరికరం ద్వారా ఆడియోను క్యాప్చర్ చేయగలదు."</string> <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>ని నిర్వహించడానికి <xliff:g id="APPLICATION">%1$s</xliff:g>ని తెరవాలా?"</string> <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ఈ USB ఉపకరణంతో ఇన్స్టాల్ చేయబడిన అనువర్తనాలు ఏవీ పని చేయవు. ఈ ఉపకరణం గురించి <xliff:g id="URL">%1$s</xliff:g>లో మరింత తెలుసుకోండి"</string> <string name="title_usb_accessory" msgid="4966265263465181372">"USB ఉపకరణం"</string> @@ -189,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"రోమింగ్"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index fc062b413cff..554009e2dd29 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5GE"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"โรมมิ่ง"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 37dd49fdf65c..309265ad6ef7 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -102,7 +102,7 @@ <string name="accessibility_back" msgid="567011538994429120">"Bumalik"</string> <string name="accessibility_home" msgid="8217216074895377641">"Home"</string> <string name="accessibility_menu" msgid="316839303324695949">"Menu"</string> - <string name="accessibility_accessibility_button" msgid="7601252764577607915">"Pagiging Naa-access"</string> + <string name="accessibility_accessibility_button" msgid="7601252764577607915">"Pagiging Accessible"</string> <string name="accessibility_rotate_button" msgid="7402949513740253006">"I-rotate ang screen"</string> <string name="accessibility_recent" msgid="5208608566793607626">"Overview"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Hanapin"</string> @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> @@ -572,7 +569,7 @@ <string name="stream_notification" msgid="2563720670905665031">"Notification"</string> <string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string> <string name="stream_dtmf" msgid="2447177903892477915">"Dual multi tone frequency"</string> - <string name="stream_accessibility" msgid="301136219144385106">"Pagiging Naa-access"</string> + <string name="stream_accessibility" msgid="301136219144385106">"Pagiging Accessible"</string> <string name="ring_toggle_title" msgid="3281244519428819576">"Mga Tawag"</string> <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Ipa-ring"</string> <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"I-vibrate"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index c052f4a1d73e..f7f4d81a6239 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Dolaşım"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index de6b6f218d5c..39b4deb4aaf1 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Роумінг"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index b361a4acc512..aa261daeaee1 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -50,8 +50,7 @@ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g> کو <xliff:g id="USB_DEVICE">%2$s</xliff:g> تک رسائی دیں؟\nاس ایپ کو ریکارڈ کی اجازت عطا نہیں کی گئی ہے مگر اس USB آلہ سے کیپچر کر سکتے ہیں۔"</string> <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> کو <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> تک رسائی حاصل کرنے کی اجازت دیں؟"</string> <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ہینڈل کرنے کیلئے <xliff:g id="APPLICATION">%1$s</xliff:g> کھولیں؟"</string> - <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) --> - <skip /> + <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> کو ہینڈل کرنے کے ليے <xliff:g id="APPLICATION">%1$s</xliff:g> کھولیں؟ \nاس ایپ کو ریکارڈ کی اجازت عطا نہیں کی گئی ہے مگر اس USB آلہ سے کیپچر کر سکتے ہیں۔"</string> <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ہینڈل کرنے کیلئے <xliff:g id="APPLICATION">%1$s</xliff:g> کھولیں؟"</string> <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"اس USB لوازم کے ساتھ کوئی انسٹال کردہ ایپس کام نہیں کرتی ہیں۔ <xliff:g id="URL">%1$s</xliff:g> پر مزید جانیں"</string> <string name="title_usb_accessory" msgid="4966265263465181372">"USB لوازم"</string> @@ -189,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"رومنگ"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 5c077d83cf61..f538853cf9cb 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Rouming"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 88849c8368ca..78bf6270c047 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G trở lên"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Chuyển vùng"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index cdc20700a406..de6ad78c4d98 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"漫游"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index a69bb7b855e1..0bf6fbf0c3e1 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5GE"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"漫遊"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index dedaebf679ad..9b3aeb2a2725 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"漫遊"</string> <string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 192bfb4dd38e..9cf931385b36 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -188,9 +188,6 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"I-LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"I-LTE+"</string> - <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> - <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> - <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> <string name="data_connection_roaming" msgid="6037232010953697354">"Iyazulazula"</string> <string name="data_connection_edge" msgid="871835227939216682">"I-EDGE"</string> diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java index 3f56ff07b24d..8825f1279796 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java @@ -67,13 +67,11 @@ public class SystemUIService extends Service { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (args != null && args.length > 0 && args[0].equals("--config")) { + dumpServices(((SystemUIApplication) getApplication()).getServices(), fd, pw, args); + + if (args == null || args.length == 0 || args[0].equals("--config")) { dumpConfig(pw); - return; } - - dumpServices(((SystemUIApplication) getApplication()).getServices(), fd, pw, args); - dumpConfig(pw); } static void dumpServices( diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt index ff4711cb208a..98d7f8b864fb 100644 --- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt +++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt @@ -23,6 +23,7 @@ import android.os.Handler import android.os.Looper import android.os.Message import android.os.UserHandle +import android.text.TextUtils import android.util.Log import android.util.SparseArray import com.android.internal.annotations.VisibleForTesting @@ -55,7 +56,8 @@ private const val DEBUG = false * a given broadcast. * * Use only for IntentFilters with actions and optionally categories. It does not support, - * permissions, schemes or data types. Cannot be used for getting sticky broadcasts. + * permissions, schemes, data types or data authorities. + * Cannot be used for getting sticky broadcasts. */ @Singleton open class BroadcastDispatcher @Inject constructor ( @@ -72,11 +74,14 @@ open class BroadcastDispatcher @Inject constructor ( * * @param receiver A receiver to dispatch the [Intent] * @param filter A filter to determine what broadcasts should be dispatched to this receiver. - * It will only take into account actions and categories for filtering. + * It will only take into account actions and categories for filtering. It must + * have at least one action. * @param handler A handler to dispatch [BroadcastReceiver.onReceive]. By default, it is the * main handler. Pass `null` to use the default. * @param user A user handle to determine which broadcast should be dispatched to this receiver. * By default, it is the current user. + * @throws IllegalArgumentException if the filter has other constraints that are not actions or + * categories or the filter has no actions. */ @JvmOverloads fun registerReceiver( @@ -85,12 +90,23 @@ open class BroadcastDispatcher @Inject constructor ( handler: Handler? = mainHandler, user: UserHandle = context.user ) { + checkFilter(filter) this.handler .obtainMessage(MSG_ADD_RECEIVER, ReceiverData(receiver, filter, handler ?: mainHandler, user)) .sendToTarget() } + private fun checkFilter(filter: IntentFilter) { + val sb = StringBuilder() + if (filter.countActions() == 0) sb.append("Filter must contain at least one action. ") + if (filter.countDataAuthorities() != 0) sb.append("Filter cannot contain DataAuthorities. ") + if (filter.countDataPaths() != 0) sb.append("Filter cannot contain DataPaths. ") + if (filter.countDataSchemes() != 0) sb.append("Filter cannot contain DataSchemes. ") + if (filter.countDataTypes() != 0) sb.append("Filter cannot contain DataTypes. ") + if (!TextUtils.isEmpty(sb)) throw IllegalArgumentException(sb.toString()) + } + /** * Unregister receiver for all users. * <br> diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java index d6d1e418240a..37a447fc1957 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java @@ -20,6 +20,7 @@ import android.app.Activity; import android.app.Service; import com.android.systemui.SystemUI; +import com.android.systemui.recents.RecentsImplementation; /** * Interface necessary to make Dagger happy. See {@link ContextComponentResolver}. @@ -29,6 +30,9 @@ public interface ContextComponentHelper { Activity resolveActivity(String className); /** Turns a classname into an instance of the class or returns null. */ + RecentsImplementation resolveRecents(String className); + + /** Turns a classname into an instance of the class or returns null. */ Service resolveService(String className); /** Turns a classname into an instance of the class or returns null. */ diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java index d7822c9fc6da..06339bd5122e 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java @@ -20,6 +20,7 @@ import android.app.Activity; import android.app.Service; import com.android.systemui.SystemUI; +import com.android.systemui.recents.RecentsImplementation; import java.util.Map; @@ -35,15 +36,17 @@ public class ContextComponentResolver implements ContextComponentHelper { private final Map<Class<?>, Provider<Activity>> mActivityCreators; private final Map<Class<?>, Provider<Service>> mServiceCreators; private final Map<Class<?>, Provider<SystemUI>> mSystemUICreators; + private final Map<Class<?>, Provider<RecentsImplementation>> mRecentsCreators; @Inject - ContextComponentResolver( - Map<Class<?>, Provider<Activity>> activityCreators, + ContextComponentResolver(Map<Class<?>, Provider<Activity>> activityCreators, Map<Class<?>, Provider<Service>> serviceCreators, - Map<Class<?>, Provider<SystemUI>> systemUICreators) { + Map<Class<?>, Provider<SystemUI>> systemUICreators, + Map<Class<?>, Provider<RecentsImplementation>> recentsCreators) { mActivityCreators = activityCreators; mServiceCreators = serviceCreators; mSystemUICreators = systemUICreators; + mRecentsCreators = recentsCreators; } /** @@ -55,6 +58,14 @@ public class ContextComponentResolver implements ContextComponentHelper { } /** + * Looks up the RecentsImplementation class name to see if Dagger has an instance of it. + */ + @Override + public RecentsImplementation resolveRecents(String className) { + return resolve(className, mRecentsCreators); + } + + /** * Looks up the Service class name to see if Dagger has an instance of it. */ @Override diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java index 4be610fcd9ee..61ded138134c 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ActivityBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java @@ -30,7 +30,7 @@ import dagger.multibindings.IntoMap; * Services and Activities that are injectable should go here. */ @Module -public abstract class ActivityBinder { +public abstract class DefaultActivityBinder { /** Inject into TunerActivity. */ @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultComponentBinder.java index 4e4c06e9d447..d8989ee624fa 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultComponentBinder.java @@ -16,16 +16,13 @@ package com.android.systemui.dagger; -import dagger.Binds; import dagger.Module; /** * Dagger Module that collects related sub-modules together. + * + * See {@link ContextComponentResolver} */ -@Module(includes = {ActivityBinder.class, ServiceBinder.class, SystemUIBinder.class}) -public abstract class ComponentBinder { - /** */ - @Binds - public abstract ContextComponentHelper bindComponentHelper( - ContextComponentResolver componentHelper); +@Module(includes = {DefaultActivityBinder.class, DefaultServiceBinder.class, SystemUIBinder.class}) +public abstract class DefaultComponentBinder { } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ServiceBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java index 1f2c0a18f928..14bb80c79e27 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ServiceBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java @@ -31,7 +31,7 @@ import dagger.multibindings.IntoMap; * Services that are injectable should go here. */ @Module -public abstract class ServiceBinder { +public abstract class DefaultServiceBinder { /** */ @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java index 6674c12ab613..9032c6fc8639 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java @@ -20,6 +20,7 @@ import com.android.systemui.ActivityStarterDelegate; import com.android.systemui.appops.AppOpsController; import com.android.systemui.appops.AppOpsControllerImpl; import com.android.systemui.classifier.FalsingManagerProxy; +import com.android.systemui.doze.DozeHost; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.FalsingManager; @@ -33,6 +34,7 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl; +import com.android.systemui.statusbar.phone.DozeServiceHost; import com.android.systemui.statusbar.phone.ManagedProfileController; import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -241,5 +243,10 @@ public abstract class DependencyBinder { /** */ @Binds - public abstract FalsingManager provideFalsingmanager(FalsingManagerProxy falsingManagerImpl); + public abstract FalsingManager provideFalsingManager(FalsingManagerProxy falsingManagerImpl); + + /** + */ + @Binds + public abstract DozeHost provideDozeHost(DozeServiceHost dozeServiceHost); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java index 738f53920b98..c3d2a1f9b7dc 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java @@ -16,19 +16,89 @@ package com.android.systemui.dagger; +import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; + +import android.content.Context; +import android.os.PowerManager; +import android.util.DisplayMetrics; + +import com.android.internal.logging.MetricsLogger; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.ForegroundServiceController; import com.android.systemui.LatencyTester; import com.android.systemui.ScreenDecorations; import com.android.systemui.SystemUI; +import com.android.systemui.UiOffloadThread; +import com.android.systemui.appops.AppOpsController; +import com.android.systemui.assist.AssistManager; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.bubbles.BubbleController; +import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.doze.DozeLog; import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.pip.PipUI; +import com.android.systemui.plugins.FalsingManager; import com.android.systemui.power.PowerUI; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsModule; +import com.android.systemui.statusbar.FeatureFlags; +import com.android.systemui.statusbar.NavigationBarController; +import com.android.systemui.statusbar.NotificationListener; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationViewHierarchyManager; +import com.android.systemui.statusbar.PulseExpansionHandler; +import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.VibratorHelper; +import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier; +import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.NewNotifPipeline; +import com.android.systemui.statusbar.notification.NotificationAlertingManager; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; +import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.logging.NotifLog; +import com.android.systemui.statusbar.notification.logging.NotificationLogger; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.phone.AutoHideController; +import com.android.systemui.statusbar.phone.BiometricUnlockController; +import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.statusbar.phone.DozeScrimController; +import com.android.systemui.statusbar.phone.DozeServiceHost; +import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; +import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.phone.LightBarController; +import com.android.systemui.statusbar.phone.LockscreenWallpaper; +import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper; +import com.android.systemui.statusbar.phone.NotificationGroupManager; +import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarIconController; +import com.android.systemui.statusbar.phone.StatusBarWindowController; +import com.android.systemui.statusbar.phone.StatusBarWindowViewController; +import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; +import com.android.systemui.statusbar.policy.UserSwitcherController; +import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.util.InjectionInflationController; import com.android.systemui.util.leak.GarbageMonitor; import com.android.systemui.volume.VolumeUI; +import javax.inject.Named; +import javax.inject.Singleton; + import dagger.Binds; +import dagger.Lazy; import dagger.Module; +import dagger.Provides; import dagger.multibindings.ClassKey; import dagger.multibindings.IntoMap; @@ -80,10 +150,151 @@ public abstract class SystemUIBinder { @ClassKey(ScreenDecorations.class) public abstract SystemUI bindScreenDecorations(ScreenDecorations sysui); + /** Inject into StatusBar. */ + @Binds + @IntoMap + @ClassKey(StatusBar.class) + public abstract SystemUI bindsStatusBar(StatusBar sysui); + /** Inject into VolumeUI. */ @Binds @IntoMap @ClassKey(VolumeUI.class) public abstract SystemUI bindVolumeUI(VolumeUI sysui); + /** + * Provides our instance of StatusBar which is considered optional. + */ + @Provides + @Singleton + static StatusBar provideStatusBar( + Context context, + FeatureFlags featureFlags, + LightBarController lightBarController, + AutoHideController autoHideController, + KeyguardUpdateMonitor keyguardUpdateMonitor, + StatusBarIconController statusBarIconController, + DozeLog dozeLog, + InjectionInflationController injectionInflationController, + PulseExpansionHandler pulseExpansionHandler, + NotificationWakeUpCoordinator notificationWakeUpCoordinator, + KeyguardBypassController keyguardBypassController, + KeyguardStateController keyguardStateController, + HeadsUpManagerPhone headsUpManagerPhone, + DynamicPrivacyController dynamicPrivacyController, + BypassHeadsUpNotifier bypassHeadsUpNotifier, + @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowNotificationLongPress, + Lazy<NewNotifPipeline> newNotifPipeline, + FalsingManager falsingManager, + BroadcastDispatcher broadcastDispatcher, + RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler, + NotificationGutsManager notificationGutsManager, + NotificationLogger notificationLogger, + NotificationEntryManager notificationEntryManager, + NotificationInterruptionStateProvider notificationInterruptionStateProvider, + NotificationViewHierarchyManager notificationViewHierarchyManager, + ForegroundServiceController foregroundServiceController, + AppOpsController appOpsController, + KeyguardViewMediator keyguardViewMediator, + ZenModeController zenModeController, + NotificationAlertingManager notificationAlertingManager, + DisplayMetrics displayMetrics, + MetricsLogger metricsLogger, + UiOffloadThread uiOffloadThread, + NotificationMediaManager notificationMediaManager, + NotificationLockscreenUserManager lockScreenUserManager, + NotificationRemoteInputManager remoteInputManager, + UserSwitcherController userSwitcherController, + NetworkController networkController, + BatteryController batteryController, + SysuiColorExtractor colorExtractor, + ScreenLifecycle screenLifecycle, + WakefulnessLifecycle wakefulnessLifecycle, + SysuiStatusBarStateController statusBarStateController, + VibratorHelper vibratorHelper, + BubbleController bubbleController, + NotificationGroupManager groupManager, + NotificationGroupAlertTransferHelper groupAlertTransferHelper, + VisualStabilityManager visualStabilityManager, + DeviceProvisionedController deviceProvisionedController, + NavigationBarController navigationBarController, + AssistManager assistManager, + NotificationListener notificationListener, + ConfigurationController configurationController, + StatusBarWindowController statusBarWindowController, + StatusBarWindowViewController.Builder statusBarWindowViewControllerBuilder, + NotifLog notifLog, + DozeParameters dozeParameters, + ScrimController scrimController, + Lazy<LockscreenWallpaper> lockscreenWallpaperLazy, + Lazy<BiometricUnlockController> biometricUnlockControllerLazy, + DozeServiceHost dozeServiceHost, + PowerManager powerManager, + DozeScrimController dozeScrimController) { + return new StatusBar( + context, + featureFlags, + lightBarController, + autoHideController, + keyguardUpdateMonitor, + statusBarIconController, + dozeLog, + injectionInflationController, + pulseExpansionHandler, + notificationWakeUpCoordinator, + keyguardBypassController, + keyguardStateController, + headsUpManagerPhone, + dynamicPrivacyController, + bypassHeadsUpNotifier, + allowNotificationLongPress, + newNotifPipeline, + falsingManager, + broadcastDispatcher, + remoteInputQuickSettingsDisabler, + notificationGutsManager, + notificationLogger, + notificationEntryManager, + notificationInterruptionStateProvider, + notificationViewHierarchyManager, + foregroundServiceController, + appOpsController, + keyguardViewMediator, + zenModeController, + notificationAlertingManager, + displayMetrics, + metricsLogger, + uiOffloadThread, + notificationMediaManager, + lockScreenUserManager, + remoteInputManager, + userSwitcherController, + networkController, + batteryController, + colorExtractor, + screenLifecycle, + wakefulnessLifecycle, + statusBarStateController, + vibratorHelper, + bubbleController, + groupManager, + groupAlertTransferHelper, + visualStabilityManager, + deviceProvisionedController, + navigationBarController, + assistManager, + notificationListener, + configurationController, + statusBarWindowController, + statusBarWindowViewControllerBuilder, + notifLog, + dozeParameters, + scrimController, + lockscreenWallpaperLazy, + biometricUnlockControllerLazy, + dozeServiceHost, + powerManager, + dozeScrimController); + } + } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java index c95b50b195b3..7b8d3bc4a121 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java @@ -21,7 +21,6 @@ import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME; import androidx.annotation.Nullable; -import com.android.systemui.SystemUI; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; import com.android.systemui.power.EnhancedEstimates; @@ -39,8 +38,6 @@ import javax.inject.Singleton; import dagger.Binds; import dagger.Module; import dagger.Provides; -import dagger.multibindings.ClassKey; -import dagger.multibindings.IntoMap; /** * A dagger module for injecting default implementations of components of System UI that may be @@ -74,11 +71,6 @@ abstract class SystemUIDefaultModule { @Binds abstract ShadeController provideShadeController(StatusBar statusBar); - @Binds - @IntoMap - @ClassKey(StatusBar.class) - public abstract SystemUI providesStatusBar(StatusBar statusBar); - @Singleton @Provides @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 4e60f194e552..5f1455f63e54 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -26,10 +26,13 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.people.PeopleHubModule; import com.android.systemui.statusbar.phone.KeyguardLiftController; +import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.sensors.AsyncSensorManager; import javax.inject.Singleton; +import dagger.Binds; +import dagger.BindsOptionalOf; import dagger.Module; import dagger.Provides; @@ -38,9 +41,12 @@ import dagger.Provides; * implementation. */ @Module(includes = {AssistModule.class, - ComponentBinder.class, PeopleHubModule.class}) public abstract class SystemUIModule { + /** */ + @Binds + public abstract ContextComponentHelper bindComponentHelper( + ContextComponentResolver componentHelper); @Singleton @Provides @@ -56,10 +62,12 @@ public abstract class SystemUIModule { keyguardUpdateMonitor); } - @Singleton @Provides static SysUiState provideSysUiState() { return new SysUiState(); } + + @BindsOptionalOf + abstract StatusBar optionalStatusBar(); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java index 113c9c845d95..83d956c22fb8 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java @@ -37,6 +37,7 @@ import dagger.Component; */ @Singleton @Component(modules = { + DefaultComponentBinder.class, DependencyProvider.class, DependencyBinder.class, SystemServicesModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/log/RichEvent.java b/packages/SystemUI/src/com/android/systemui/log/RichEvent.java index 89b7a8181c44..acf761ed3936 100644 --- a/packages/SystemUI/src/com/android/systemui/log/RichEvent.java +++ b/packages/SystemUI/src/com/android/systemui/log/RichEvent.java @@ -67,7 +67,7 @@ public abstract class RichEvent extends Event { private B mBuilder = getBuilder(); protected int mType = UNINITIALIZED; protected String mReason; - protected @Level int mLogLevel; + protected @Level int mLogLevel = VERBOSE; /** * Get the log-specific builder. diff --git a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java index a6e10e6b345b..f094cb909cf2 100644 --- a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java +++ b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java @@ -120,9 +120,9 @@ public class SysuiLog implements Dumpable { } /** - * @return user-readable string of the given event + * @return user-readable string of the given event with timestamp */ - public String eventToString(Event event) { + public String eventToTimestampedString(Event event) { StringBuilder sb = new StringBuilder(); sb.append(SysuiLog.DATE_FORMAT.format(event.getTimestamp())); sb.append(" "); @@ -131,13 +131,20 @@ public class SysuiLog implements Dumpable { } /** + * @return user-readable string of the given event without a timestamp + */ + public String eventToString(Event event) { + return event.getMessage(); + } + + /** * only call on this method if you have the mDataLock */ private void dumpTimelineLocked(PrintWriter pw) { pw.println("\tTimeline:"); for (Event event : mTimeline) { - pw.println("\t" + eventToString(event)); + pw.println("\t" + eventToTimestampedString(event)); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index a267bbb92ee7..a0ea7fae493d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -14,7 +14,6 @@ package com.android.systemui.qs; -import android.provider.Settings; import android.util.Log; import android.view.View; import android.view.View.OnAttachStateChangeListener; @@ -31,6 +30,7 @@ import com.android.systemui.qs.TouchAnimator.Builder; import com.android.systemui.qs.TouchAnimator.Listener; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; +import com.android.systemui.util.Utils; import java.util.ArrayList; import java.util.Collection; @@ -270,9 +270,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha } - int flag = Settings.System.getInt(mQsPanel.getContext().getContentResolver(), - "qs_media_player", 0); - if (flag == 1) { + if (Utils.useQsMediaPlayer(mQsPanel.getContext())) { View qsMediaView = mQsPanel.getMediaPanel(); View qqsMediaView = mQuickQsPanel.getMediaPlayer().getView(); translationXBuilder.addFloat(qsMediaView, "alpha", 0, 1); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index b48814bfd6bd..39f08650bf2c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -18,6 +18,7 @@ package com.android.systemui.qs; import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState; import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; +import static com.android.systemui.util.Utils.useQsMediaPlayer; import android.annotation.Nullable; import android.content.ComponentName; @@ -150,8 +151,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne addDivider(); // Add media carousel - int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_player", 0); - if (flag == 1) { + if (useQsMediaPlayer(context)) { HorizontalScrollView mediaScrollView = new HorizontalScrollView(mContext); mediaScrollView.setHorizontalScrollBarEnabled(false); int playerHeight = (int) getResources().getDimension(R.dimen.qs_media_height); @@ -201,8 +201,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne */ public void addMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor, View actionsContainer, StatusBarNotification notif) { - int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0); - if (flag != 1) { + if (!useQsMediaPlayer(mContext)) { // Shouldn't happen, but just in case Log.e(TAG, "Tried to add media session without player!"); return; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index dcd4633a79d2..94a1cf0c4e35 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -21,7 +21,6 @@ import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEX import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; -import android.provider.Settings; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; @@ -36,6 +35,7 @@ import com.android.systemui.plugins.qs.QSTile.State; import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; +import com.android.systemui.util.Utils; import java.util.ArrayList; import java.util.Collection; @@ -72,8 +72,7 @@ public class QuickQSPanel extends QSPanel { removeView((View) mTileLayout); } - int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_player", 0); - if (flag == 1) { + if (Utils.useQsMediaPlayer(context)) { LinearLayout mHorizontalLinearLayout = new LinearLayout(mContext); mHorizontalLinearLayout.setOrientation(LinearLayout.HORIZONTAL); mHorizontalLinearLayout.setClipChildren(false); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 592e3881ea97..16c61e60179e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -17,6 +17,7 @@ package com.android.systemui.qs; import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; +import static com.android.systemui.util.Utils.useQsMediaPlayer; import android.annotation.ColorInt; import android.app.ActivityManager; @@ -393,11 +394,10 @@ public class QuickStatusBarHeader extends RelativeLayout implements FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); - int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0); if (mQsDisabled) { lp.height = resources.getDimensionPixelSize( com.android.internal.R.dimen.quick_qs_offset_height); - } else if (flag == 1) { + } else if (useQsMediaPlayer(mContext)) { lp.height = Math.max(getMinimumHeight(), resources.getDimensionPixelSize( com.android.internal.R.dimen.quick_qs_total_height_with_media)); diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java index 958695d3b2ee..7f11e56ba168 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.trust.TrustManager; import android.content.Context; @@ -40,12 +41,22 @@ import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.phone.StatusBar; +import java.util.Optional; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import dagger.Lazy; + /** * An implementation of the Recents interface which proxies to the OverviewProxyService. */ +@Singleton public class OverviewProxyRecentsImpl implements RecentsImplementation { private final static String TAG = "OverviewProxyRecentsImpl"; + @Nullable + private final Lazy<StatusBar> mStatusBarLazy; private SysUiServiceProvider mSysUiServiceProvider; private Context mContext; @@ -53,6 +64,12 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation { private TrustManager mTrustManager; private OverviewProxyService mOverviewProxyService; + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + @Inject + public OverviewProxyRecentsImpl(Optional<Lazy<StatusBar>> statusBarLazy) { + mStatusBarLazy = statusBarLazy.orElse(null); + } + @Override public void onStart(Context context, SysUiServiceProvider sysUiServiceProvider) { mContext = context; @@ -107,9 +124,8 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation { } }; // Preload only if device for current user is unlocked - final StatusBar statusBar = mSysUiServiceProvider.getComponent(StatusBar.class); - if (statusBar != null && statusBar.isKeyguardShowing()) { - statusBar.executeRunnableDismissingKeyguard(() -> { + if (mStatusBarLazy != null && mStatusBarLazy.get().isKeyguardShowing()) { + mStatusBarLazy.get().executeRunnableDismissingKeyguard(() -> { // Flush trustmanager before checking device locked per user mTrustManager.reportKeyguardShowingChanged(); mHandler.post(toggleRecents); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java index 3efed3fe2c4d..8cd17e9a1728 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java @@ -23,7 +23,10 @@ import com.android.systemui.SysUiServiceProvider; import java.io.PrintWriter; -interface RecentsImplementation { +/** + * API for creating a Recents view. + */ +public interface RecentsImplementation { default void onStart(Context context, SysUiServiceProvider sysUiServiceProvider) {} default void onBootCompleted() {} default void onAppTransitionFinished() {} diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java index 55552850890f..f57bfad890ff 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java @@ -19,35 +19,53 @@ package com.android.systemui.recents; import android.content.Context; import com.android.systemui.R; +import com.android.systemui.dagger.ContextComponentHelper; +import dagger.Binds; import dagger.Module; import dagger.Provides; +import dagger.multibindings.ClassKey; +import dagger.multibindings.IntoMap; /** * Dagger injection module for {@link RecentsImplementation} */ @Module -public class RecentsModule { +public abstract class RecentsModule { + /** * @return The {@link RecentsImplementation} from the config. */ @Provides - public RecentsImplementation provideRecentsImpl(Context context) { + public static RecentsImplementation provideRecentsImpl(Context context, + ContextComponentHelper componentHelper) { final String clsName = context.getString(R.string.config_recentsComponent); if (clsName == null || clsName.length() == 0) { throw new RuntimeException("No recents component configured", null); } - Class<?> cls = null; - try { - cls = context.getClassLoader().loadClass(clsName); - } catch (Throwable t) { - throw new RuntimeException("Error loading recents component: " + clsName, t); - } - try { - RecentsImplementation impl = (RecentsImplementation) cls.newInstance(); - return impl; - } catch (Throwable t) { - throw new RuntimeException("Error creating recents component: " + clsName, t); + RecentsImplementation impl = componentHelper.resolveRecents(clsName); + + if (impl == null) { + Class<?> cls = null; + try { + cls = context.getClassLoader().loadClass(clsName); + } catch (Throwable t) { + throw new RuntimeException("Error loading recents component: " + clsName, t); + } + try { + impl = (RecentsImplementation) cls.newInstance(); + } catch (Throwable t) { + throw new RuntimeException("Error creating recents component: " + clsName, t); + } } + + return impl; } + + /** */ + @Binds + @IntoMap + @ClassKey(OverviewProxyRecentsImpl.class) + public abstract RecentsImplementation bindOverviewProxyRecentsImpl( + OverviewProxyRecentsImpl impl); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index 0988e347945c..d668665f062c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -163,7 +163,7 @@ public class NotificationMediaManager implements Dumpable { if (!isPlaybackActive(state.getState())) { clearCurrentMediaNotification(); } - dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */); + findAndUpdateMediaNotifications(); } } @@ -200,6 +200,16 @@ public class NotificationMediaManager implements Dumpable { mEntryManager = notificationEntryManager; notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @Override + public void onPendingEntryAdded(NotificationEntry entry) { + findAndUpdateMediaNotifications(); + } + + @Override + public void onPreEntryUpdated(NotificationEntry entry) { + findAndUpdateMediaNotifications(); + } + + @Override public void onEntryRemoved( NotificationEntry entry, NotificationVisibility visibility, @@ -272,16 +282,12 @@ public class NotificationMediaManager implements Dumpable { boolean metaDataChanged = false; synchronized (mEntryManager.getNotificationData()) { - ArrayList<NotificationEntry> activeNotifications = - mEntryManager.getNotificationData().getActiveNotifications(); - final int N = activeNotifications.size(); + Set<NotificationEntry> allNotifications = mEntryManager.getAllNotifs(); // Promote the media notification with a controller in 'playing' state, if any. NotificationEntry mediaNotification = null; MediaController controller = null; - for (int i = 0; i < N; i++) { - final NotificationEntry entry = activeNotifications.get(i); - + for (NotificationEntry entry : allNotifications) { if (entry.isMediaNotification()) { final MediaSession.Token token = entry.getSbn().getNotification().extras.getParcelable( @@ -319,8 +325,7 @@ public class NotificationMediaManager implements Dumpable { // now to see if we have one like this final String pkg = aController.getPackageName(); - for (int i = 0; i < N; i++) { - final NotificationEntry entry = activeNotifications.get(i); + for (NotificationEntry entry : allNotifications) { if (entry.getSbn().getPackageName().equals(pkg)) { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: found controller matching " diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index d6b87afc53b9..20a3e35791c4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -21,7 +21,6 @@ import android.content.res.Resources; import android.os.Handler; import android.os.Trace; import android.os.UserHandle; -import android.provider.Settings; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -40,6 +39,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.util.Assert; +import com.android.systemui.util.Utils; import java.util.ArrayList; import java.util.HashMap; @@ -146,9 +146,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle final int N = activeNotifications.size(); for (int i = 0; i < N; i++) { NotificationEntry ent = activeNotifications.get(i); - int flag = Settings.System.getInt(mContext.getContentResolver(), - "qs_media_player", 0); - boolean hideMedia = (flag == 1); + boolean hideMedia = Utils.useQsMediaPlayer(mContext); if (ent.isRowDismissed() || ent.isRowRemoved() || (ent.isMediaNotification() && hideMedia) || mBubbleController.isBubbleNotificationSuppressedFromShade(ent.getKey())) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index bde097a89f6b..404087d9b973 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -53,8 +53,10 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; @@ -561,6 +563,15 @@ public class NotificationEntryManager implements } /** + * @return all notification we're currently aware of (both pending and visible notifications) + */ + public Set<NotificationEntry> getAllNotifs() { + Set<NotificationEntry> allNotifs = new HashSet<>(mPendingNotifications.values()); + allNotifs.addAll(mNotificationData.getActiveNotifications()); + return allNotifs; + } + + /** * Gets the pending or visible notification entry with the given key. Returns null if * notification doesn't exist. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java index 9bc0ca440893..d0122c2b59b2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java @@ -28,7 +28,6 @@ import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.metrics.LogMaker; import android.os.Handler; -import android.provider.Settings; import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; @@ -48,6 +47,7 @@ import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.phone.StatusBarWindowController; +import com.android.systemui.util.Utils; import java.util.Timer; import java.util.TimerTask; @@ -182,8 +182,7 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi final MediaSession.Token token = mRow.getEntry().getSbn().getNotification().extras .getParcelable(Notification.EXTRA_MEDIA_SESSION); - int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0); - if (flag == 1) { + if (Utils.useQsMediaPlayer(mContext)) { StatusBarWindowController ctrl = Dependency.get(StatusBarWindowController.class); QuickQSPanel panel = ctrl.getStatusBarView().findViewById( com.android.systemui.R.id.quick_qs_panel); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index 033171a28c62..1e8e28fd1614 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -55,10 +55,10 @@ import javax.inject.Singleton; @Singleton public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { - private static final String TAG = "BiometricUnlockController"; + private static final String TAG = "BiometricUnlockCtrl"; private static final boolean DEBUG_BIO_WAKELOCK = KeyguardConstants.DEBUG_BIOMETRIC_WAKELOCK; private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000; - private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock wakelock"; + private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock"; @IntDef(prefix = { "MODE_" }, value = { MODE_NONE, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index 50d33a70fed5..bc482353753d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -24,7 +24,6 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.MathUtils; -import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.MainResources; import com.android.systemui.doze.AlwaysOnDisplayPolicy; @@ -188,12 +187,7 @@ public class DozeParameters implements TunerService.Tunable, return; } mControlScreenOffAnimation = controlScreenOffAnimation; - getPowerManager().setDozeAfterScreenOff(!controlScreenOffAnimation); - } - - @VisibleForTesting - protected PowerManager getPowerManager() { - return mPowerManager; + mPowerManager.setDozeAfterScreenOff(!controlScreenOffAnimation); } private boolean getBoolean(String propName, int resId) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java index fe3c04e3cda3..1ecc4899d5e7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java @@ -29,10 +29,12 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import javax.inject.Inject; +import javax.inject.Singleton; /** * Controller which handles all the doze animations of the scrims. */ +@Singleton public class DozeScrimController implements StateListener { private static final String TAG = "DozeScrimController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java new file mode 100644 index 000000000000..28543555bf4d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java @@ -0,0 +1,465 @@ +/* + * 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 com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE; +import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING; + +import android.annotation.NonNull; +import android.os.Bundle; +import android.os.PowerManager; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.assist.AssistManager; +import com.android.systemui.doze.DozeEvent; +import com.android.systemui.doze.DozeHost; +import com.android.systemui.doze.DozeLog; +import com.android.systemui.doze.DozeReceiver; +import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.statusbar.PulseExpansionHandler; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; +import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; + +import java.util.ArrayList; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import dagger.Lazy; + +/** + * Implementation of DozeHost for SystemUI. + */ +@Singleton +public final class DozeServiceHost implements DozeHost { + private static final String TAG = "DozeServiceHost"; + private final ArrayList<Callback> mCallbacks = new ArrayList<>(); + private final DozeLog mDozeLog; + private final PowerManager mPowerManager; + private boolean mAnimateWakeup; + private boolean mAnimateScreenOff; + private boolean mIgnoreTouchWhilePulsing; + private Runnable mPendingScreenOffCallback; + @VisibleForTesting + boolean mWakeLockScreenPerformsAuth = SystemProperties.getBoolean( + "persist.sysui.wake_performs_auth", true); + private boolean mDozingRequested; + private boolean mDozing; + private boolean mPulsing; + private WakefulnessLifecycle mWakefulnessLifecycle; + private final SysuiStatusBarStateController mStatusBarStateController; + private final DeviceProvisionedController mDeviceProvisionedController; + private final HeadsUpManagerPhone mHeadsUpManagerPhone; + private final BatteryController mBatteryController; + private final ScrimController mScrimController; + private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy; + private BiometricUnlockController mBiometricUnlockController; + private final KeyguardViewMediator mKeyguardViewMediator; + private final AssistManager mAssistManager; + private final DozeScrimController mDozeScrimController; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final VisualStabilityManager mVisualStabilityManager; + private final PulseExpansionHandler mPulseExpansionHandler; + private final StatusBarWindowController mStatusBarWindowController; + private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator; + private NotificationIconAreaController mNotificationIconAreaController; + private StatusBarWindowViewController mStatusBarWindowViewController; + private StatusBarWindowView mStatusBarWindow; + private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + private NotificationPanelView mNotificationPanel; + private View mAmbientIndicationContainer; + private StatusBar mStatusBar; + + @Inject + public DozeServiceHost(DozeLog dozeLog, PowerManager powerManager, + WakefulnessLifecycle wakefulnessLifecycle, + SysuiStatusBarStateController statusBarStateController, + DeviceProvisionedController deviceProvisionedController, + HeadsUpManagerPhone headsUpManagerPhone, BatteryController batteryController, + ScrimController scrimController, + Lazy<BiometricUnlockController> biometricUnlockControllerLazy, + KeyguardViewMediator keyguardViewMediator, + AssistManager assistManager, + DozeScrimController dozeScrimController, KeyguardUpdateMonitor keyguardUpdateMonitor, + VisualStabilityManager visualStabilityManager, + PulseExpansionHandler pulseExpansionHandler, + StatusBarWindowController statusBarWindowController, + NotificationWakeUpCoordinator notificationWakeUpCoordinator) { + super(); + mDozeLog = dozeLog; + mPowerManager = powerManager; + mWakefulnessLifecycle = wakefulnessLifecycle; + mStatusBarStateController = statusBarStateController; + mDeviceProvisionedController = deviceProvisionedController; + mHeadsUpManagerPhone = headsUpManagerPhone; + mBatteryController = batteryController; + mScrimController = scrimController; + mBiometricUnlockControllerLazy = biometricUnlockControllerLazy; + mKeyguardViewMediator = keyguardViewMediator; + mAssistManager = assistManager; + mDozeScrimController = dozeScrimController; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mVisualStabilityManager = visualStabilityManager; + mPulseExpansionHandler = pulseExpansionHandler; + mStatusBarWindowController = statusBarWindowController; + mNotificationWakeUpCoordinator = notificationWakeUpCoordinator; + } + + // TODO: we should try to not pass status bar in here if we can avoid it. + + /** + * Initialize instance with objects only available later during execution. + */ + public void initialize(StatusBar statusBar, + NotificationIconAreaController notificationIconAreaController, + StatusBarWindowViewController statusBarWindowViewController, + StatusBarWindowView statusBarWindow, + StatusBarKeyguardViewManager statusBarKeyguardViewManager, + NotificationPanelView notificationPanel, View ambientIndicationContainer) { + mStatusBar = statusBar; + mNotificationIconAreaController = notificationIconAreaController; + mStatusBarWindowViewController = statusBarWindowViewController; + mStatusBarWindow = statusBarWindow; + mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; + mNotificationPanel = notificationPanel; + mAmbientIndicationContainer = ambientIndicationContainer; + mBiometricUnlockController = mBiometricUnlockControllerLazy.get(); + } + + @Override + public String toString() { + return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]"; + } + + void firePowerSaveChanged(boolean active) { + for (Callback callback : mCallbacks) { + callback.onPowerSaveChanged(active); + } + } + + void fireNotificationPulse(NotificationEntry entry) { + Runnable pulseSuppressedListener = () -> { + entry.setPulseSuppressed(true); + mNotificationIconAreaController.updateAodNotificationIcons(); + }; + for (Callback callback : mCallbacks) { + callback.onNotificationAlerted(pulseSuppressedListener); + } + } + + boolean getDozingRequested() { + return mDozingRequested; + } + + boolean isPulsing() { + return mPulsing; + } + + + @Override + public void addCallback(@NonNull Callback callback) { + mCallbacks.add(callback); + } + + @Override + public void removeCallback(@NonNull Callback callback) { + mCallbacks.remove(callback); + } + + @Override + public void startDozing() { + if (!mDozingRequested) { + mDozingRequested = true; + mDozeLog.traceDozing(mDozing); + updateDozing(); + mStatusBar.updateIsKeyguard(); + } + } + + void updateDozing() { + // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked. + boolean + dozing = + mDozingRequested && mStatusBarStateController.getState() == StatusBarState.KEYGUARD + || mBiometricUnlockController.getMode() + == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING; + // When in wake-and-unlock we may not have received a change to StatusBarState + // but we still should not be dozing, manually set to false. + if (mBiometricUnlockController.getMode() + == BiometricUnlockController.MODE_WAKE_AND_UNLOCK) { + dozing = false; + } + + + mStatusBarStateController.setIsDozing(dozing); + } + + @Override + public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) { + if (reason == DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS) { + mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE, + "com.android.systemui:LONG_PRESS"); + mAssistManager.startAssist(new Bundle()); + return; + } + + if (reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) { + mScrimController.setWakeLockScreenSensorActive(true); + } + + if (reason == DozeEvent.PULSE_REASON_DOCKING && mStatusBarWindow != null) { + mStatusBarWindowViewController.suppressWakeUpGesture(true); + } + + boolean passiveAuthInterrupt = reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN + && mWakeLockScreenPerformsAuth; + // Set the state to pulsing, so ScrimController will know what to do once we ask it to + // execute the transition. The pulse callback will then be invoked when the scrims + // are black, indicating that StatusBar is ready to present the rest of the UI. + mPulsing = true; + mDozeScrimController.pulse(new PulseCallback() { + @Override + public void onPulseStarted() { + callback.onPulseStarted(); + mStatusBar.updateNotificationPanelTouchState(); + setPulsing(true); + } + + @Override + public void onPulseFinished() { + mPulsing = false; + callback.onPulseFinished(); + mStatusBar.updateNotificationPanelTouchState(); + mScrimController.setWakeLockScreenSensorActive(false); + if (mStatusBarWindow != null) { + mStatusBarWindowViewController.suppressWakeUpGesture(false); + } + setPulsing(false); + } + + private void setPulsing(boolean pulsing) { + mStatusBarStateController.setPulsing(pulsing); + mStatusBarKeyguardViewManager.setPulsing(pulsing); + mKeyguardViewMediator.setPulsing(pulsing); + mNotificationPanel.setPulsing(pulsing); + mVisualStabilityManager.setPulsing(pulsing); + mStatusBarWindowViewController.setPulsing(pulsing); + mIgnoreTouchWhilePulsing = false; + if (mKeyguardUpdateMonitor != null && passiveAuthInterrupt) { + mKeyguardUpdateMonitor.onAuthInterruptDetected(pulsing /* active */); + } + mStatusBar.updateScrimController(); + mPulseExpansionHandler.setPulsing(pulsing); + mNotificationWakeUpCoordinator.setPulsing(pulsing); + } + }, reason); + // DozeScrimController is in pulse state, now let's ask ScrimController to start + // pulsing and draw the black frame, if necessary. + mStatusBar.updateScrimController(); + } + + @Override + public void stopDozing() { + if (mDozingRequested) { + mDozingRequested = false; + mDozeLog.traceDozing(mDozing); + updateDozing(); + } + } + + @Override + public void onIgnoreTouchWhilePulsing(boolean ignore) { + if (ignore != mIgnoreTouchWhilePulsing) { + mDozeLog.tracePulseTouchDisabledByProx(ignore); + } + mIgnoreTouchWhilePulsing = ignore; + if (mDozing && ignore) { + mStatusBarWindowViewController.cancelCurrentTouch(); + } + } + + @Override + public void dozeTimeTick() { + mNotificationPanel.dozeTimeTick(); + if (mAmbientIndicationContainer instanceof DozeReceiver) { + ((DozeReceiver) mAmbientIndicationContainer).dozeTimeTick(); + } + } + + @Override + public boolean isPowerSaveActive() { + return mBatteryController.isAodPowerSave(); + } + + @Override + public boolean isPulsingBlocked() { + return mBiometricUnlockController.getMode() + == BiometricUnlockController.MODE_WAKE_AND_UNLOCK; + } + + @Override + public boolean isProvisioned() { + return mDeviceProvisionedController.isDeviceProvisioned() + && mDeviceProvisionedController.isCurrentUserSetup(); + } + + @Override + public boolean isBlockingDoze() { + if (mBiometricUnlockController.hasPendingAuthentication()) { + Log.i(StatusBar.TAG, "Blocking AOD because fingerprint has authenticated"); + return true; + } + return false; + } + + @Override + public void extendPulse(int reason) { + if (reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) { + mScrimController.setWakeLockScreenSensorActive(true); + } + if (mDozeScrimController.isPulsing() && mHeadsUpManagerPhone.hasNotifications()) { + mHeadsUpManagerPhone.extendHeadsUp(); + } else { + mDozeScrimController.extendPulse(); + } + } + + @Override + public void stopPulsing() { + if (mDozeScrimController.isPulsing()) { + mDozeScrimController.pulseOutNow(); + } + } + + @Override + public void setAnimateWakeup(boolean animateWakeup) { + if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE + || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING) { + // Too late to change the wakeup animation. + return; + } + mAnimateWakeup = animateWakeup; + } + + @Override + public void setAnimateScreenOff(boolean animateScreenOff) { + mAnimateScreenOff = animateScreenOff; + } + + @Override + public void onSlpiTap(float screenX, float screenY) { + if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null + && mAmbientIndicationContainer.getVisibility() == View.VISIBLE) { + int[] locationOnScreen = new int[2]; + mAmbientIndicationContainer.getLocationOnScreen(locationOnScreen); + float viewX = screenX - locationOnScreen[0]; + float viewY = screenY - locationOnScreen[1]; + if (0 <= viewX && viewX <= mAmbientIndicationContainer.getWidth() + && 0 <= viewY && viewY <= mAmbientIndicationContainer.getHeight()) { + + // Dispatch a tap + long now = SystemClock.elapsedRealtime(); + MotionEvent ev = MotionEvent.obtain( + now, now, MotionEvent.ACTION_DOWN, screenX, screenY, 0); + mAmbientIndicationContainer.dispatchTouchEvent(ev); + ev.recycle(); + ev = MotionEvent.obtain( + now, now, MotionEvent.ACTION_UP, screenX, screenY, 0); + mAmbientIndicationContainer.dispatchTouchEvent(ev); + ev.recycle(); + } + } + } + + @Override + public void setDozeScreenBrightness(int value) { + mStatusBarWindowController.setDozeScreenBrightness(value); + } + + @Override + public void setAodDimmingScrim(float scrimOpacity) { + mScrimController.setAodFrontScrimAlpha(scrimOpacity); + } + + + + @Override + public void prepareForGentleSleep(Runnable onDisplayOffCallback) { + if (mPendingScreenOffCallback != null) { + Log.w(TAG, "Overlapping onDisplayOffCallback. Ignoring previous one."); + } + mPendingScreenOffCallback = onDisplayOffCallback; + mStatusBar.updateScrimController(); + } + + @Override + public void cancelGentleSleep() { + mPendingScreenOffCallback = null; + if (mScrimController.getState() == ScrimState.OFF) { + mStatusBar.updateScrimController(); + } + } + + /** + * When the dozing host is waiting for scrims to fade out to change the display state. + */ + boolean hasPendingScreenOffCallback() { + return mPendingScreenOffCallback != null; + } + + /** + * Executes an nullifies the pending display state callback. + * + * @see #hasPendingScreenOffCallback() + * @see #prepareForGentleSleep(Runnable) + */ + void executePendingScreenOffCallback() { + if (mPendingScreenOffCallback == null) { + return; + } + mPendingScreenOffCallback.run(); + mPendingScreenOffCallback = null; + } + + boolean shouldAnimateWakeup() { + return mAnimateWakeup; + } + + boolean shouldAnimateScreenOff() { + return mAnimateScreenOff; + } + + public void setDozing(boolean dozing) { + mDozing = dozing; + } + + boolean getIgnoreTouchWhilePulsing() { + return mIgnoreTouchWhilePulsing; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 179375e31dd3..4e91e4c84e99 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -240,7 +240,7 @@ public class KeyguardClockPositionAlgorithm { * @return Alpha from 0 to 1. */ private float getClockAlpha(int y) { - float alphaKeyguard = Math.max(0, y / Math.max(1f, getExpandedPreferredClockY())); + float alphaKeyguard = Math.max(0, y / Math.max(1f, getClockY(1f))); alphaKeyguard = Interpolators.ACCELERATE.getInterpolation(alphaKeyguard); return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount); } 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 e00cfb16bd67..467df375e7eb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -109,6 +109,7 @@ import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.util.InjectionInflationController; +import com.android.systemui.util.Utils; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -769,8 +770,7 @@ public class NotificationPanelView extends PanelView implements int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings); int topMargin = res.getDimensionPixelOffset(com.android.internal.R.dimen.quick_qs_total_height); - int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0); - if (flag == 1) { + if (Utils.useQsMediaPlayer(mContext)) { topMargin = res.getDimensionPixelOffset( com.android.internal.R.dimen.quick_qs_total_height_with_media); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 6064fbedf63d..f21a9a2eba88 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -60,11 +60,13 @@ import java.lang.annotation.RetentionPolicy; import java.util.function.Consumer; import javax.inject.Inject; +import javax.inject.Singleton; /** * Controls both the scrim behind the notifications and in front of the notifications (when a * security method gets shown). */ +@Singleton public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnColorsChangedListener, Dumpable { @@ -748,6 +750,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } private void onFinished(Callback callback) { + if (mPendingFrameCallback != null) { + // No animations can finish while we're waiting on the blanking to finish + return; + + } if (isAnimating(mScrimBehind) || isAnimating(mScrimInFront) || isAnimating(mScrimForBubble)) { 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 2e0fbfa6ea0b..001599ff37c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -50,7 +50,6 @@ import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityOptions; @@ -157,10 +156,9 @@ import com.android.systemui.bubbles.BubbleController; import com.android.systemui.charging.WirelessChargingAnimation; import com.android.systemui.classifier.FalsingLog; import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.doze.DozeEvent; +import com.android.systemui.dagger.SystemUIBinder; import com.android.systemui.doze.DozeHost; import com.android.systemui.doze.DozeLog; -import com.android.systemui.doze.DozeReceiver; import com.android.systemui.fragments.ExtensionFragmentListener; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.keyguard.KeyguardSliceProvider; @@ -249,14 +247,11 @@ import java.io.StringWriter; import java.util.ArrayList; import java.util.Map; -import javax.inject.Inject; import javax.inject.Named; -import javax.inject.Singleton; import dagger.Lazy; import dagger.Subcomponent; -@Singleton public class StatusBar extends SystemUI implements DemoMode, ActivityStarter, KeyguardStateController.Callback, OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback, @@ -347,7 +342,7 @@ public class StatusBar extends SystemUI implements DemoMode, /** * The {@link StatusBarState} of the status bar. */ - protected int mState; + protected int mState; // TODO: remove this. Just use StatusBarStateController protected boolean mBouncerShowing; private PhoneStatusBarPolicy mIconPolicy; @@ -373,7 +368,7 @@ public class StatusBar extends SystemUI implements DemoMode, protected StatusBarWindowController mStatusBarWindowController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @VisibleForTesting - DozeServiceHost mDozeServiceHost = new DozeServiceHost(); + DozeServiceHost mDozeServiceHost; private boolean mWakeUpComingFromTouch; private PointF mWakeUpTouchLocation; @@ -493,7 +488,6 @@ public class StatusBar extends SystemUI implements DemoMode, private final UiOffloadThread mUiOffloadThread; protected boolean mDozing; - private boolean mDozingRequested; private final NotificationMediaManager mMediaManager; private final NotificationLockscreenUserManager mLockscreenUserManager; @@ -612,7 +606,6 @@ public class StatusBar extends SystemUI implements DemoMode, private ActivityLaunchAnimator mActivityLaunchAnimator; protected StatusBarNotificationPresenter mPresenter; private NotificationActivityStarter mNotificationActivityStarter; - private boolean mPulsing; private final BubbleController mBubbleController; private final BubbleController.BubbleExpandListener mBubbleExpandListener; @@ -631,7 +624,12 @@ public class StatusBar extends SystemUI implements DemoMode, AppOpsManager.OP_COARSE_LOCATION, AppOpsManager.OP_FINE_LOCATION}; - @Inject + /** + * Public constructor for StatusBar. + * + * StatusBar is considered optional, and therefore can not be marked as @Inject directly. + * Instead, an @Provide method is included in {@link SystemUIBinder}. + */ public StatusBar( Context context, FeatureFlags featureFlags, @@ -692,7 +690,10 @@ public class StatusBar extends SystemUI implements DemoMode, DozeParameters dozeParameters, ScrimController scrimController, Lazy<LockscreenWallpaper> lockscreenWallpaperLazy, - Lazy<BiometricUnlockController> biometricUnlockControllerLazy) { + Lazy<BiometricUnlockController> biometricUnlockControllerLazy, + DozeServiceHost dozeServiceHost, + PowerManager powerManager, + DozeScrimController dozeScrimController) { super(context); mFeatureFlags = featureFlags; mLightBarController = lightBarController; @@ -749,9 +750,12 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarWindowController = statusBarWindowController; mStatusBarWindowViewControllerBuilder = statusBarWindowViewControllerBuilder; mNotifLog = notifLog; + mDozeServiceHost = dozeServiceHost; + mPowerManager = powerManager; mDozeParameters = dozeParameters; mScrimController = scrimController; mLockscreenWallpaperLazy = lockscreenWallpaperLazy; + mDozeScrimController = dozeScrimController; mBiometricUnlockControllerLazy = biometricUnlockControllerLazy; mBubbleExpandListener = @@ -804,7 +808,6 @@ public class StatusBar extends SystemUI implements DemoMode, mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); - mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController); mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); @@ -904,6 +907,9 @@ public class StatusBar extends SystemUI implements DemoMode, startKeyguard(); mKeyguardUpdateMonitor.registerCallback(mUpdateCallback); + mDozeServiceHost.initialize(this, mNotificationIconAreaController, + mStatusBarWindowViewController, mStatusBarWindow, mStatusBarKeyguardViewManager, + mNotificationPanel, mAmbientIndicationContainer); putComponent(DozeHost.class, mDozeServiceHost); mScreenPinningRequest = new ScreenPinningRequest(mContext); @@ -1068,7 +1074,6 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationPanel.initDependencies(this, mGroupManager, mNotificationShelf, mHeadsUpManager, mNotificationIconAreaController, mScrimController); - mDozeScrimController = new DozeScrimController(mDozeParameters, mDozeLog); BackDropView backdrop = mStatusBarWindow.findViewById(R.id.backdrop); mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front), @@ -1526,7 +1531,6 @@ public class StatusBar extends SystemUI implements DemoMode, .start(); } } - mMediaManager.findAndUpdateMediaNotifications(); } private void updateReportRejectedTouchVisibility() { @@ -1729,7 +1733,7 @@ public class StatusBar extends SystemUI implements DemoMode, if (isDozing() && isHeadsUp) { entry.setPulseSuppressed(false); mDozeServiceHost.fireNotificationPulse(entry); - if (mPulsing) { + if (mDozeServiceHost.isPulsing()) { mDozeScrimController.cancelPendingPulseTimeout(); } } @@ -1761,7 +1765,7 @@ public class StatusBar extends SystemUI implements DemoMode, } public boolean isPulsing() { - return mPulsing; + return mDozeServiceHost.isPulsing(); } public boolean hideStatusBarIconsWhenExpanded() { @@ -2824,7 +2828,7 @@ public class StatusBar extends SystemUI implements DemoMode, if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP && mKeyguardStateController.canDismissLockScreen() && !mStatusBarStateController.leaveOpenOnKeyguardHide() - && isPulsing()) { + && mDozeServiceHost.isPulsing()) { // Reuse the biometric wake-and-unlock transition if we dismiss keyguard from a pulse. // TODO: Factor this transition out of BiometricUnlockController. mBiometricUnlockController.startWakeAndUnlock( @@ -3155,7 +3159,7 @@ public class StatusBar extends SystemUI implements DemoMode, return mState == StatusBarState.FULLSCREEN_USER_SWITCHER; } - private boolean updateIsKeyguard() { + boolean updateIsKeyguard() { boolean wakeAndUnlocking = mBiometricUnlockController.getMode() == BiometricUnlockController.MODE_WAKE_AND_UNLOCK; @@ -3163,8 +3167,8 @@ public class StatusBar extends SystemUI implements DemoMode, // there's no surface we can show to the user. Note that the device goes fully interactive // late in the transition, so we also allow the device to start dozing once the screen has // turned off fully. - boolean keyguardForDozing = mDozingRequested && - (!mDeviceInteractive || isGoingToSleep() && (isScreenFullyOff() || mIsKeyguard)); + boolean keyguardForDozing = mDozeServiceHost.getDozingRequested() + && (!mDeviceInteractive || isGoingToSleep() && (isScreenFullyOff() || mIsKeyguard)); boolean shouldBeKeyguard = (mStatusBarStateController.isKeyguardRequested() || keyguardForDozing) && !wakeAndUnlocking; if (keyguardForDozing) { @@ -3580,7 +3584,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void onStateChanged(int newState) { mState = newState; updateReportRejectedTouchVisibility(); - updateDozing(); + mDozeServiceHost.updateDozing(); updateTheme(); mNavigationBarController.touchAutoDim(mDisplayId); Trace.beginSection("StatusBar#updateKeyguardState"); @@ -3618,9 +3622,11 @@ public class StatusBar extends SystemUI implements DemoMode, public void onDozingChanged(boolean isDozing) { Trace.beginSection("StatusBar#updateDozing"); mDozing = isDozing; + mDozeServiceHost.setDozing(mDozing); // Collapse the notification panel if open - boolean dozingAnimated = mDozingRequested && mDozeParameters.shouldControlScreenOff(); + boolean dozingAnimated = mDozeServiceHost.getDozingRequested() + && mDozeParameters.shouldControlScreenOff(); mNotificationPanel.resetViews(dozingAnimated); updateQsExpansionEnabled(); @@ -3628,26 +3634,12 @@ public class StatusBar extends SystemUI implements DemoMode, mEntryManager.updateNotifications("onDozingChanged"); updateDozingState(); + mDozeServiceHost.updateDozing(); updateScrimController(); updateReportRejectedTouchVisibility(); Trace.endSection(); } - private void updateDozing() { - // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked. - boolean dozing = mDozingRequested && mState == StatusBarState.KEYGUARD - || mBiometricUnlockController.getMode() - == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING; - // When in wake-and-unlock we may not have received a change to mState - // but we still should not be dozing, manually set to false. - if (mBiometricUnlockController.getMode() == - BiometricUnlockController.MODE_WAKE_AND_UNLOCK) { - dozing = false; - } - - mStatusBarStateController.setIsDozing(dozing); - } - private void updateKeyguardState() { mKeyguardStateController.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(), mStatusBarKeyguardViewManager.isOccluded()); @@ -3872,10 +3864,11 @@ public class StatusBar extends SystemUI implements DemoMode, * collapse the panel after we expanded it, and thus we would end up with a blank * Keyguard. */ - private void updateNotificationPanelTouchState() { + void updateNotificationPanelTouchState() { boolean goingToSleepWithoutAnimation = isGoingToSleep() && !mDozeParameters.shouldControlScreenOff(); - boolean disabled = (!mDeviceInteractive && !mPulsing) || goingToSleepWithoutAnimation; + boolean disabled = (!mDeviceInteractive && !mDozeServiceHost.isPulsing()) + || goingToSleepWithoutAnimation; mNotificationPanel.setTouchAndAnimationDisabled(disabled); mNotificationIconAreaController.setAnimationsEnabled(!disabled); } @@ -4025,7 +4018,7 @@ public class StatusBar extends SystemUI implements DemoMode, } public void notifyBiometricAuthModeChanged() { - updateDozing(); + mDozeServiceHost.updateDozing(); updateScrimController(); mStatusBarWindowViewController.onBiometricAuthModeChanged( mBiometricUnlockController.isWakeAndUnlock(), @@ -4061,7 +4054,7 @@ public class StatusBar extends SystemUI implements DemoMode, mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback); } else if (mBrightnessMirrorVisible) { mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR); - } else if (isPulsing()) { + } else if (mDozeServiceHost.isPulsing()) { mScrimController.transitionTo(ScrimState.PULSING, mDozeScrimController.getScrimCallback()); } else if (mDozeServiceHost.hasPendingScreenOffCallback()) { @@ -4091,295 +4084,8 @@ public class StatusBar extends SystemUI implements DemoMode, return mStatusBarKeyguardViewManager.isShowing(); } - @VisibleForTesting - final class DozeServiceHost implements DozeHost { - private final ArrayList<Callback> mCallbacks = new ArrayList<>(); - private boolean mAnimateWakeup; - private boolean mAnimateScreenOff; - private boolean mIgnoreTouchWhilePulsing; - private Runnable mPendingScreenOffCallback; - @VisibleForTesting - boolean mWakeLockScreenPerformsAuth = SystemProperties.getBoolean( - "persist.sysui.wake_performs_auth", true); - - @Override - public String toString() { - return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]"; - } - - public void firePowerSaveChanged(boolean active) { - for (Callback callback : mCallbacks) { - callback.onPowerSaveChanged(active); - } - } - - public void fireNotificationPulse(NotificationEntry entry) { - Runnable pulseSupressedListener = () -> { - entry.setPulseSuppressed(true); - mNotificationIconAreaController.updateAodNotificationIcons(); - }; - for (Callback callback : mCallbacks) { - callback.onNotificationAlerted(pulseSupressedListener); - } - } - - @Override - public void addCallback(@NonNull Callback callback) { - mCallbacks.add(callback); - } - - @Override - public void removeCallback(@NonNull Callback callback) { - mCallbacks.remove(callback); - } - - @Override - public void startDozing() { - if (!mDozingRequested) { - mDozingRequested = true; - mDozeLog.traceDozing(mDozing); - updateDozing(); - updateIsKeyguard(); - } - } - - @Override - public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) { - if (reason == DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS) { - mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE, - "com.android.systemui:LONG_PRESS"); - startAssist(new Bundle()); - return; - } - - if (reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) { - mScrimController.setWakeLockScreenSensorActive(true); - } - - if (reason == DozeEvent.PULSE_REASON_DOCKING && mStatusBarWindow != null) { - mStatusBarWindowViewController.suppressWakeUpGesture(true); - } - - boolean passiveAuthInterrupt = reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN - && mWakeLockScreenPerformsAuth; - // Set the state to pulsing, so ScrimController will know what to do once we ask it to - // execute the transition. The pulse callback will then be invoked when the scrims - // are black, indicating that StatusBar is ready to present the rest of the UI. - mPulsing = true; - mDozeScrimController.pulse(new PulseCallback() { - @Override - public void onPulseStarted() { - callback.onPulseStarted(); - updateNotificationPanelTouchState(); - setPulsing(true); - } - - @Override - public void onPulseFinished() { - mPulsing = false; - callback.onPulseFinished(); - updateNotificationPanelTouchState(); - mScrimController.setWakeLockScreenSensorActive(false); - if (mStatusBarWindow != null) { - mStatusBarWindowViewController.suppressWakeUpGesture(false); - } - setPulsing(false); - } - - private void setPulsing(boolean pulsing) { - mStatusBarStateController.setPulsing(pulsing); - mStatusBarKeyguardViewManager.setPulsing(pulsing); - mKeyguardViewMediator.setPulsing(pulsing); - mNotificationPanel.setPulsing(pulsing); - mVisualStabilityManager.setPulsing(pulsing); - mStatusBarWindowViewController.setPulsing(pulsing); - mIgnoreTouchWhilePulsing = false; - if (mKeyguardUpdateMonitor != null && passiveAuthInterrupt) { - mKeyguardUpdateMonitor.onAuthInterruptDetected(pulsing /* active */); - } - updateScrimController(); - mPulseExpansionHandler.setPulsing(pulsing); - mWakeUpCoordinator.setPulsing(pulsing); - } - }, reason); - // DozeScrimController is in pulse state, now let's ask ScrimController to start - // pulsing and draw the black frame, if necessary. - updateScrimController(); - } - - @Override - public void stopDozing() { - if (mDozingRequested) { - mDozingRequested = false; - mDozeLog.traceDozing(mDozing); - updateDozing(); - } - } - - @Override - public void onIgnoreTouchWhilePulsing(boolean ignore) { - if (ignore != mIgnoreTouchWhilePulsing) { - mDozeLog.tracePulseTouchDisabledByProx(ignore); - } - mIgnoreTouchWhilePulsing = ignore; - if (isDozing() && ignore) { - mStatusBarWindowViewController.cancelCurrentTouch(); - } - } - - @Override - public void dozeTimeTick() { - mNotificationPanel.dozeTimeTick(); - if (mAmbientIndicationContainer instanceof DozeReceiver) { - ((DozeReceiver) mAmbientIndicationContainer).dozeTimeTick(); - } - } - - @Override - public boolean isPowerSaveActive() { - return mBatteryController.isAodPowerSave(); - } - - @Override - public boolean isPulsingBlocked() { - return mBiometricUnlockController.getMode() - == BiometricUnlockController.MODE_WAKE_AND_UNLOCK; - } - - @Override - public boolean isProvisioned() { - return mDeviceProvisionedController.isDeviceProvisioned() - && mDeviceProvisionedController.isCurrentUserSetup(); - } - - @Override - public boolean isBlockingDoze() { - if (mBiometricUnlockController.hasPendingAuthentication()) { - Log.i(TAG, "Blocking AOD because fingerprint has authenticated"); - return true; - } - return false; - } - - @Override - public void extendPulse(int reason) { - if (reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) { - mScrimController.setWakeLockScreenSensorActive(true); - } - if (mDozeScrimController.isPulsing() && mHeadsUpManager.hasNotifications()) { - mHeadsUpManager.extendHeadsUp(); - } else { - mDozeScrimController.extendPulse(); - } - } - - @Override - public void stopPulsing() { - if (mDozeScrimController.isPulsing()) { - mDozeScrimController.pulseOutNow(); - } - } - - @Override - public void setAnimateWakeup(boolean animateWakeup) { - if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE - || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING) { - // Too late to change the wakeup animation. - return; - } - mAnimateWakeup = animateWakeup; - } - - @Override - public void setAnimateScreenOff(boolean animateScreenOff) { - mAnimateScreenOff = animateScreenOff; - } - - @Override - public void onSlpiTap(float screenX, float screenY) { - if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null - && mAmbientIndicationContainer.getVisibility() == View.VISIBLE) { - mAmbientIndicationContainer.getLocationOnScreen(mTmpInt2); - float viewX = screenX - mTmpInt2[0]; - float viewY = screenY - mTmpInt2[1]; - if (0 <= viewX && viewX <= mAmbientIndicationContainer.getWidth() - && 0 <= viewY && viewY <= mAmbientIndicationContainer.getHeight()) { - dispatchTap(mAmbientIndicationContainer, viewX, viewY); - } - } - } - - @Override - public void setDozeScreenBrightness(int value) { - mStatusBarWindowController.setDozeScreenBrightness(value); - } - - @Override - public void setAodDimmingScrim(float scrimOpacity) { - mScrimController.setAodFrontScrimAlpha(scrimOpacity); - } - - @Override - public void prepareForGentleSleep(Runnable onDisplayOffCallback) { - if (mPendingScreenOffCallback != null) { - Log.w(TAG, "Overlapping onDisplayOffCallback. Ignoring previous one."); - } - mPendingScreenOffCallback = onDisplayOffCallback; - updateScrimController(); - } - - @Override - public void cancelGentleSleep() { - mPendingScreenOffCallback = null; - if (mScrimController.getState() == ScrimState.OFF) { - updateScrimController(); - } - } - - /** - * When the dozing host is waiting for scrims to fade out to change the display state. - */ - boolean hasPendingScreenOffCallback() { - return mPendingScreenOffCallback != null; - } - - /** - * Executes an nullifies the pending display state callback. - * - * @see #hasPendingScreenOffCallback() - * @see #prepareForGentleSleep(Runnable) - */ - void executePendingScreenOffCallback() { - if (mPendingScreenOffCallback == null) { - return; - } - mPendingScreenOffCallback.run(); - mPendingScreenOffCallback = null; - } - - private void dispatchTap(View view, float x, float y) { - long now = SystemClock.elapsedRealtime(); - dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_DOWN); - dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_UP); - } - - private void dispatchTouchEvent(View view, float x, float y, long now, int action) { - MotionEvent ev = MotionEvent.obtain(now, now, action, x, y, 0 /* meta */); - view.dispatchTouchEvent(ev); - ev.recycle(); - } - - private boolean shouldAnimateWakeup() { - return mAnimateWakeup; - } - - public boolean shouldAnimateScreenOff() { - return mAnimateScreenOff; - } - } - public boolean shouldIgnoreTouch() { - return isDozing() && mDozeServiceHost.mIgnoreTouchWhilePulsing; + return isDozing() && mDozeServiceHost.getIgnoreTouchWhilePulsing(); } // Begin Extra BaseStatusBar methods. @@ -4406,7 +4112,7 @@ public class StatusBar extends SystemUI implements DemoMode, private boolean mVisibleToUser; protected DevicePolicyManager mDevicePolicyManager; - protected PowerManager mPowerManager; + private final PowerManager mPowerManager; protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; protected KeyguardManager mKeyguardManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index 6edd75b20fed..0c68383649ab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -123,6 +123,7 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi IntentFilter filter = new IntentFilter(); filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED); + filter.addAction(Intent.ACTION_USER_UNLOCKED); context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, bgHandler); @@ -298,14 +299,11 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi } else { mVpnUserId = mCurrentUserId; } - refreshCACerts(); fireCallbacks(); } - private void refreshCACerts() { - new CACertLoader().execute(mCurrentUserId); - int workProfileId = getWorkProfileUserId(mCurrentUserId); - if (workProfileId != UserHandle.USER_NULL) new CACertLoader().execute(workProfileId); + private void refreshCACerts(int userId) { + new CACertLoader().execute(userId); } private String getNameForVpnConfig(VpnConfig cfg, UserHandle user) { @@ -401,7 +399,10 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (KeyChain.ACTION_TRUST_STORE_CHANGED.equals(intent.getAction())) { - refreshCACerts(); + refreshCACerts(getSendingUserId()); + } else if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) { + int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); + if (userId != UserHandle.USER_NULL) refreshCACerts(userId); } } }; @@ -416,9 +417,6 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi return new Pair<Integer, Boolean>(userId[0], hasCACerts); } catch (RemoteException | InterruptedException | AssertionError e) { Log.i(TAG, "failed to get CA certs", e); - mBgHandler.postDelayed( - () -> new CACertLoader().execute(userId[0]), - CA_CERT_LOADING_RETRY_TIME_IN_MS); return new Pair<Integer, Boolean>(userId[0], null); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index f2d2faed6a42..2c996684f437 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.policy; +import static android.os.UserManager.SWITCHABILITY_STATUS_OK; + import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import static com.android.systemui.DejankUtils.whitelistIpcs; @@ -196,7 +198,10 @@ public class UserSwitcherController implements Dumpable { } ArrayList<UserRecord> records = new ArrayList<>(infos.size()); int currentId = ActivityManager.getCurrentUser(); - boolean canSwitchUsers = mUserManager.canSwitchUsers(); + // Check user switchability of the foreground user since SystemUI is running in + // User 0 + boolean canSwitchUsers = mUserManager.getUserSwitchability( + UserHandle.of(ActivityManager.getCurrentUser())) == SWITCHABILITY_STATUS_OK; UserInfo currentUserInfo = null; UserRecord guestRecord = null; diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java index fa3ff64e5e18..0b273274f86d 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java @@ -35,6 +35,7 @@ import android.util.Log; import android.widget.CheckBox; import com.android.internal.app.ResolverActivity; +import com.android.internal.app.chooser.TargetInfo; import com.android.systemui.R; import java.util.ArrayList; diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java index 92a8d8482e87..aa9c5ac2e032 100644 --- a/packages/SystemUI/src/com/android/systemui/util/Utils.java +++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java @@ -20,6 +20,7 @@ import android.Manifest; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.provider.Settings; import android.view.View; import com.android.systemui.SysUiServiceProvider; @@ -124,4 +125,13 @@ public class Utils { && QuickStepContract.isGesturalMode(navMode); } + /** + * Allow the media player to be shown in the QS area, controlled by 2 flags. + */ + public static boolean useQsMediaPlayer(Context context) { + int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_player", 0); + flag |= Settings.System.getInt(context.getContentResolver(), "npv_plugin_flag", 0); + + return flag > 0; + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt index 2bff5488937d..ead14e501193 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt @@ -21,6 +21,7 @@ import android.content.Context import android.content.IntentFilter import android.os.Handler import android.os.Looper +import android.os.PatternMatcher import android.os.UserHandle import android.test.suitebuilder.annotation.SmallTest import android.testing.AndroidTestingRunner @@ -33,6 +34,7 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock +import org.mockito.Mockito.`when` import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify @@ -48,6 +50,10 @@ class BroadcastDispatcherTest : SysuiTestCase() { val user1 = UserHandle.of(1) fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() + const val TEST_ACTION = "TEST_ACTION" + const val TEST_SCHEME = "TEST_SCHEME" + const val TEST_PATH = "TEST_PATH" + const val TEST_TYPE = "test/type" } @Mock @@ -83,6 +89,11 @@ class BroadcastDispatcherTest : SysuiTestCase() { Handler(testableLooper.looper), testableLooper.looper, mapOf(0 to mockUBRUser0, 1 to mockUBRUser1)) + + // These should be valid filters + `when`(intentFilter.countActions()).thenReturn(1) + `when`(intentFilterOther.countActions()).thenReturn(1) + `when`(mockContext.user).thenReturn(user0) } @Test @@ -129,6 +140,44 @@ class BroadcastDispatcherTest : SysuiTestCase() { verify(mockUBRUser1, never()).unregisterReceiver(broadcastReceiver) } + @Test(expected = IllegalArgumentException::class) + fun testFilterMustContainActions() { + val testFilter = IntentFilter() + broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter) + } + + @Test(expected = IllegalArgumentException::class) + fun testFilterMustNotContainDataScheme() { + val testFilter = IntentFilter(TEST_ACTION).apply { + addDataScheme(TEST_SCHEME) + } + broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter) + } + + @Test(expected = IllegalArgumentException::class) + fun testFilterMustNotContainDataAuthority() { + val testFilter = IntentFilter(TEST_ACTION).apply { + addDataAuthority(mock(IntentFilter.AuthorityEntry::class.java)) + } + broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter) + } + + @Test(expected = IllegalArgumentException::class) + fun testFilterMustNotContainDataPath() { + val testFilter = IntentFilter(TEST_ACTION).apply { + addDataPath(TEST_PATH, PatternMatcher.PATTERN_LITERAL) + } + broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter) + } + + @Test(expected = IllegalArgumentException::class) + fun testFilterMustNotContainDataType() { + val testFilter = IntentFilter(TEST_ACTION).apply { + addDataType(TEST_TYPE) + } + broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter) + } + private class TestBroadcastDispatcher( context: Context, mainHandler: Handler, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt index a1822c72fe18..f5d6f223b86c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt @@ -50,19 +50,14 @@ class PeopleHubViewControllerTest : SysuiTestCase() { fun testBindViewModelToViewBoundary() { val fakePerson1 = fakePersonViewModel("name") val fakeViewModel = PeopleHubViewModel(sequenceOf(fakePerson1), true) - val fakePersonViewAdapter1 = FakeDataListener<PersonViewModel?>() val fakePersonViewAdapter2 = FakeDataListener<PersonViewModel?>() - val mockClickView = mock(View::class.java) - `when`(mockViewBoundary.associatedViewForClickAnimation).thenReturn(mockClickView) `when`(mockViewBoundary.personViewAdapters) .thenReturn(sequenceOf(fakePersonViewAdapter1, fakePersonViewAdapter2)) - val mockFactory = mock(PeopleHubViewModelFactory::class.java) `when`(mockFactory.createWithAssociatedClickView(any())).thenReturn(fakeViewModel) - val mockSubscription = mock(Subscription::class.java) val fakeFactoryDataSource = object : DataSource<PeopleHubViewModelFactory> { override fun registerListener( @@ -82,6 +77,7 @@ class PeopleHubViewControllerTest : SysuiTestCase() { verify(mockFactory).createWithAssociatedClickView(mockClickView) } + @Test fun testViewModelDataSourceTransformsModel() { val fakeClickIntent = PendingIntent.getActivity(context, 0, Intent("action"), 0) val fakePerson = fakePersonModel("id", "name", fakeClickIntent) @@ -99,16 +95,17 @@ class PeopleHubViewControllerTest : SysuiTestCase() { val mockClickView = mock(View::class.java) factoryDataSource.registerListener(fakeListener) + val viewModel = (fakeListener.lastSeen as Maybe.Just).value .createWithAssociatedClickView(mockClickView) assertThat(viewModel.isVisible).isTrue() - val people = viewModel.people.toList() assertThat(people.size).isEqualTo(1) assertThat(people[0].name).isEqualTo("name") assertThat(people[0].icon).isSameAs(fakePerson.avatar) people[0].onClick() + verify(mockActivityStarter).startPendingIntentDismissingKeyguard( same(fakeClickIntent), any(), @@ -117,16 +114,20 @@ class PeopleHubViewControllerTest : SysuiTestCase() { } } +/** Works around Mockito matchers returning `null` and breaking non-nullable Kotlin code. */ private inline fun <reified T : Any> any(): T { return Mockito.any() ?: createInstance(T::class) } +/** Works around Mockito matchers returning `null` and breaking non-nullable Kotlin code. */ private inline fun <reified T : Any> same(value: T): T { return Mockito.same(value) ?: createInstance(T::class) } +/** Creates an instance of the given class. */ private fun <T : Any> createInstance(clazz: KClass<T>): T = castNull() +/** Tricks the Kotlin compiler into assigning `null` to a non-nullable variable. */ @Suppress("UNCHECKED_CAST") private fun <T> castNull(): T = null as T diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java new file mode 100644 index 000000000000..b05172c6d7c2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java @@ -0,0 +1,224 @@ +/* + * 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 org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.os.PowerManager; +import android.testing.AndroidTestingRunner; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.assist.AssistManager; +import com.android.systemui.doze.DozeEvent; +import com.android.systemui.doze.DozeHost; +import com.android.systemui.doze.DozeLog; +import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.statusbar.PulseExpansionHandler; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.StatusBarStateControllerImpl; +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; +import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; + +import dagger.Lazy; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class DozeServiceHostTest extends SysuiTestCase { + + private DozeServiceHost mDozeServiceHost; + + @Mock private HeadsUpManagerPhone mHeadsUpManager; + @Mock private ScrimController mScrimController; + @Mock private DozeScrimController mDozeScrimController; + @Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy; + @Mock private VisualStabilityManager mVisualStabilityManager; + @Mock private KeyguardViewMediator mKeyguardViewMediator; + @Mock private StatusBarStateControllerImpl mStatusBarStateController; + @Mock private BatteryController mBatteryController; + @Mock private DeviceProvisionedController mDeviceProvisionedController; + @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock private AssistManager mAssistManager; + @Mock private DozeLog mDozeLog; + @Mock private PulseExpansionHandler mPulseExpansionHandler; + @Mock private NotificationWakeUpCoordinator mNotificationWakeUpCoordinator; + @Mock private StatusBarWindowController mStatusBarWindowController; + @Mock private PowerManager mPowerManager; + @Mock private WakefulnessLifecycle mWakefullnessLifecycle; + @Mock private StatusBar mStatusBar; + @Mock private NotificationIconAreaController mNotificationIconAreaController; + @Mock private StatusBarWindowViewController mStatusBarWindowViewController; + @Mock private StatusBarWindowView mStatusBarWindow; + @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + @Mock private NotificationPanelView mNotificationPanel; + @Mock private View mAmbientIndicationContainer; + @Mock private BiometricUnlockController mBiometricUnlockController; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController); + mDozeServiceHost = new DozeServiceHost(mDozeLog, mPowerManager, mWakefullnessLifecycle, + mStatusBarStateController, mDeviceProvisionedController, mHeadsUpManager, + mBatteryController, mScrimController, mBiometricUnlockControllerLazy, + mKeyguardViewMediator, mAssistManager, mDozeScrimController, mKeyguardUpdateMonitor, + mVisualStabilityManager, mPulseExpansionHandler, mStatusBarWindowController, + mNotificationWakeUpCoordinator); + + mDozeServiceHost.initialize(mStatusBar, mNotificationIconAreaController, + mStatusBarWindowViewController, mStatusBarWindow, mStatusBarKeyguardViewManager, + mNotificationPanel, mAmbientIndicationContainer); + } + + @Test + public void testStartStopDozing() { + when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); + when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true); + + assertFalse(mDozeServiceHost.getDozingRequested()); + + mDozeServiceHost.startDozing(); + verify(mStatusBarStateController).setIsDozing(eq(true)); + verify(mStatusBar).updateIsKeyguard(); + + mDozeServiceHost.stopDozing(); + verify(mStatusBarStateController).setIsDozing(eq(false)); + } + + + @Test + public void testPulseWhileDozing_updatesScrimController() { + mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD); + mStatusBar.showKeyguardImpl(); + + // Keep track of callback to be able to stop the pulse +// DozeHost.PulseCallback[] pulseCallback = new DozeHost.PulseCallback[1]; +// doAnswer(invocation -> { +// pulseCallback[0] = invocation.getArgument(0); +// return null; +// }).when(mDozeScrimController).pulse(any(), anyInt()); + + // Starting a pulse should change the scrim controller to the pulsing state + mDozeServiceHost.pulseWhileDozing(new DozeHost.PulseCallback() { + @Override + public void onPulseStarted() { + } + + @Override + public void onPulseFinished() { + } + }, DozeEvent.PULSE_REASON_NOTIFICATION); + + ArgumentCaptor<DozeHost.PulseCallback> pulseCallbackArgumentCaptor = + ArgumentCaptor.forClass(DozeHost.PulseCallback.class); + + verify(mDozeScrimController).pulse( + pulseCallbackArgumentCaptor.capture(), eq(DozeEvent.PULSE_REASON_NOTIFICATION)); + verify(mStatusBar).updateScrimController(); + reset(mStatusBar); + + pulseCallbackArgumentCaptor.getValue().onPulseFinished(); + assertFalse(mDozeScrimController.isPulsing()); + verify(mStatusBar).updateScrimController(); + } + + + @Test + public void testPulseWhileDozingWithDockingReason_suppressWakeUpGesture() { + // Keep track of callback to be able to stop the pulse + final DozeHost.PulseCallback[] pulseCallback = new DozeHost.PulseCallback[1]; + doAnswer(invocation -> { + pulseCallback[0] = invocation.getArgument(0); + return null; + }).when(mDozeScrimController).pulse(any(), anyInt()); + + // Starting a pulse while docking should suppress wakeup gesture + mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class), + DozeEvent.PULSE_REASON_DOCKING); + verify(mStatusBarWindowViewController).suppressWakeUpGesture(eq(true)); + + // Ending a pulse should restore wakeup gesture + pulseCallback[0].onPulseFinished(); + verify(mStatusBarWindowViewController).suppressWakeUpGesture(eq(false)); + } + + @Test + public void testPulseWhileDozing_notifyAuthInterrupt() { + HashSet<Integer> reasonsWantingAuth = new HashSet<>( + Collections.singletonList(DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN)); + HashSet<Integer> reasonsSkippingAuth = new HashSet<>( + Arrays.asList(DozeEvent.PULSE_REASON_INTENT, + DozeEvent.PULSE_REASON_NOTIFICATION, + DozeEvent.PULSE_REASON_SENSOR_SIGMOTION, + DozeEvent.REASON_SENSOR_PICKUP, + DozeEvent.REASON_SENSOR_DOUBLE_TAP, + DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS, + DozeEvent.PULSE_REASON_DOCKING, + DozeEvent.REASON_SENSOR_WAKE_UP, + DozeEvent.REASON_SENSOR_TAP)); + HashSet<Integer> reasonsThatDontPulse = new HashSet<>( + Arrays.asList(DozeEvent.REASON_SENSOR_PICKUP, + DozeEvent.REASON_SENSOR_DOUBLE_TAP, + DozeEvent.REASON_SENSOR_TAP)); + + doAnswer(invocation -> { + DozeHost.PulseCallback callback = invocation.getArgument(0); + callback.onPulseStarted(); + return null; + }).when(mDozeScrimController).pulse(any(), anyInt()); + + mDozeServiceHost.mWakeLockScreenPerformsAuth = true; + for (int i = 0; i < DozeEvent.TOTAL_REASONS; i++) { + reset(mKeyguardUpdateMonitor); + mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class), i); + if (reasonsWantingAuth.contains(i)) { + verify(mKeyguardUpdateMonitor).onAuthInterruptDetected(eq(true)); + } else if (reasonsSkippingAuth.contains(i) || reasonsThatDontPulse.contains(i)) { + verify(mKeyguardUpdateMonitor, never()).onAuthInterruptDetected(eq(true)); + } else { + throw new AssertionError("Reason " + i + " isn't specified as wanting or skipping" + + " passive auth. Please consider how this pulse reason should behave."); + } + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index f5e92e4f3181..66c01ca58491 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -85,8 +85,6 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.doze.DozeEvent; -import com.android.systemui.doze.DozeHost; import com.android.systemui.doze.DozeLog; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; @@ -145,9 +143,6 @@ import org.mockito.MockitoAnnotations; import java.io.ByteArrayOutputStream; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; import dagger.Lazy; @@ -231,6 +226,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private DozeParameters mDozeParameters; @Mock private Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy; @Mock private LockscreenWallpaper mLockscreenWallpaper; + @Mock private DozeServiceHost mDozeServiceHost; @Mock private LinearLayout mLockIconContainer; @Mock private ViewMediatorCallback mKeyguardVieMediatorCallback; @@ -365,7 +361,10 @@ public class StatusBarTest extends SysuiTestCase { mDozeParameters, mScrimController, mLockscreenWallpaperLazy, - mBiometricUnlockControllerLazy); + mBiometricUnlockControllerLazy, + mDozeServiceHost, + mPowerManager, + mDozeScrimController); when(mStatusBarWindowView.findViewById(R.id.lock_icon_container)).thenReturn( mLockIconContainer); @@ -388,7 +387,6 @@ public class StatusBarTest extends SysuiTestCase { mStatusBar.mNotificationIconAreaController = mNotificationIconAreaController; mStatusBar.mPresenter = mNotificationPresenter; mStatusBar.mKeyguardIndicationController = mKeyguardIndicationController; - mStatusBar.mPowerManager = mPowerManager; mStatusBar.mBarService = mBarService; mStatusBar.mStackScroller = mStackScroller; mStatusBar.mStatusBarWindowViewController = mStatusBarWindowViewController; @@ -757,83 +755,18 @@ public class StatusBarTest extends SysuiTestCase { mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD); mStatusBar.showKeyguardImpl(); - // Keep track of callback to be able to stop the pulse - DozeHost.PulseCallback[] pulseCallback = new DozeHost.PulseCallback[1]; - doAnswer(invocation -> { - pulseCallback[0] = invocation.getArgument(0); - return null; - }).when(mDozeScrimController).pulse(any(), anyInt()); - // Starting a pulse should change the scrim controller to the pulsing state - mStatusBar.mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class), - DozeEvent.PULSE_REASON_NOTIFICATION); + when(mDozeServiceHost.isPulsing()).thenReturn(true); + mStatusBar.updateScrimController(); verify(mScrimController).transitionTo(eq(ScrimState.PULSING), any()); // Ending a pulse should take it back to keyguard state - pulseCallback[0].onPulseFinished(); + when(mDozeServiceHost.isPulsing()).thenReturn(false); + mStatusBar.updateScrimController(); verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD)); } @Test - public void testPulseWhileDozing_notifyAuthInterrupt() { - HashSet<Integer> reasonsWantingAuth = new HashSet<>( - Collections.singletonList(DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN)); - HashSet<Integer> reasonsSkippingAuth = new HashSet<>( - Arrays.asList(DozeEvent.PULSE_REASON_INTENT, - DozeEvent.PULSE_REASON_NOTIFICATION, - DozeEvent.PULSE_REASON_SENSOR_SIGMOTION, - DozeEvent.REASON_SENSOR_PICKUP, - DozeEvent.REASON_SENSOR_DOUBLE_TAP, - DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS, - DozeEvent.PULSE_REASON_DOCKING, - DozeEvent.REASON_SENSOR_WAKE_UP, - DozeEvent.REASON_SENSOR_TAP)); - HashSet<Integer> reasonsThatDontPulse = new HashSet<>( - Arrays.asList(DozeEvent.REASON_SENSOR_PICKUP, - DozeEvent.REASON_SENSOR_DOUBLE_TAP, - DozeEvent.REASON_SENSOR_TAP)); - - doAnswer(invocation -> { - DozeHost.PulseCallback callback = invocation.getArgument(0); - callback.onPulseStarted(); - return null; - }).when(mDozeScrimController).pulse(any(), anyInt()); - - mStatusBar.mDozeServiceHost.mWakeLockScreenPerformsAuth = true; - for (int i = 0; i < DozeEvent.TOTAL_REASONS; i++) { - reset(mKeyguardUpdateMonitor); - mStatusBar.mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class), i); - if (reasonsWantingAuth.contains(i)) { - verify(mKeyguardUpdateMonitor).onAuthInterruptDetected(eq(true)); - } else if (reasonsSkippingAuth.contains(i) || reasonsThatDontPulse.contains(i)) { - verify(mKeyguardUpdateMonitor, never()).onAuthInterruptDetected(eq(true)); - } else { - throw new AssertionError("Reason " + i + " isn't specified as wanting or skipping" - + " passive auth. Please consider how this pulse reason should behave."); - } - } - } - - @Test - public void testPulseWhileDozingWithDockingReason_suppressWakeUpGesture() { - // Keep track of callback to be able to stop the pulse - final DozeHost.PulseCallback[] pulseCallback = new DozeHost.PulseCallback[1]; - doAnswer(invocation -> { - pulseCallback[0] = invocation.getArgument(0); - return null; - }).when(mDozeScrimController).pulse(any(), anyInt()); - - // Starting a pulse while docking should suppress wakeup gesture - mStatusBar.mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class), - DozeEvent.PULSE_REASON_DOCKING); - verify(mStatusBarWindowViewController).suppressWakeUpGesture(eq(true)); - - // Ending a pulse should restore wakeup gesture - pulseCallback[0].onPulseFinished(); - verify(mStatusBarWindowViewController).suppressWakeUpGesture(eq(false)); - } - - @Test public void testSetState_changesIsFullScreenUserSwitcherState() { mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD); assertFalse(mStatusBar.isFullScreenUserSwitcherState()); @@ -859,27 +792,17 @@ public class StatusBarTest extends SysuiTestCase { } @Test - public void testStartStopDozing() { - mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD); - when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true); - - mStatusBar.mDozeServiceHost.startDozing(); - verify(mStatusBarStateController).setIsDozing(eq(true)); - - mStatusBar.mDozeServiceHost.stopDozing(); - verify(mStatusBarStateController).setIsDozing(eq(false)); - } - - @Test public void testOnStartedWakingUp_isNotDozing() { mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD); when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true); - mStatusBar.mDozeServiceHost.startDozing(); - verify(mStatusBarStateController).setIsDozing(eq(true)); + when(mDozeServiceHost.getDozingRequested()).thenReturn(true); + mStatusBar.updateIsKeyguard(); + // TODO: mNotificationPanelView.expand(false) gets called twice. Should be once. + verify(mNotificationPanelView, times(2)).expand(eq(false)); clearInvocations(mNotificationPanelView); mStatusBar.mWakefulnessObserver.onStartedWakingUp(); - verify(mStatusBarStateController).setIsDozing(eq(false)); + verify(mDozeServiceHost).stopDozing(); verify(mNotificationPanelView).expand(eq(false)); } @@ -887,7 +810,8 @@ public class StatusBarTest extends SysuiTestCase { public void testOnStartedWakingUp_doesNotDismissBouncer_whenPulsing() { mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD); when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true); - mStatusBar.mDozeServiceHost.startDozing(); + when(mDozeServiceHost.getDozingRequested()).thenReturn(true); + mStatusBar.updateIsKeyguard(); clearInvocations(mNotificationPanelView); mStatusBar.setBouncerShowing(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java index 854cc2fad8e5..97542a9e5966 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java @@ -93,9 +93,9 @@ public class SecurityControllerTest extends SysuiTestCase implements SecurityCon when(mKeyChainService.queryLocalInterface("android.security.IKeyChainService")) .thenReturn(mKeyChainService); - // Wait for callbacks from 1) the CACertLoader and 2) the onUserSwitched() function in the + // Wait for callbacks from the onUserSwitched() function in the // constructor of mSecurityController - mStateChangedLatch = new CountDownLatch(2); + mStateChangedLatch = new CountDownLatch(1); // TODO: Migrate this test to TestableLooper and use a handler attached // to that. mSecurityController = new SecurityControllerImpl(mContext, @@ -169,7 +169,6 @@ public class SecurityControllerTest extends SysuiTestCase implements SecurityCon assertTrue(mSecurityController.hasCACertInCurrentUser()); // Exception - mStateChangedLatch = new CountDownLatch(1); when(mKeyChainService.getUserCaAliases()) @@ -181,9 +180,12 @@ public class SecurityControllerTest extends SysuiTestCase implements SecurityCon assertFalse(mStateChangedLatch.await(1, TimeUnit.SECONDS)); assertTrue(mSecurityController.hasCACertInCurrentUser()); - // The retry takes 30s - //assertTrue(mStateChangedLatch.await(31, TimeUnit.SECONDS)); - //assertFalse(mSecurityController.hasCACertInCurrentUser()); + + mSecurityController.new CACertLoader() + .execute(0); + + assertTrue(mStateChangedLatch.await(1, TimeUnit.SECONDS)); + assertFalse(mSecurityController.hasCACertInCurrentUser()); } @Test diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp index ca69c187089d..998572fc5679 100644 --- a/packages/Tethering/Android.bp +++ b/packages/Tethering/Android.bp @@ -21,6 +21,7 @@ java_defaults { "src/**/*.java", ":framework-tethering-shared-srcs", ":services-tethering-shared-srcs", + ":servicescore-tethering-src", ], static_libs: [ "androidx.annotation_annotation", @@ -67,13 +68,23 @@ android_app { // This group will be removed when tethering migration is done. filegroup { - name: "tethering-services-srcs", + name: "tethering-servicescore-srcs", srcs: [ + "src/com/android/server/connectivity/tethering/EntitlementManager.java", "src/com/android/server/connectivity/tethering/TetheringConfiguration.java", + "src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java", + ], +} + +// This group will be removed when tethering migration is done. +filegroup { + name: "tethering-servicesnet-srcs", + srcs: [ "src/android/net/dhcp/DhcpServerCallbacks.java", "src/android/net/dhcp/DhcpServingParamsParcelExt.java", "src/android/net/ip/IpServer.java", "src/android/net/ip/RouterAdvertisementDaemon.java", "src/android/net/util/InterfaceSet.java", + "src/android/net/util/PrefixUtils.java", ], } diff --git a/packages/Tethering/AndroidManifestBase.xml b/packages/Tethering/AndroidManifestBase.xml index b9cac1994359..dc013da33869 100644 --- a/packages/Tethering/AndroidManifestBase.xml +++ b/packages/Tethering/AndroidManifestBase.xml @@ -23,7 +23,6 @@ <application android:label="Tethering" android:defaultToDeviceProtectedStorage="true" - android:directBootAware="true" - android:usesCleartextTraffic="true"> + android:directBootAware="true"> </application> </manifest> diff --git a/services/net/java/android/net/util/PrefixUtils.java b/packages/Tethering/src/android/net/util/PrefixUtils.java index f60694aaedc9..f203e9995f3d 100644 --- a/services/net/java/android/net/util/PrefixUtils.java +++ b/packages/Tethering/src/android/net/util/PrefixUtils.java @@ -42,16 +42,19 @@ public class PrefixUtils { public static final IpPrefix DEFAULT_WIFI_P2P_PREFIX = pfx("192.168.49.0/24"); + /** Get non forwardable prefixes. */ public static Set<IpPrefix> getNonForwardablePrefixes() { final HashSet<IpPrefix> prefixes = new HashSet<>(); addNonForwardablePrefixes(prefixes); return prefixes; } + /** Add non forwardable prefixes. */ public static void addNonForwardablePrefixes(Set<IpPrefix> prefixes) { Collections.addAll(prefixes, MIN_NON_FORWARDABLE_PREFIXES); } + /** Get local prefixes from |lp|. */ public static Set<IpPrefix> localPrefixesFrom(LinkProperties lp) { final HashSet<IpPrefix> localPrefixes = new HashSet<>(); if (lp == null) return localPrefixes; @@ -66,10 +69,12 @@ public class PrefixUtils { return localPrefixes; } + /** Convert LinkAddress |addr| to IpPrefix. */ public static IpPrefix asIpPrefix(LinkAddress addr) { return new IpPrefix(addr.getAddress(), addr.getPrefixLength()); } + /** Convert InetAddress |ip| to IpPrefix. */ public static IpPrefix ipAddressAsPrefix(InetAddress ip) { final int bitLength = (ip instanceof Inet4Address) ? NetworkConstants.IPV4_ADDR_BITS diff --git a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java index f952bcef5606..6b0f1de7ce85 100644 --- a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java @@ -87,7 +87,6 @@ public class EntitlementManager { private static final int EVENT_MAYBE_RUN_PROVISIONING = 3; private static final int EVENT_GET_ENTITLEMENT_VALUE = 4; - // The ArraySet contains enabled downstream types, ex: // {@link ConnectivityManager.TETHERING_WIFI} // {@link ConnectivityManager.TETHERING_USB} @@ -112,7 +111,6 @@ public class EntitlementManager { public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log, int permissionChangeMessageCode, MockableSystemProperties systemProperties) { - mContext = ctx; mLog = log.forSubComponent(TAG); mCurrentTethers = new ArraySet<Integer>(); @@ -138,7 +136,7 @@ public class EntitlementManager { /** * Ui entitlement check fails in |downstream|. * - * @param downstream tethering type from ConnectivityManager.TETHERING_{@code *}. + * @param downstream tethering type from ConnectivityManager.TETHERING_{@code *}. */ void onUiEntitlementFailed(int downstream); } @@ -662,7 +660,6 @@ public class EntitlementManager { private void handleGetLatestTetheringEntitlementValue(int downstream, ResultReceiver receiver, boolean showEntitlementUi) { - final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); if (!isTetherProvisioningRequired(config)) { receiver.send(TETHER_ERROR_NO_ERROR, null); diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java index 3a9e21f943d8..9769596ab1d0 100644 --- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java @@ -146,6 +146,7 @@ public class UpstreamNetworkMonitor { } } + /** Listen all networks. */ public void startObserveAllNetworks() { stop(); @@ -155,6 +156,13 @@ public class UpstreamNetworkMonitor { cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler); } + /** + * Stop tracking candidate tethering upstreams and release mobile network request. + * Note: this function is used when tethering is stopped because tethering do not need to + * choose upstream anymore. But it would not stop default network tracking because + * EntitlementManager may need to know default network to decide whether to request entitlement + * check even tethering is not active yet. + */ public void stop() { releaseMobileNetworkRequest(); @@ -165,6 +173,7 @@ public class UpstreamNetworkMonitor { mNetworkMap.clear(); } + /** Setup or teardown DUN connection according to |dunRequired|. */ public void updateMobileRequiresDun(boolean dunRequired) { final boolean valueChanged = (mDunRequired != dunRequired); mDunRequired = dunRequired; @@ -174,10 +183,12 @@ public class UpstreamNetworkMonitor { } } + /** Whether mobile network is requested. */ public boolean mobileNetworkRequested() { return (mMobileNetworkCallback != null); } + /** Request mobile network if mobile upstream is permitted. */ public void registerMobileNetworkRequest() { if (!isCellularUpstreamPermitted()) { mLog.i("registerMobileNetworkRequest() is not permitted"); @@ -209,6 +220,7 @@ public class UpstreamNetworkMonitor { cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, legacyType, mHandler); } + /** Release mobile network request. */ public void releaseMobileNetworkRequest() { if (mMobileNetworkCallback == null) return; @@ -221,6 +233,9 @@ public class UpstreamNetworkMonitor { // becomes available and useful we (a) file a request to keep it up as // necessary and (b) change all upstream tracking state accordingly (by // passing LinkProperties up to Tethering). + /** + * Select the first available network from |perferredTypes|. + */ public NetworkState selectPreferredUpstreamType(Iterable<Integer> preferredTypes) { final TypeStatePair typeStatePair = findFirstAvailableUpstreamByType( mNetworkMap.values(), preferredTypes, isCellularUpstreamPermitted()); @@ -254,7 +269,11 @@ public class UpstreamNetworkMonitor { return typeStatePair.ns; } - // Returns null if no current upstream available. + /** + * Get current preferred upstream network. If default network is cellular and DUN is required, + * preferred upstream would be DUN otherwise preferred upstream is the same as default network. + * Returns null if no current upstream is available. + */ public NetworkState getCurrentPreferredUpstream() { final NetworkState dfltState = (mDefaultInternetNetwork != null) ? mNetworkMap.get(mDefaultInternetNetwork) @@ -270,10 +289,12 @@ public class UpstreamNetworkMonitor { return findFirstDunNetwork(mNetworkMap.values()); } + /** Tell UpstreamNetworkMonitor which network is the current upstream of tethering. */ public void setCurrentUpstream(Network upstream) { mTetheringUpstreamNetwork = upstream; } + /** Return local prefixes. */ public Set<IpPrefix> getLocalPrefixes() { return (Set<IpPrefix>) mLocalPrefixes.clone(); } @@ -501,8 +522,8 @@ public class UpstreamNetworkMonitor { try { nc = ConnectivityManager.networkCapabilitiesForType(type); } catch (IllegalArgumentException iae) { - Log.e(TAG, "No NetworkCapabilities mapping for legacy type: " + - ConnectivityManager.getNetworkTypeName(type)); + Log.e(TAG, "No NetworkCapabilities mapping for legacy type: " + + ConnectivityManager.getNetworkTypeName(type)); continue; } if (!isCellularUpstreamPermitted && isCellular(nc)) { @@ -547,18 +568,18 @@ public class UpstreamNetworkMonitor { } private static boolean isCellular(NetworkCapabilities nc) { - return (nc != null) && nc.hasTransport(TRANSPORT_CELLULAR) && - nc.hasCapability(NET_CAPABILITY_NOT_VPN); + return (nc != null) && nc.hasTransport(TRANSPORT_CELLULAR) + && nc.hasCapability(NET_CAPABILITY_NOT_VPN); } private static boolean hasCapability(NetworkState ns, int netCap) { - return (ns != null) && (ns.networkCapabilities != null) && - ns.networkCapabilities.hasCapability(netCap); + return (ns != null) && (ns.networkCapabilities != null) + && ns.networkCapabilities.hasCapability(netCap); } private static boolean isNetworkUsableAndNotCellular(NetworkState ns) { - return (ns != null) && (ns.networkCapabilities != null) && (ns.linkProperties != null) && - !isCellular(ns.networkCapabilities); + return (ns != null) && (ns.networkCapabilities != null) && (ns.linkProperties != null) + && !isCellular(ns.networkCapabilities); } private static NetworkState findFirstDunNetwork(Iterable<NetworkState> netStates) { diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp index da621076bb63..7c06e5f0d7ce 100644 --- a/packages/Tethering/tests/unit/Android.bp +++ b/packages/Tethering/tests/unit/Android.bp @@ -17,7 +17,10 @@ android_test { name: "TetheringTests", certificate: "platform", - srcs: ["src/**/*.java"], + srcs: [ + ":servicescore-tethering-src", + "src/**/*.java", + ], test_suites: ["device-tests"], static_libs: [ "androidx.test.rules", @@ -42,7 +45,9 @@ android_test { filegroup { name: "tethering-tests-src", srcs: [ + "src/com/android/server/connectivity/tethering/EntitlementManagerTest.java", "src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java", + "src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java", "src/android/net/dhcp/DhcpServingParamsParcelExtTest.java", "src/android/net/ip/IpServerTest.java", "src/android/net/util/InterfaceSetTest.java", diff --git a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java index 5217e26a40ef..5217e26a40ef 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java index 0d276cbd1b85..c028d6d9cadc 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java @@ -87,7 +87,7 @@ public class UpstreamNetworkMonitorTest { // Actual contents of the request don't matter for this test. The lack of // any specific TRANSPORT_* is sufficient to identify this request. - private static final NetworkRequest mDefaultRequest = new NetworkRequest.Builder().build(); + private static final NetworkRequest sDefaultRequest = new NetworkRequest.Builder().build(); @Mock private Context mContext; @Mock private EntitlementManager mEntitleMgr; @@ -140,7 +140,7 @@ public class UpstreamNetworkMonitorTest { @Test public void testDefaultNetworkIsTracked() throws Exception { assertTrue(mCM.hasNoCallbacks()); - mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr); + mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); mUNM.startObserveAllNetworks(); assertEquals(1, mCM.trackingDefault.size()); @@ -153,7 +153,7 @@ public class UpstreamNetworkMonitorTest { public void testListensForAllNetworks() throws Exception { assertTrue(mCM.listening.isEmpty()); - mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr); + mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); mUNM.startObserveAllNetworks(); assertFalse(mCM.listening.isEmpty()); assertTrue(mCM.isListeningForAll()); @@ -164,9 +164,9 @@ public class UpstreamNetworkMonitorTest { @Test public void testCallbacksRegistered() { - mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr); + mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); verify(mCM, times(1)).requestNetwork( - eq(mDefaultRequest), any(NetworkCallback.class), any(Handler.class)); + eq(sDefaultRequest), any(NetworkCallback.class), any(Handler.class)); mUNM.startObserveAllNetworks(); verify(mCM, times(1)).registerNetworkCallback( any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class)); @@ -191,7 +191,7 @@ public class UpstreamNetworkMonitorTest { mUNM.registerMobileNetworkRequest(); assertTrue(mUNM.mobileNetworkRequested()); assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI); - assertFalse(mCM.isDunRequested()); + assertFalse(isDunRequested()); mUNM.stop(); assertFalse(mUNM.mobileNetworkRequested()); @@ -217,7 +217,7 @@ public class UpstreamNetworkMonitorTest { assertTrue(mUNM.mobileNetworkRequested()); assertUpstreamTypeRequested(TYPE_MOBILE_DUN); - assertTrue(mCM.isDunRequested()); + assertTrue(isDunRequested()); // Try a few things that must not result in any state change. mUNM.registerMobileNetworkRequest(); @@ -226,7 +226,7 @@ public class UpstreamNetworkMonitorTest { assertTrue(mUNM.mobileNetworkRequested()); assertUpstreamTypeRequested(TYPE_MOBILE_DUN); - assertTrue(mCM.isDunRequested()); + assertTrue(isDunRequested()); mUNM.stop(); verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class)); @@ -250,7 +250,7 @@ public class UpstreamNetworkMonitorTest { mUNM.registerMobileNetworkRequest(); assertTrue(mUNM.mobileNetworkRequested()); assertUpstreamTypeRequested(TYPE_MOBILE_DUN); - assertTrue(mCM.isDunRequested()); + assertTrue(isDunRequested()); mUNM.stop(); assertFalse(mUNM.mobileNetworkRequested()); @@ -266,17 +266,17 @@ public class UpstreamNetworkMonitorTest { mUNM.registerMobileNetworkRequest(); assertTrue(mUNM.mobileNetworkRequested()); assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI); - assertFalse(mCM.isDunRequested()); + assertFalse(isDunRequested()); mUNM.updateMobileRequiresDun(true); assertTrue(mUNM.mobileNetworkRequested()); assertUpstreamTypeRequested(TYPE_MOBILE_DUN); - assertTrue(mCM.isDunRequested()); + assertTrue(isDunRequested()); // Test going from DUN to no-DUN correctly re-registers callbacks. mUNM.updateMobileRequiresDun(false); assertTrue(mUNM.mobileNetworkRequested()); assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI); - assertFalse(mCM.isDunRequested()); + assertFalse(isDunRequested()); mUNM.stop(); assertFalse(mUNM.mobileNetworkRequested()); @@ -287,7 +287,7 @@ public class UpstreamNetworkMonitorTest { final Collection<Integer> preferredTypes = new ArrayList<>(); preferredTypes.add(TYPE_WIFI); - mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr); + mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); mUNM.startObserveAllNetworks(); // There are no networks, so there is nothing to select. assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes)); @@ -369,7 +369,7 @@ public class UpstreamNetworkMonitorTest { @Test public void testGetCurrentPreferredUpstream() throws Exception { - mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr); + mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); mUNM.startObserveAllNetworks(); mUNM.updateMobileRequiresDun(false); @@ -418,7 +418,7 @@ public class UpstreamNetworkMonitorTest { @Test public void testLocalPrefixes() throws Exception { - mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr); + mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); mUNM.startObserveAllNetworks(); // [0] Test minimum set of local prefixes. @@ -431,13 +431,13 @@ public class UpstreamNetworkMonitorTest { final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI); final LinkProperties wifiLp = wifiAgent.linkProperties; wifiLp.setInterfaceName("wlan0"); - final String[] WIFI_ADDRS = { + final String[] wifi_addrs = { "fe80::827a:bfff:fe6f:374d", "100.112.103.18", "2001:db8:4:fd00:827a:bfff:fe6f:374d", "2001:db8:4:fd00:6dea:325a:fdae:4ef4", "fd6a:a640:60bf:e985::123", // ULA address for good measure. }; - for (String addrStr : WIFI_ADDRS) { + for (String addrStr : wifi_addrs) { final String cidr = addrStr.contains(":") ? "/64" : "/20"; wifiLp.addLinkAddress(new LinkAddress(addrStr + cidr)); } @@ -458,10 +458,10 @@ public class UpstreamNetworkMonitorTest { final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); final LinkProperties cellLp = cellAgent.linkProperties; cellLp.setInterfaceName("rmnet_data0"); - final String[] CELL_ADDRS = { + final String[] cell_addrs = { "10.102.211.48", "2001:db8:0:1:b50e:70d9:10c9:433d", }; - for (String addrStr : CELL_ADDRS) { + for (String addrStr : cell_addrs) { final String cidr = addrStr.contains(":") ? "/64" : "/27"; cellLp.addLinkAddress(new LinkAddress(addrStr + cidr)); } @@ -481,10 +481,10 @@ public class UpstreamNetworkMonitorTest { dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET); final LinkProperties dunLp = dunAgent.linkProperties; dunLp.setInterfaceName("rmnet_data1"); - final String[] DUN_ADDRS = { + final String[] dun_addrs = { "192.0.2.48", "2001:db8:1:2:b50e:70d9:10c9:433d", }; - for (String addrStr : DUN_ADDRS) { + for (String addrStr : dun_addrs) { final String cidr = addrStr.contains(":") ? "/64" : "/27"; dunLp.addLinkAddress(new LinkAddress(addrStr + cidr)); } @@ -525,7 +525,7 @@ public class UpstreamNetworkMonitorTest { // Mobile has higher pirority than wifi. preferredTypes.add(TYPE_MOBILE_HIPRI); preferredTypes.add(TYPE_WIFI); - mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr); + mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); mUNM.startObserveAllNetworks(); // Setup wifi and make wifi as default network. final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI); @@ -556,6 +556,15 @@ public class UpstreamNetworkMonitorTest { mCM.legacyTypeMap.values().iterator().next()); } + private boolean isDunRequested() { + for (NetworkRequest req : mCM.requested.values()) { + if (req.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)) { + return true; + } + } + return false; + } + public static class TestConnectivityManager extends ConnectivityManager { public Map<NetworkCallback, Handler> allCallbacks = new HashMap<>(); public Set<NetworkCallback> trackingDefault = new HashSet<>(); @@ -598,17 +607,10 @@ public class UpstreamNetworkMonitorTest { return false; } - boolean isDunRequested() { - for (NetworkRequest req : requested.values()) { - if (req.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)) { - return true; - } - } - return false; + int getNetworkId() { + return ++mNetworkId; } - int getNetworkId() { return ++mNetworkId; } - void makeDefaultNetwork(TestNetworkAgent agent) { if (Objects.equals(defaultNetwork, agent)) return; @@ -630,7 +632,7 @@ public class UpstreamNetworkMonitorTest { public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) { assertFalse(allCallbacks.containsKey(cb)); allCallbacks.put(cb, h); - if (mDefaultRequest.equals(req)) { + if (sDefaultRequest.equals(req)) { assertFalse(trackingDefault.contains(cb)); trackingDefault.add(cb); } else { @@ -749,9 +751,13 @@ public class UpstreamNetworkMonitorTest { private final State mLoggingState = new LoggingState(); class LoggingState extends State { - @Override public void enter() { messages.clear(); } + @Override public void enter() { + messages.clear(); + } - @Override public void exit() { messages.clear(); } + @Override public void exit() { + messages.clear(); + } @Override public boolean processMessage(Message msg) { messages.add(msg); diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto deleted file mode 100644 index bd682de52471..000000000000 --- a/proto/src/wifi.proto +++ /dev/null @@ -1,2707 +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. - */ - -syntax = "proto2"; - -package clearcut.connectivity; - -option java_package = "com.android.server.wifi"; -option java_outer_classname = "WifiMetricsProto"; - -// The information about the Wifi events. -message WifiLog { - - // Session information that gets logged for every Wifi connection. - repeated ConnectionEvent connection_event = 1; - - // Number of saved networks in the user profile. - optional int32 num_saved_networks = 2; - - // Number of open networks in the saved networks. - optional int32 num_open_networks = 3; - - // Number of legacy personal networks. - optional int32 num_legacy_personal_networks = 4; - - // Number of legacy enterprise networks. - optional int32 num_legacy_enterprise_networks = 5; - - // Does the user have location setting enabled. - optional bool is_location_enabled = 6; - - // Does the user have scanning enabled. - optional bool is_scanning_always_enabled = 7; - - // Number of times user toggled wifi using the settings menu. - optional int32 num_wifi_toggled_via_settings = 8; - - // Number of times user toggled wifi using the airplane menu. - optional int32 num_wifi_toggled_via_airplane = 9; - - // Number of networks added by the user. - optional int32 num_networks_added_by_user = 10; - - // Number of networks added by applications. - optional int32 num_networks_added_by_apps = 11; - - // Number scans that returned empty results. - optional int32 num_empty_scan_results = 12; - - // Number scans that returned at least one result. - optional int32 num_non_empty_scan_results = 13; - - // Number of single scans requests. - optional int32 num_oneshot_scans = 14; - - // Number of repeated background scans that were scheduled to the chip. - optional int32 num_background_scans = 15; - - // Error codes that a scan can result in. - enum ScanReturnCode { - - // Return Code is unknown. - SCAN_UNKNOWN = 0; - - // Scan was successful. - SCAN_SUCCESS = 1; - - // Scan was successfully started, but was interrupted. - SCAN_FAILURE_INTERRUPTED = 2; - - // Scan failed to start because of invalid configuration - // (bad channel, etc). - SCAN_FAILURE_INVALID_CONFIGURATION = 3; - - // Could not start a scan because wifi is disabled. - FAILURE_WIFI_DISABLED = 4; - - } - - // Mapping of error codes to the number of times that scans resulted - // in that error. - repeated ScanReturnEntry scan_return_entries = 16; - - message ScanReturnEntry { - - // Return code of the scan. - optional ScanReturnCode scan_return_code = 1; - - // Number of entries that were found in the scan. - optional int32 scan_results_count = 2; - } - - // State of the Wifi. - enum WifiState { - - // State is unknown. - WIFI_UNKNOWN = 0; - - // Wifi is disabled. - WIFI_DISABLED = 1; - - // Wifi is enabled. - WIFI_DISCONNECTED = 2; - - // Wifi is enabled and associated with an AP. - WIFI_ASSOCIATED = 3; - } - - // Mapping of system state to the number of times that scans were requested in - // that state - repeated WifiSystemStateEntry wifi_system_state_entries = 17; - - message WifiSystemStateEntry { - - // Current WiFi state. - optional WifiState wifi_state = 1; - - // Count of scans in state. - optional int32 wifi_state_count = 2; - - // Is screen on. - optional bool is_screen_on = 3; - } - - // Mapping of Error/Success codes to the number of background scans that resulted in it - repeated ScanReturnEntry background_scan_return_entries = 18; - - // Mapping of system state to the number of times that Background scans were requested in that - // state - repeated WifiSystemStateEntry background_scan_request_state = 19; - - // Total number of times the Watchdog of Last Resort triggered, resetting the wifi stack - optional int32 num_last_resort_watchdog_triggers = 20; - - // Total number of networks over bad association threshold when watchdog triggered - optional int32 num_last_resort_watchdog_bad_association_networks_total = 21; - - // Total number of networks over bad authentication threshold when watchdog triggered - optional int32 num_last_resort_watchdog_bad_authentication_networks_total = 22; - - // Total number of networks over bad dhcp threshold when watchdog triggered - optional int32 num_last_resort_watchdog_bad_dhcp_networks_total = 23; - - // Total number of networks over bad other threshold when watchdog triggered - optional int32 num_last_resort_watchdog_bad_other_networks_total = 24; - - // Total count of networks seen when watchdog triggered - optional int32 num_last_resort_watchdog_available_networks_total = 25; - - // Total count of triggers with atleast one bad association network - optional int32 num_last_resort_watchdog_triggers_with_bad_association = 26; - - // Total count of triggers with atleast one bad authentication network - optional int32 num_last_resort_watchdog_triggers_with_bad_authentication = 27; - - // Total count of triggers with atleast one bad dhcp network - optional int32 num_last_resort_watchdog_triggers_with_bad_dhcp = 28; - - // Total count of triggers with atleast one bad other network - optional int32 num_last_resort_watchdog_triggers_with_bad_other = 29; - - // Count of times connectivity watchdog confirmed pno is working - optional int32 num_connectivity_watchdog_pno_good = 30; - - // Count of times connectivity watchdog found pno not working - optional int32 num_connectivity_watchdog_pno_bad = 31; - - // Count of times connectivity watchdog confirmed background scan is working - optional int32 num_connectivity_watchdog_background_good = 32; - - // Count of times connectivity watchdog found background scan not working - optional int32 num_connectivity_watchdog_background_bad = 33; - - // The time duration represented by this wifi log, from start to end of capture - optional int32 record_duration_sec = 34; - - // Counts the occurrences of each individual RSSI poll level - repeated RssiPollCount rssi_poll_rssi_count = 35; - - // Total number of times WiFi connected immediately after a Last Resort Watchdog trigger, - // without new networks becoming available. - optional int32 num_last_resort_watchdog_successes = 36; - - // Total number of saved hidden networks - optional int32 num_hidden_networks = 37; - - // Total number of saved passpoint / hotspot 2.0 networks - optional int32 num_passpoint_networks = 38; - - // Total number of scan results - optional int32 num_total_scan_results = 39; - - // Total number of scan results for open networks - optional int32 num_open_network_scan_results = 40; - - // Total number of scan results for legacy personal networks - optional int32 num_legacy_personal_network_scan_results = 41; - - // Total number of scan results for legacy enterprise networks - optional int32 num_legacy_enterprise_network_scan_results = 42; - - // Total number of scan results for hidden networks - optional int32 num_hidden_network_scan_results = 43; - - // Total number of scan results for hotspot 2.0 r1 networks - optional int32 num_hotspot2_r1_network_scan_results = 44; - - // Total number of scan results for hotspot 2.0 r2 networks - optional int32 num_hotspot2_r2_network_scan_results = 45; - - // Total number of scans handled by framework (oneshot or otherwise) - optional int32 num_scans = 46; - - // Counts the occurrences of each alert reason. - repeated AlertReasonCount alert_reason_count = 47; - - // Counts the occurrences of each Wifi score - repeated WifiScoreCount wifi_score_count = 48; - - // Histogram of Soft AP Durations - repeated SoftApDurationBucket soft_ap_duration = 49; - - // Histogram of Soft AP ReturnCode - repeated SoftApReturnCodeCount soft_ap_return_code = 50; - - // Histogram of the delta between scan result RSSI and RSSI polls - repeated RssiPollCount rssi_poll_delta_count = 51; - - // List of events - repeated StaEvent sta_event_list = 52; - - // Total number of times WiFi HAL crashed. - optional int32 num_hal_crashes = 53; - - // Total number of times WiFicond crashed. - optional int32 num_wificond_crashes = 54; - - // Indicates the number of times an error was encountered in - // Wifi HAL on |WifiNative.setupInterfaceForClientMode|. - optional int32 num_setup_client_interface_failure_due_to_hal = 55; - - // Indicates the number of times an error was encountered in - // Wificond on |WifiNative.setupInterfaceForClientMode|. - optional int32 num_setup_client_interface_failure_due_to_wificond = 56; - - // Wi-Fi Aware metrics - optional WifiAwareLog wifi_aware_log = 57; - - // Number of saved Passpoint providers in user profile. - optional int32 num_passpoint_providers = 58; - - // Count of times Passpoint provider being installed. - optional int32 num_passpoint_provider_installation = 59; - - // Count of times Passpoint provivider is installed successfully. - optional int32 num_passpoint_provider_install_success = 60; - - // Count of times Passpoint provider is being uninstalled. - optional int32 num_passpoint_provider_uninstallation = 61; - - // Count of times Passpoint provider is uninstalled successfully. - optional int32 num_passpoint_provider_uninstall_success = 62; - - // Count of saved Passpoint providers device has ever connected to. - optional int32 num_passpoint_providers_successfully_connected = 63; - - // Histogram counting instances of scans with N many ScanResults with unique ssids - repeated NumConnectableNetworksBucket total_ssids_in_scan_histogram = 64; - - // Histogram counting instances of scans with N many ScanResults/bssids - repeated NumConnectableNetworksBucket total_bssids_in_scan_histogram = 65; - - // Histogram counting instances of scans with N many unique open ssids - repeated NumConnectableNetworksBucket available_open_ssids_in_scan_histogram = 66; - - // Histogram counting instances of scans with N many bssids for open networks - repeated NumConnectableNetworksBucket available_open_bssids_in_scan_histogram = 67; - - // Histogram counting instances of scans with N many unique ssids for saved networks - repeated NumConnectableNetworksBucket available_saved_ssids_in_scan_histogram = 68; - - // Histogram counting instances of scans with N many bssids for saved networks - repeated NumConnectableNetworksBucket available_saved_bssids_in_scan_histogram = 69; - - // Histogram counting instances of scans with N many unique SSIDs for open or saved networks - repeated NumConnectableNetworksBucket available_open_or_saved_ssids_in_scan_histogram = 70; - - // Histogram counting instances of scans with N many BSSIDs for open or saved networks - repeated NumConnectableNetworksBucket available_open_or_saved_bssids_in_scan_histogram = 71; - - // Histogram counting instances of scans with N many ScanResults matching unique saved passpoint providers - repeated NumConnectableNetworksBucket available_saved_passpoint_provider_profiles_in_scan_histogram = 72; - - // Histogram counting instances of scans with N many ScanResults BSSIDs matching a saved passpoint provider - repeated NumConnectableNetworksBucket available_saved_passpoint_provider_bssids_in_scan_histogram = 73; - - // Counts the number of AllSingleScanLister.onResult calls with a full band scan result - optional int32 full_band_all_single_scan_listener_results = 74; - - // Counts the number of AllSingleScanLister.onResult calls with a partial (channels) scan result - optional int32 partial_all_single_scan_listener_results = 75; - - // Pno scan metrics - optional PnoScanMetrics pno_scan_metrics = 76; - - // Histogram of "Connect to Network" notifications. - // The notification Action should be unset. - repeated ConnectToNetworkNotificationAndActionCount connect_to_network_notification_count = 77; - - // Histogram of "Connect to Network" notification user actions. - repeated ConnectToNetworkNotificationAndActionCount connect_to_network_notification_action_count = 78; - - // The number of SSIDs blacklisted from recommendation by the open network - // notification recommender - optional int32 open_network_recommender_blacklist_size = 79; - - // Is the available network notification feature turned on - optional bool is_wifi_networks_available_notification_on = 80; - - // Count of recommendation updates made by the open network notification - // recommender - optional int32 num_open_network_recommendation_updates = 81; - - // Count of connection attempts that were initiated unsuccessfully - optional int32 num_open_network_connect_message_failed_to_send = 82; - - // Histogram counting instances of scans with N many HotSpot 2.0 R1 APs - repeated NumConnectableNetworksBucket observed_hotspot_r1_aps_in_scan_histogram = 83; - - // Histogram counting instances of scans with N many HotSpot 2.0 R2 APs - repeated NumConnectableNetworksBucket observed_hotspot_r2_aps_in_scan_histogram = 84; - - // Histogram counting instances of scans with N many unique HotSpot 2.0 R1 ESS. - // Where ESS is defined as the (HESSID, ANQP Domain ID), (SSID, ANQP Domain ID) or - // (SSID, BSSID) tuple depending on AP configuration (in the above priority - // order). - repeated NumConnectableNetworksBucket observed_hotspot_r1_ess_in_scan_histogram = 85; - - // Histogram counting instances of scans with N many unique HotSpot 2.0 R2 ESS. - // Where ESS is defined as the (HESSID, ANQP Domain ID), (SSID, ANQP Domain ID) or - // (SSID, BSSID) tuple depending on AP configuration (in the above priority - // order). - repeated NumConnectableNetworksBucket observed_hotspot_r2_ess_in_scan_histogram = 86; - - // Histogram counting number of HotSpot 2.0 R1 APs per observed ESS in a scan - // (one value added per unique ESS - potentially multiple counts per single - // scan!) - repeated NumConnectableNetworksBucket observed_hotspot_r1_aps_per_ess_in_scan_histogram = 87; - - // Histogram counting number of HotSpot 2.0 R2 APs per observed ESS in a scan - // (one value added per unique ESS - potentially multiple counts per single - // scan!) - repeated NumConnectableNetworksBucket observed_hotspot_r2_aps_per_ess_in_scan_histogram = 88; - - // SoftAP event list tracking sessions and client counts in tethered mode - repeated SoftApConnectedClientsEvent soft_ap_connected_clients_events_tethered = 89; - - // SoftAP event list tracking sessions and client counts in local only mode - repeated SoftApConnectedClientsEvent soft_ap_connected_clients_events_local_only = 90; - - // Wps connection metrics - optional WpsMetrics wps_metrics = 91; - - // Wifi power statistics - optional WifiPowerStats wifi_power_stats = 92; - - // Number of connectivity single scan requests. - optional int32 num_connectivity_oneshot_scans = 93; - - // WifiWake statistics - optional WifiWakeStats wifi_wake_stats = 94; - - // Histogram counting instances of scans with N many 802.11mc (RTT) supporting APs - repeated NumConnectableNetworksBucket observed_80211mc_supporting_aps_in_scan_histogram = 95; - - // Total number of times supplicant crashed. - optional int32 num_supplicant_crashes = 96; - - // Total number of times hostapd crashed. - optional int32 num_hostapd_crashes = 97; - - // Indicates the number of times an error was encountered in - // supplicant on |WifiNative.setupInterfaceForClientMode|. - optional int32 num_setup_client_interface_failure_due_to_supplicant = 98; - - // Indicates the number of times an error was encountered in - // Wifi HAL on |WifiNative.setupInterfaceForSoftApMode|. - optional int32 num_setup_soft_ap_interface_failure_due_to_hal = 99; - - // Indicates the number of times an error was encountered in - // Wifi HAL on |WifiNative.setupInterfaceForSoftApMode|. - optional int32 num_setup_soft_ap_interface_failure_due_to_wificond = 100; - - // Indicates the number of times an error was encountered in - // Wifi HAL on |WifiNative.setupInterfaceForSoftApMode|. - optional int32 num_setup_soft_ap_interface_failure_due_to_hostapd = 101; - - // Indicates the number of times we got an interface down in client mode. - optional int32 num_client_interface_down = 102; - - // Indicates the number of times we got an interface down in softap mode. - optional int32 num_soft_ap_interface_down = 103; - - // Indicates the number of scan requests from external apps. - optional int32 num_external_app_oneshot_scan_requests = 104; - - // Indicates the number of times a scan request from an external foreground app was throttled. - optional int32 num_external_foreground_app_oneshot_scan_requests_throttled = 105; - - // Indicates the number of times a scan request from an external background app was throttled. - optional int32 num_external_background_app_oneshot_scan_requests_throttled = 106; - - // WifiLastResortWatchdog time milliseconds delta between trigger and first connection success - optional int64 watchdog_trigger_to_connection_success_duration_ms = 107 [default = -1]; - - // The number of times wifi experienced failures after watchdog has already been triggered and is - // waiting for a connection success - optional int64 watchdog_total_connection_failure_count_after_trigger = 108; - - // Number of times DFS channel scans are requested in single scan requests. - optional int32 num_oneshot_has_dfs_channel_scans = 109; - - // Wi-Fi RTT metrics - optional WifiRttLog wifi_rtt_log = 110; - - // Flag which indicates if Connected MAC Randomization is enabled - optional bool is_mac_randomization_on = 111 [default = false]; - - // Number of radio mode changes to MCC (Multi channel concurrency). - optional int32 num_radio_mode_change_to_mcc = 112; - - // Number of radio mode changes to SCC (Single channel concurrency). - optional int32 num_radio_mode_change_to_scc = 113; - - // Number of radio mode changes to SBS (Single band simultaneous). - optional int32 num_radio_mode_change_to_sbs = 114; - - // Number of radio mode changes to DBS (Dual band simultaneous). - optional int32 num_radio_mode_change_to_dbs = 115; - - // Number of times the firmware picked a SoftAp channel not satisfying user band preference. - optional int32 num_soft_ap_user_band_preference_unsatisfied = 116; - - // Identifier for experimental scoring parameter settings. - optional string score_experiment_id = 117; - - // Data on wifi radio usage - optional WifiRadioUsage wifi_radio_usage = 118; - - // Stores settings values used for metrics testing. - optional ExperimentValues experiment_values = 119; - - // List of WifiIsUnusableEvents which get logged when we notice that WiFi is unusable. - // Collected only when WIFI_IS_UNUSABLE_EVENT_METRICS_ENABLED Settings is enabled. - repeated WifiIsUnusableEvent wifi_is_unusable_event_list = 120; - - // Counts the occurrences of each link speed (Mbps) level - // with rssi (dBm) and rssi^2 sums (dBm^2) - repeated LinkSpeedCount link_speed_counts = 121; - - // Number of times the SarManager failed to register SAR sensor listener - optional int32 num_sar_sensor_registration_failures = 122; - - // Hardware revision (EVT, DVT, PVT etc.) - optional string hardware_revision = 124; - - // Total wifi link layer usage data over the logging duration in ms. - optional WifiLinkLayerUsageStats wifi_link_layer_usage_stats = 125; - - // Multiple lists of timestamped link layer stats with labels to represent whether wifi is usable - repeated WifiUsabilityStats wifi_usability_stats_list = 126; - - // Counts the occurrences of each Wifi usability score provided by external app - repeated WifiUsabilityScoreCount wifi_usability_score_count = 127; - - // List of PNO scan stats, one element for each mobility state - repeated DeviceMobilityStatePnoScanStats mobility_state_pno_stats_list = 128; - - // Wifi p2p statistics - optional WifiP2pStats wifi_p2p_stats = 129; - - // Easy Connect (DPP) metrics - optional WifiDppLog wifi_dpp_log = 130; - - // Number of Enhanced Open (OWE) networks in the saved networks. - optional int32 num_enhanced_open_networks = 131; - - // Number of WPA3-Personal networks. - optional int32 num_wpa3_personal_networks = 132; - - // Number of WPA3-Enterprise networks. - optional int32 num_wpa3_enterprise_networks = 133; - - // Total number of scan results for Enhanced open networks - optional int32 num_enhanced_open_network_scan_results = 134; - - // Total number of scan results for WPA3-Personal networks - optional int32 num_wpa3_personal_network_scan_results = 135; - - // Total number of scan results for WPA3-Enterprise networks - optional int32 num_wpa3_enterprise_network_scan_results = 136; - - // WifiConfigStore read/write metrics. - optional WifiConfigStoreIO wifi_config_store_io = 137; - - // Total number of saved networks with mac randomization enabled. - optional int32 num_saved_networks_with_mac_randomization = 138; - - // Link Probe metrics - optional LinkProbeStats link_probe_stats = 139; - - // List of NetworkSelectionExperimentDecisions stats for each experiment - repeated NetworkSelectionExperimentDecisions network_selection_experiment_decisions_list = 140; - - // Network Request API surface metrics. - optional WifiNetworkRequestApiLog wifi_network_request_api_log = 141; - - // Network Suggestion API surface metrics. - optional WifiNetworkSuggestionApiLog wifi_network_suggestion_api_log = 142; - - // WifiLock statistics - optional WifiLockStats wifi_lock_stats = 143; - - // Stats on number of times Wi-Fi is turned on/off though the WifiManager#setWifiEnabled API - optional WifiToggleStats wifi_toggle_stats = 144; - - // Number of times WifiManager#addOrUpdateNetwork is called. - optional int32 num_add_or_update_network_calls = 145; - - // Number of times WifiManager#enableNetwork is called. - optional int32 num_enable_network_calls = 146; - - // Passpoint provison metrics - optional PasspointProvisionStats passpoint_provision_stats = 147; - - // Histogram of the EAP method type of all installed Passpoint profiles for R1 - repeated PasspointProfileTypeCount installed_passpoint_profile_type_for_r1 = 123; - - // Histogram of the EAP method type of all installed Passpoint profiles for R2 - repeated PasspointProfileTypeCount installed_passpoint_profile_type_for_r2 = 148; - - // Histogram of Tx link speed at 2G - repeated Int32Count tx_link_speed_count_2g = 149; - - // Histogram of Tx link speed at 5G low band - repeated Int32Count tx_link_speed_count_5g_low = 150; - - // Histogram of Tx link speed at 5G middle band - repeated Int32Count tx_link_speed_count_5g_mid = 151; - - // Histogram of Tx link speed at 5G high band - repeated Int32Count tx_link_speed_count_5g_high = 152; - - // Histogram of Rx link speed at 2G - repeated Int32Count rx_link_speed_count_2g = 153; - - // Histogram of Rx link speed at 5G low band - repeated Int32Count rx_link_speed_count_5g_low = 154; - - // Histogram of Rx link speed at 5G middle band - repeated Int32Count rx_link_speed_count_5g_mid = 155; - - // Histogram of Rx link speed at 5G high band - repeated Int32Count rx_link_speed_count_5g_high = 156; -} - -// Information that gets logged for every WiFi connection. -message RouterFingerPrint { - - enum RoamType { - - // Type is unknown. - ROAM_TYPE_UNKNOWN = 0; - - // No roaming - usually happens on a single band (2.4 GHz) router. - ROAM_TYPE_NONE = 1; - - // Enterprise router. - ROAM_TYPE_ENTERPRISE = 2; - - // DBDC => Dual Band Dual Concurrent essentially a router that - // supports both 2.4 GHz and 5 GHz bands. - ROAM_TYPE_DBDC = 3; - } - - enum Auth { - - // Auth is unknown. - AUTH_UNKNOWN = 0; - - // No authentication. - AUTH_OPEN = 1; - - // If the router uses a personal authentication. - AUTH_PERSONAL = 2; - - // If the router is setup for enterprise authentication. - AUTH_ENTERPRISE = 3; - } - - enum RouterTechnology { - - // Router is unknown. - ROUTER_TECH_UNKNOWN = 0; - - // Router Channel A. - ROUTER_TECH_A = 1; - - // Router Channel B. - ROUTER_TECH_B = 2; - - // Router Channel G. - ROUTER_TECH_G = 3; - - // Router Channel N. - ROUTER_TECH_N = 4; - - // Router Channel AC. - ROUTER_TECH_AC = 5; - - // When the channel is not one of the above. - ROUTER_TECH_OTHER = 6; - } - - optional RoamType roam_type = 1; - - // Channel on which the connection takes place. - optional int32 channel_info = 2; - - // DTIM setting of the router. - optional int32 dtim = 3; - - // Authentication scheme of the router. - optional Auth authentication = 4; - - // If the router is hidden. - optional bool hidden = 5; - - // Channel information. - optional RouterTechnology router_technology = 6; - - // whether ipv6 is supported. - optional bool supports_ipv6 = 7; - - // If the router is a passpoint / hotspot 2.0 network - optional bool passpoint = 8; -} - -message ConnectionEvent { - - // Roam Type. - enum RoamType { - - // Type is unknown. - ROAM_UNKNOWN = 0; - - // No roaming. - ROAM_NONE = 1; - - // DBDC roaming. - ROAM_DBDC = 2; - - // Enterprise roaming. - ROAM_ENTERPRISE = 3; - - // User selected roaming. - ROAM_USER_SELECTED = 4; - - // Unrelated. - ROAM_UNRELATED = 5; - } - - // Connectivity Level Failure. - enum ConnectivityLevelFailure { - - // Failure is unknown. - HLF_UNKNOWN = 0; - - // No failure. - HLF_NONE = 1; - - // DHCP failure. - HLF_DHCP = 2; - - // No internet connection. - HLF_NO_INTERNET = 3; - - // No internet connection. - HLF_UNWANTED = 4; - } - - // Level 2 failure reason. - enum Level2FailureReason { - - // Unknown default - FAILURE_REASON_UNKNOWN = 0; - - // The reason code if there is no error during authentication. It could - // also imply that there no authentication in progress. - AUTH_FAILURE_NONE = 1; - - // The reason code if there was a timeout authenticating. - AUTH_FAILURE_TIMEOUT = 2; - - // The reason code if there was a wrong password while authenticating. - AUTH_FAILURE_WRONG_PSWD = 3; - - // The reason code if there was EAP failure while authenticating. - AUTH_FAILURE_EAP_FAILURE = 4; - - // The reason code if the AP can no longer accept new clients. - ASSOCIATION_REJECTION_AP_UNABLE_TO_HANDLE_NEW_STA = 5; - } - - // Entity that recommended connecting to this network. - enum ConnectionNominator { - // Unknown nominator - NOMINATOR_UNKNOWN = 0; - - // User selected network manually - NOMINATOR_MANUAL = 1; - - // Saved network - NOMINATOR_SAVED = 2; - - // Suggestion API - NOMINATOR_SUGGESTION = 3; - - // Passpoint - NOMINATOR_PASSPOINT = 4; - - // Carrier suggestion - NOMINATOR_CARRIER = 5; - - // External scorer - NOMINATOR_EXTERNAL_SCORED = 6; - - // Network Specifier - NOMINATOR_SPECIFIER = 7; - - // User connected choice override - NOMINATOR_SAVED_USER_CONNECT_CHOICE = 8; - - // Open Network Available Pop-up - NOMINATOR_OPEN_NETWORK_AVAILABLE = 9; - } - - // Start time of the connection. - optional int64 start_time_millis = 1;// [(datapol.semantic_type) = ST_TIMESTAMP]; - - // Duration to connect. - optional int32 duration_taken_to_connect_millis = 2; - - // Router information. - optional RouterFingerPrint router_fingerprint = 3; - - // RSSI at the start of the connection. - optional int32 signal_strength = 4; - - // Roam Type. - optional RoamType roam_type = 5; - - // Result of the connection. - optional int32 connection_result = 6; - - // Reasons for level 2 failure (needs to be coordinated with wpa-supplicant). - optional int32 level_2_failure_code = 7; - - // Failures that happen at the connectivity layer. - optional ConnectivityLevelFailure connectivity_level_failure_code = 8; - - // Has bug report been taken. - optional bool automatic_bug_report_taken = 9; - - // Connection is using locally generated random MAC address. - optional bool use_randomized_mac = 10 [default = false]; - - // Who chose to connect. - optional ConnectionNominator connection_nominator = 11; - - // The currently running network selector when this connection event occurred. - optional int32 network_selector_experiment_id = 12; - - // Breakdown of level_2_failure_code with more detailed reason. - optional Level2FailureReason level_2_failure_reason = 13 - [default = FAILURE_REASON_UNKNOWN]; -} - -// Number of occurrences of a specific RSSI poll rssi value -message RssiPollCount { - // RSSI - optional int32 rssi = 1; - - // Number of RSSI polls with 'rssi' - optional int32 count = 2; - - // Beacon frequency of the channel in MHz - optional int32 frequency = 3; -} - -// Number of occurrences of a specific alert reason value -message AlertReasonCount { - // Alert reason - optional int32 reason = 1; - - // Number of alerts with |reason|. - optional int32 count = 2; -} - -// Counts the number of instances of a specific Wifi Score calculated by WifiScoreReport -message WifiScoreCount { - // Wifi Score - optional int32 score = 1; - - // Number of Wifi score reports with this score - optional int32 count = 2; -} - -// Counts the number of instances of a specific Wifi Usability Score -message WifiUsabilityScoreCount { - // Wifi Usability Score - optional int32 score = 1; - - // Number of Wifi score reports with this score - optional int32 count = 2; -} - -// Number of occurrences of a specific link speed (Mbps) -// and sum of rssi (dBm) and rssi^2 (dBm^2) -message LinkSpeedCount { - // Link speed (Mbps) - optional int32 link_speed_mbps = 1; - - // Number of RSSI polls with link_speed - optional int32 count = 2; - - // Sum of absolute values of rssi values (dBm) - optional int32 rssi_sum_dbm = 3; - - // Sum of squares of rssi values (dBm^2) - optional int64 rssi_sum_of_squares_dbm_sq = 4; -} - - -// Number of occurrences of Soft AP session durations -message SoftApDurationBucket { - // Bucket covers duration : [duration_sec, duration_sec + bucket_size_sec) - // The (inclusive) lower bound of Soft AP session duration represented by this bucket - optional int32 duration_sec = 1; - - // The size of this bucket - optional int32 bucket_size_sec = 2; - - // Number of soft AP session durations that fit into this bucket - optional int32 count = 3; -} - -// Number of occurrences of a soft AP session return code -message SoftApReturnCodeCount { - - enum SoftApStartResult { - - // SoftApManager return code unknown - SOFT_AP_RETURN_CODE_UNKNOWN = 0; - - // SoftAp started successfully - SOFT_AP_STARTED_SUCCESSFULLY = 1; - - // Catch all for failures with no specific failure reason - SOFT_AP_FAILED_GENERAL_ERROR = 2; - - // SoftAp failed to start due to NO_CHANNEL error - SOFT_AP_FAILED_NO_CHANNEL = 3; - } - - // Historical, no longer used for writing as of 01/2017. - optional int32 return_code = 1 [deprecated = true]; - - // Occurrences of this soft AP return code - optional int32 count = 2; - - // Result of attempt to start SoftAp - optional SoftApStartResult start_result = 3; -} - -message StaEvent { - message ConfigInfo { - // The set of key management protocols supported by this configuration. - optional uint32 allowed_key_management = 1 [default = 0]; - - // The set of security protocols supported by this configuration. - optional uint32 allowed_protocols = 2 [default = 0]; - - // The set of authentication protocols supported by this configuration. - optional uint32 allowed_auth_algorithms = 3 [default = 0]; - - // The set of pairwise ciphers for WPA supported by this configuration. - optional uint32 allowed_pairwise_ciphers = 4 [default = 0]; - - // The set of group ciphers supported by this configuration. - optional uint32 allowed_group_ciphers = 5; - - // Is this a 'hidden network' - optional bool hidden_ssid = 6; - - // Is this a Hotspot 2.0 / passpoint network - optional bool is_passpoint = 7; - - // Is this an 'ephemeral' network (Not in saved network list, recommended externally) - optional bool is_ephemeral = 8; - - // Has a successful connection ever been established using this WifiConfiguration - optional bool has_ever_connected = 9; - - // RSSI of the scan result candidate associated with this WifiConfiguration - optional int32 scan_rssi = 10 [default = -127]; - - // Frequency of the scan result candidate associated with this WifiConfiguration - optional int32 scan_freq = 11 [default = -1]; - } - - enum EventType { - // Default/Invalid event - TYPE_UNKNOWN = 0; - - // Supplicant Association Rejection event. Code contains the 802.11 - TYPE_ASSOCIATION_REJECTION_EVENT = 1; - - // Supplicant L2 event, - TYPE_AUTHENTICATION_FAILURE_EVENT = 2; - - // Supplicant L2 event - TYPE_NETWORK_CONNECTION_EVENT = 3; - - // Supplicant L2 event - TYPE_NETWORK_DISCONNECTION_EVENT = 4; - - // Supplicant L2 event - TYPE_SUPPLICANT_STATE_CHANGE_EVENT = 5; - - // Supplicant L2 event - TYPE_CMD_ASSOCIATED_BSSID = 6; - - // IP Manager successfully completed IP Provisioning - TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL = 7; - - // IP Manager failed to complete IP Provisioning - TYPE_CMD_IP_CONFIGURATION_LOST = 8; - - // IP Manager lost reachability to network neighbors - TYPE_CMD_IP_REACHABILITY_LOST = 9; - - // Indicator that Supplicant is targeting a BSSID for roam/connection - TYPE_CMD_TARGET_BSSID = 10; - - // Wifi framework is initiating a connection attempt - TYPE_CMD_START_CONNECT = 11; - - // Wifi framework is initiating a roaming connection attempt - TYPE_CMD_START_ROAM = 12; - - // SystemAPI connect() command, Settings App - TYPE_CONNECT_NETWORK = 13; - - // Network Agent has validated the internet connection (Captive Portal Check success, or user - // validation) - TYPE_NETWORK_AGENT_VALID_NETWORK = 14; - - // Framework initiated disconnect. Sometimes generated to give an extra reason for a disconnect - // Should typically be followed by a NETWORK_DISCONNECTION_EVENT with a local_gen = true - TYPE_FRAMEWORK_DISCONNECT = 15; - - // The NetworkAgent score for wifi has changed in a way that may impact - // connectivity - TYPE_SCORE_BREACH = 16; - - // Framework changed Sta interface MAC address - TYPE_MAC_CHANGE = 17; - - // Wifi is turned on - TYPE_WIFI_ENABLED = 18; - - // Wifi is turned off - TYPE_WIFI_DISABLED = 19; - - // The NetworkAgent Wifi usability score has changed in a way that may - // impact connectivity - TYPE_WIFI_USABILITY_SCORE_BREACH = 20; - - // Link probe was performed - TYPE_LINK_PROBE = 21; - } - - enum FrameworkDisconnectReason { - // default/none/unknown value - DISCONNECT_UNKNOWN = 0; - - // API DISCONNECT - DISCONNECT_API = 1; - - // Some framework internal reason (generic) - DISCONNECT_GENERIC = 2; - - // Network Agent network validation failed, user signaled network unwanted - DISCONNECT_UNWANTED = 3; - - // Roaming timed out - DISCONNECT_ROAM_WATCHDOG_TIMER = 4; - - // P2P service requested wifi disconnect - DISCONNECT_P2P_DISCONNECT_WIFI_REQUEST = 5; - - // SIM was removed while using a SIM config - DISCONNECT_RESET_SIM_NETWORKS = 6; - } - - // Authentication Failure reasons as reported through the API. - enum AuthFailureReason { - // Unknown default - AUTH_FAILURE_UNKNOWN = 0; - - // The reason code if there is no error during authentication. It could also imply that there no - // authentication in progress, - AUTH_FAILURE_NONE = 1; - - // The reason code if there was a timeout authenticating. - AUTH_FAILURE_TIMEOUT = 2; - - // The reason code if there was a wrong password while authenticating. - AUTH_FAILURE_WRONG_PSWD = 3; - - // The reason code if there was EAP failure while authenticating. - AUTH_FAILURE_EAP_FAILURE = 4; - } - - // What event was this - optional EventType type = 1; - - // 80211 death reason code, relevant to NETWORK_DISCONNECTION_EVENTs - optional int32 reason = 2 [default = -1]; - - // 80211 Association Status code, relevant to ASSOCIATION_REJECTION_EVENTs - optional int32 status = 3 [default = -1]; - - // Designates whether a NETWORK_DISCONNECT_EVENT was by the STA or AP - optional bool local_gen = 4 [default = false]; - - // Network information from the WifiConfiguration of a framework initiated connection attempt - optional ConfigInfo config_info = 5; - - // RSSI from the last rssi poll (Only valid for active connections) - optional int32 last_rssi = 6 [default = -127]; - - // Link speed from the last rssi poll (Only valid for active connections) - optional int32 last_link_speed = 7 [default = -1]; - - // Frequency from the last rssi poll (Only valid for active connections) - optional int32 last_freq = 8 [default = -1]; - - // Enum used to define bit positions in the supplicant_state_change_bitmask - // See {@code frameworks/base/wifi/java/android/net/wifi/SupplicantState.java} for documentation - enum SupplicantState { - STATE_DISCONNECTED = 0; - - STATE_INTERFACE_DISABLED = 1; - - STATE_INACTIVE = 2; - - STATE_SCANNING = 3; - - STATE_AUTHENTICATING = 4; - - STATE_ASSOCIATING = 5; - - STATE_ASSOCIATED = 6; - - STATE_FOUR_WAY_HANDSHAKE = 7; - - STATE_GROUP_HANDSHAKE = 8; - - STATE_COMPLETED = 9; - - STATE_DORMANT = 10; - - STATE_UNINITIALIZED = 11; - - STATE_INVALID = 12; - } - - // Bit mask of all supplicant state changes that occurred since the last event - optional uint32 supplicant_state_changes_bitmask = 9 [default = 0]; - - // The number of milliseconds that have elapsed since the device booted - optional int64 start_time_millis = 10 [default = 0]; - - optional FrameworkDisconnectReason framework_disconnect_reason = 11 [default = DISCONNECT_UNKNOWN]; - - // Flag which indicates if an association rejection event occurred due to a timeout - optional bool association_timed_out = 12 [default = false]; - - // Authentication failure reason, as reported by WifiManager (calculated from state & deauth code) - optional AuthFailureReason auth_failure_reason = 13 [default = AUTH_FAILURE_UNKNOWN]; - - // NetworkAgent score of connected wifi - optional int32 last_score = 14 [default = -1]; - - // NetworkAgent Wifi usability score of connected wifi - optional int32 last_wifi_usability_score = 15 [default = -1]; - - // Prediction horizon (in second) of Wifi usability score provided by external - // system app - optional int32 last_prediction_horizon_sec = 16 [default = -1]; - - // Only valid if event type == TYPE_LINK_PROBE. - // true if link probe succeeded, false otherwise. - optional bool link_probe_was_success = 17; - - // Only valid if event type == TYPE_LINK_PROBE and link_probe_was_success == true. - // Elapsed time, in milliseconds, of a successful link probe. - optional int32 link_probe_success_elapsed_time_ms = 18; - - // Only valid if event type == TYPE_LINK_PROBE and link_probe_was_success == false. - // Failure reason for an unsuccessful link probe. - optional LinkProbeStats.LinkProbeFailureReason link_probe_failure_reason = 19; -} - -// Wi-Fi Aware metrics -message WifiAwareLog { - // total number of unique apps that used Aware (measured on attach) - optional int32 num_apps = 1; - - // total number of unique apps that used an identity callback when attaching - optional int32 num_apps_using_identity_callback = 2; - - // maximum number of attaches for an app - optional int32 max_concurrent_attach_sessions_in_app = 3; - - // histogram of attach request results - repeated NanStatusHistogramBucket histogram_attach_session_status = 4; - - // maximum number of concurrent publish sessions in a single app - optional int32 max_concurrent_publish_in_app = 5; - - // maximum number of concurrent subscribe sessions in a single app - optional int32 max_concurrent_subscribe_in_app = 6; - - // maximum number of concurrent discovery (publish+subscribe) sessions in a single app - optional int32 max_concurrent_discovery_sessions_in_app = 7; - - // maximum number of concurrent publish sessions in the system - optional int32 max_concurrent_publish_in_system = 8; - - // maximum number of concurrent subscribe sessions in the system - optional int32 max_concurrent_subscribe_in_system = 9; - - // maximum number of concurrent discovery (publish+subscribe) sessions in the system - optional int32 max_concurrent_discovery_sessions_in_system = 10; - - // histogram of publish request results - repeated NanStatusHistogramBucket histogram_publish_status = 11; - - // histogram of subscribe request results - repeated NanStatusHistogramBucket histogram_subscribe_status = 12; - - // number of unique apps which experienced a discovery session creation failure due to lack of - // resources - optional int32 num_apps_with_discovery_session_failure_out_of_resources = 13; - - // histogram of create ndp request results - repeated NanStatusHistogramBucket histogram_request_ndp_status = 14; - - // histogram of create ndp out-of-band (OOB) request results - repeated NanStatusHistogramBucket histogram_request_ndp_oob_status = 15; - - // maximum number of concurrent active data-interfaces (NDI) in a single app - optional int32 max_concurrent_ndi_in_app = 19; - - // maximum number of concurrent active data-interfaces (NDI) in the system - optional int32 max_concurrent_ndi_in_system = 20; - - // maximum number of concurrent data-paths (NDP) in a single app - optional int32 max_concurrent_ndp_in_app = 21; - - // maximum number of concurrent data-paths (NDP) in the system - optional int32 max_concurrent_ndp_in_system = 22; - - // maximum number of concurrent secure data-paths (NDP) in a single app - optional int32 max_concurrent_secure_ndp_in_app = 23; - - // maximum number of concurrent secure data-paths (NDP) in the system - optional int32 max_concurrent_secure_ndp_in_system = 24; - - // maximum number of concurrent data-paths (NDP) per data-interface (NDI) - optional int32 max_concurrent_ndp_per_ndi = 25; - - // histogram of durations of Aware being available - repeated HistogramBucket histogram_aware_available_duration_ms = 26; - - // histogram of durations of Aware being enabled - repeated HistogramBucket histogram_aware_enabled_duration_ms = 27; - - // histogram of duration (in ms) of attach sessions - repeated HistogramBucket histogram_attach_duration_ms = 28; - - // histogram of duration (in ms) of publish sessions - repeated HistogramBucket histogram_publish_session_duration_ms = 29; - - // histogram of duration (in ms) of subscribe sessions - repeated HistogramBucket histogram_subscribe_session_duration_ms = 30; - - // histogram of duration (in ms) of data-paths (NDP) - repeated HistogramBucket histogram_ndp_session_duration_ms = 31; - - // histogram of usage (in MB) of data-paths (NDP) - repeated HistogramBucket histogram_ndp_session_data_usage_mb = 32; - - // histogram of usage (in MB) of data-path creation time (in ms) measured as request -> confirm - repeated HistogramBucket histogram_ndp_creation_time_ms = 33; - - // statistics for data-path (NDP) creation time (in ms) measured as request -> confirm: minimum - optional int64 ndp_creation_time_ms_min = 34; - - // statistics for data-path (NDP) creation time (in ms) measured as request -> confirm: maximum - optional int64 ndp_creation_time_ms_max = 35; - - // statistics for data-path (NDP) creation time (in ms) measured as request -> confirm: sum - optional int64 ndp_creation_time_ms_sum = 36; - - // statistics for data-path (NDP) creation time (in ms) measured as request -> confirm: sum of sq - optional int64 ndp_creation_time_ms_sum_of_sq = 37; - - // statistics for data-path (NDP) creation time (in ms) measured as request -> confirm: number of - // samples - optional int64 ndp_creation_time_ms_num_samples = 38; - - // total time within the logging window that aware was available - optional int64 available_time_ms = 39; - - // total time within the logging window that aware was enabled - optional int64 enabled_time_ms = 40; - - // maximum number of concurrent publish sessions enabling ranging in a single app - optional int32 max_concurrent_publish_with_ranging_in_app = 41; - - // maximum number of concurrent subscribe sessions specifying a geofence in a single app - optional int32 max_concurrent_subscribe_with_ranging_in_app = 42; - - // maximum number of concurrent publish sessions enabling ranging in the system - optional int32 max_concurrent_publish_with_ranging_in_system = 43; - - // maximum number of concurrent subscribe sessions specifying a geofence in the system - optional int32 max_concurrent_subscribe_with_ranging_in_system = 44; - - // histogram of subscribe session geofence minimum (only when specified) - repeated HistogramBucket histogram_subscribe_geofence_min = 45; - - // histogram of subscribe session geofence maximum (only when specified) - repeated HistogramBucket histogram_subscribe_geofence_max = 46; - - // total number of subscribe sessions which enabled ranging - optional int32 num_subscribes_with_ranging = 47; - - // total number of matches (service discovery indication) with ranging provided - optional int32 num_matches_with_ranging = 48; - - // total number of matches (service discovery indication) for service discovery with ranging - // enabled which did not trigger ranging - optional int32 num_matches_without_ranging_for_ranging_enabled_subscribes = 49; - - // Histogram bucket for Wi-Fi Aware logs. Range is [start, end) - message HistogramBucket { - // lower range of the bucket (inclusive) - optional int64 start = 1; - - // upper range of the bucket (exclusive) - optional int64 end = 2; - - // number of samples in the bucket - optional int32 count = 3; - } - - // Status of various NAN operations - enum NanStatusTypeEnum { - // constant to be used by proto - UNKNOWN = 0; - - // NAN operation succeeded - SUCCESS = 1; - - // NAN Discovery Engine/Host driver failures - INTERNAL_FAILURE = 2; - - // NAN OTA failures - PROTOCOL_FAILURE = 3; - - // The publish/subscribe discovery session id is invalid - INVALID_SESSION_ID = 4; - - // Out of resources to fufill request - NO_RESOURCES_AVAILABLE = 5; - - // Invalid arguments passed - INVALID_ARGS = 6; - - // Invalid peer id - INVALID_PEER_ID = 7; - - // Invalid NAN data-path (ndp) id - INVALID_NDP_ID = 8; - - // Attempting to enable NAN when not available, e.g. wifi is disabled - NAN_NOT_ALLOWED = 9; - - // Over the air ACK not received - NO_OTA_ACK = 10; - - // Attempting to enable NAN when already enabled - ALREADY_ENABLED = 11; - - // Can't queue tx followup message foor transmission - FOLLOWUP_TX_QUEUE_FULL = 12; - - // Unsupported concurrency of NAN and another feature - NAN disabled - UNSUPPORTED_CONCURRENCY_NAN_DISABLED = 13; - - // Unknown NanStatusType - UNKNOWN_HAL_STATUS = 14; - } - - // Histogram bucket for Wi-Fi Aware (NAN) status. - message NanStatusHistogramBucket { - // status type defining the bucket - optional NanStatusTypeEnum nan_status_type = 1; - - // number of samples in the bucket - optional int32 count = 2; - } -} - -// Data point used to build 'Number of Connectable Network' histograms -message NumConnectableNetworksBucket { - // Number of connectable networks seen in a scan result - optional int32 num_connectable_networks = 1 [default = 0]; - - // Number of scan results with num_connectable_networks - optional int32 count = 2 [default = 0]; -} - -// Pno scan metrics -// Here "Pno Scan" refers to the session of offloaded scans, these metrics count the result of a -// single session, and not the individual scans within that session. -message PnoScanMetrics { - // Total number of attempts to offload pno scans - optional int32 num_pno_scan_attempts = 1; - - // Total number of pno scans failed - optional int32 num_pno_scan_failed = 2; - - // Number of pno scans started successfully over offload - optional int32 num_pno_scan_started_over_offload = 3; - - // Number of pno scans failed over offload - optional int32 num_pno_scan_failed_over_offload = 4; - - // Total number of pno scans that found any network - optional int32 num_pno_found_network_events = 5; -} - -// Number of occurrences for a particular "Connect to Network" Notification or -// notification Action. -message ConnectToNetworkNotificationAndActionCount { - - // "Connect to Network" notifications - enum Notification { - - // Default - NOTIFICATION_UNKNOWN = 0; - - // Initial notification with a recommended network. - NOTIFICATION_RECOMMEND_NETWORK = 1; - - // Notification when connecting to the recommended network. - NOTIFICATION_CONNECTING_TO_NETWORK = 2; - - // Notification when successfully connected to the network. - NOTIFICATION_CONNECTED_TO_NETWORK = 3; - - // Notification when failed to connect to network. - NOTIFICATION_FAILED_TO_CONNECT = 4; - } - - // "Connect to Network" notification actions - enum Action { - - // Default - ACTION_UNKNOWN = 0; - - // User dismissed the "Connect to Network" notification. - ACTION_USER_DISMISSED_NOTIFICATION = 1; - - // User tapped action button to connect to recommended network. - ACTION_CONNECT_TO_NETWORK = 2; - - // User tapped action button to open Wi-Fi Settings. - ACTION_PICK_WIFI_NETWORK = 3; - - // User tapped "Failed to connect" notification to open Wi-Fi Settings. - ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE = 4; - } - - // Recommenders of the "Connect to Network" notification - enum Recommender { - - // Default. - RECOMMENDER_UNKNOWN = 0; - - // Open Network Available recommender. - RECOMMENDER_OPEN = 1; - } - - // Notification Type. - optional Notification notification = 1; - - // Action Type. - optional Action action = 2; - - // Recommender Type. - optional Recommender recommender = 3; - - // Occurrences of this action. - optional int32 count = 4; -} - -// SoftAP event tracking sessions and client counts -message SoftApConnectedClientsEvent { - - // Soft AP event Types - enum SoftApEventType { - - // Soft AP is Up and ready for use - SOFT_AP_UP = 0; - - // Soft AP is Down - SOFT_AP_DOWN = 1; - - // Number of connected soft AP clients has changed - NUM_CLIENTS_CHANGED = 2; - } - - // Soft AP channel bandwidth types - enum ChannelBandwidth { - - BANDWIDTH_INVALID = 0; - - BANDWIDTH_20_NOHT = 1; - - BANDWIDTH_20 = 2; - - BANDWIDTH_40 = 3; - - BANDWIDTH_80 = 4; - - BANDWIDTH_80P80 = 5; - - BANDWIDTH_160 = 6; - } - - // Type of event being recorded - optional SoftApEventType event_type = 1; - - // Time passed since last boot in milliseconds - optional int64 time_stamp_millis = 2; - - // Number of connected clients if event_type is NUM_CLIENTS_CHANGED, otherwise zero. - optional int32 num_connected_clients = 3; - - // Channel frequency used for Soft AP - optional int32 channel_frequency = 4; - - // Channel bandwidth used for Soft AP - optional ChannelBandwidth channel_bandwidth = 5; -} - -// Wps connection metrics -// Keeps track of Wi-Fi Protected Setup usage -message WpsMetrics { - // Total number of wps connection attempts - optional int32 num_wps_attempts = 1; - - // Total number of wps connection successes - optional int32 num_wps_success = 2; - - // Total number of wps failures on start - optional int32 num_wps_start_failure = 3; - - // Total number of wps overlap failure - optional int32 num_wps_overlap_failure = 4; - - // Total number of wps timeout failure - optional int32 num_wps_timeout_failure = 5; - - // Total number of other wps failure during connection - optional int32 num_wps_other_connection_failure = 6; - - // Total number of supplicant failure after wps - optional int32 num_wps_supplicant_failure = 7; - - // Total number of wps cancellation - optional int32 num_wps_cancellation = 8; -} - -// Power stats for Wifi -message WifiPowerStats { - - // Duration of log (ms) - optional int64 logging_duration_ms = 1; - - // Energy consumed by wifi (mAh) - optional double energy_consumed_mah = 2; - - // Amount of time wifi is in idle (ms) - optional int64 idle_time_ms = 3; - - // Amount of time wifi is in rx (ms) - optional int64 rx_time_ms = 4; - - // Amount of time wifi is in tx (ms) - optional int64 tx_time_ms = 5; - - // Amount of time kernel is active because of wifi data (ms) - optional int64 wifi_kernel_active_time_ms = 6; - - // Number of packets sent (tx) - optional int64 num_packets_tx = 7; - - // Number of bytes sent (tx) - optional int64 num_bytes_tx = 8; - - // Number of packets received (rx) - optional int64 num_packets_rx = 9; - - // Number of bytes sent (rx) - optional int64 num_bytes_rx = 10; - - // Amount of time wifi is in sleep (ms) - optional int64 sleep_time_ms = 11; - - // Amount of time wifi is scanning (ms) - optional int64 scan_time_ms = 12; - - // Actual monitored rail energy consumed by wifi (mAh) - optional double monitored_rail_energy_consumed_mah = 13; -} - -// Metrics for Wifi Wake -message WifiWakeStats { - // An individual session for Wifi Wake - message Session { - // A Wifi Wake lifecycle event - message Event { - // Elapsed time in milliseconds since start of session. - optional int64 elapsed_time_millis = 1; - - // Number of scans that have occurred since start of session. - optional int32 elapsed_scans = 2; - } - - // Start time of session in milliseconds. - optional int64 start_time_millis = 1; - - // The number of networks the lock was provided with at start. - optional int32 locked_networks_at_start = 2; - - // The number of networks in the lock at the time of the initialize event. Only valid if - // initialize_event is recorded. - optional int32 locked_networks_at_initialize = 6; - - // Event for fully initializing the WakeupLock (i.e. WakeupLock is "locked"). - optional Event initialize_event = 7; - - // Event for unlocking the WakeupLock. Does not occur if lock was initialized with 0 networks. - optional Event unlock_event = 3; - - // Event for triggering wakeup. - optional Event wakeup_event = 4; - - // Event for WifiWake reset event. This event marks the end of a session. - optional Event reset_event = 5; - } - - // Total number of sessions for Wifi Wake. - optional int32 num_sessions = 1; - - // Session information for every Wifi Wake session (up to a maximum of 10). - repeated Session sessions = 2; - - // Number of ignored calls to start (due to WakeupController already being active). - optional int32 num_ignored_starts = 3; - - // Number of Wifi Wake sessions that have recorded wakeup events. - optional int32 num_wakeups = 4; -} - -// Metrics for Wi-Fi RTT -message WifiRttLog { - // Number of RTT request API calls - optional int32 num_requests = 1; - - // Histogram of RTT operation overall status - repeated RttOverallStatusHistogramBucket histogram_overall_status = 2; - - // RTT to Access Points metrics - optional RttToPeerLog rtt_to_ap = 3; - - // RTT to Wi-Fi Aware peers metrics - optional RttToPeerLog rtt_to_aware = 4; - - // Metrics for a RTT to Peer (peer = AP or Wi-Fi Aware) - message RttToPeerLog { - // Total number of API calls - optional int32 num_requests = 1; - - // Total number of individual requests - optional int32 num_individual_requests = 2; - - // Total number of apps which requested RTT - optional int32 num_apps = 3; - - // Histogram of total number of RTT requests by an app (WifiRttManager#startRanging) - repeated HistogramBucket histogram_num_requests_per_app = 4; - - // Histogram of number of peers in a single RTT request (RangingRequest entries) - repeated HistogramBucket histogram_num_peers_per_request = 5; - - // Histogram of status of individual RTT operations (RangingResult entries) - repeated RttIndividualStatusHistogramBucket histogram_individual_status = 6; - - // Histogram of measured distances (RangingResult entries) - repeated HistogramBucket histogram_distance = 7; - - // Histogram of interval of RTT requests by an app (WifiRttManager#startRanging) - repeated HistogramBucket histogram_request_interval_ms = 8; - } - - // Histogram bucket for Wi-Fi RTT logs. Range is [start, end) - message HistogramBucket { - // lower range of the bucket (inclusive) - optional int64 start = 1; - - // upper range of the bucket (exclusive) - optional int64 end = 2; - - // number of samples in the bucket - optional int32 count = 3; - } - - // Status codes for overall RTT operation - enum RttOverallStatusTypeEnum { - // constant to be used by proto - OVERALL_UNKNOWN = 0; - - // RTT operation succeeded (individual results may still fail) - OVERALL_SUCCESS = 1; - - // RTT operation failed (unspecified reason) - OVERALL_FAIL = 2; - - // RTT operation failed since RTT was not available (e.g. Airplane mode) - OVERALL_RTT_NOT_AVAILABLE = 3; - - // RTT operation timed-out: didn't receive response from HAL in expected time - OVERALL_TIMEOUT = 4; - - // RTT operation aborted since the app is spamming the service - OVERALL_THROTTLE = 5; - - // RTT request to HAL received immediate failure - OVERALL_HAL_FAILURE = 6; - - // RTT to Wi-Fi Aware peer using PeerHandle failed to get a MAC address translation - OVERALL_AWARE_TRANSLATION_FAILURE = 7; - - // RTT operation failed due to missing Location permission (post execution) - OVERALL_LOCATION_PERMISSION_MISSING = 8; - } - - // Status codes for individual RTT operation - enum RttIndividualStatusTypeEnum { - // constant to be used by proto - UNKNOWN = 0; - - // RTT operation succeeded - SUCCESS = 1; - - // RTT failure: generic reason (no further information) - FAILURE = 2; - - // Target STA does not respond to request - FAIL_NO_RSP = 3; - - // Request rejected. Applies to 2-sided RTT only - FAIL_REJECTED = 4; - - // Operation not scheduled - FAIL_NOT_SCHEDULED_YET = 5; - - // Timing measurement times out - FAIL_TM_TIMEOUT = 6; - - // Target on different channel, cannot range - FAIL_AP_ON_DIFF_CHANNEL = 7; - - // Ranging not supported - FAIL_NO_CAPABILITY = 8; - - // Request aborted for unknown reason - ABORTED = 9; - - // Invalid T1-T4 timestamp - FAIL_INVALID_TS = 10; - - // 11mc protocol failed - FAIL_PROTOCOL = 11; - - // Request could not be scheduled - FAIL_SCHEDULE = 12; - - // Responder cannot collaborate at time of request - FAIL_BUSY_TRY_LATER = 13; - - // Bad request args - INVALID_REQ = 14; - - // WiFi not enabled - NO_WIFI = 15; - - // Responder overrides param info, cannot range with new params - FAIL_FTM_PARAM_OVERRIDE = 16; - - // HAL did not provide a result to a framework request - MISSING_RESULT = 17; - } - - // Histogram bucket for Wi-Fi RTT overall operation status - message RttOverallStatusHistogramBucket { - // status type defining the bucket - optional RttOverallStatusTypeEnum status_type = 1; - - // number of samples in the bucket - optional int32 count = 2; - } - - // Histogram bucket for Wi-Fi RTT individual operation status - message RttIndividualStatusHistogramBucket { - // status type defining the bucket - optional RttIndividualStatusTypeEnum status_type = 1; - - // number of samples in the bucket - optional int32 count = 2; - } -} - -// Usage data for the wifi radio while device is running on battery. -message WifiRadioUsage { - // Duration of log (ms) - optional int64 logging_duration_ms = 1; - - // Total time for which the radio is awake due to scan. - optional int64 scan_time_ms = 2; -} - -message ExperimentValues { - // Indicates if we are logging WifiIsUnusableEvent in metrics - optional bool wifi_is_unusable_logging_enabled = 1; - - // Minimum number of txBad to trigger a data stall - optional int32 wifi_data_stall_min_tx_bad = 2; - - // Minimum number of txSuccess to trigger a data stall - // when rxSuccess is 0 - optional int32 wifi_data_stall_min_tx_success_without_rx = 3; - - // Indicates if we are logging LinkSpeedCount in metrics - optional bool link_speed_counts_logging_enabled = 4; - - // Duration for evaluating Wifi condition to trigger a data stall - // measured in milliseconds - optional int32 data_stall_duration_ms = 5; - - // Threshold of Tx throughput below which to trigger a data stall - // measured in Kbps - optional int32 data_stall_tx_tput_thr_kbps = 6; - - // Threshold of Rx throughput below which to trigger a data stall - // measured in Kbps - optional int32 data_stall_rx_tput_thr_kbps = 7; - - // Threshold of Tx packet error rate above which to trigger a data stall - // in percentage - optional int32 data_stall_tx_per_thr = 8; - - // Threshold of CCA level above which to trigger a data stall in percentage - optional int32 data_stall_cca_level_thr = 9; -} - -message WifiIsUnusableEvent { - enum TriggerType { - // Default/Invalid event - TYPE_UNKNOWN = 0; - - // There is a data stall from tx failures - TYPE_DATA_STALL_BAD_TX = 1; - - // There is a data stall from rx failures - TYPE_DATA_STALL_TX_WITHOUT_RX = 2; - - // There is a data stall from both tx and rx failures - TYPE_DATA_STALL_BOTH = 3; - - // Firmware generated an alert - TYPE_FIRMWARE_ALERT = 4; - - // IP Manager lost reachability to network neighbors - TYPE_IP_REACHABILITY_LOST = 5; - } - - // What event triggered WifiIsUnusableEvent. - optional TriggerType type = 1; - - // The timestamp at which this event occurred. - // Measured in milliseconds that have elapsed since the device booted. - optional int64 start_time_millis = 2; - - // NetworkAgent score of connected wifi. - // Defaults to -1 if the score was never set. - optional int32 last_score = 3 [default = -1]; - - // Delta of successfully transmitted (ACKed) unicast data packets - // between the last two WifiLinkLayerStats. - optional int64 tx_success_delta = 4; - - // Delta of transmitted unicast data retry packets - // between the last two WifiLinkLayerStats. - optional int64 tx_retries_delta = 5; - - // Delta of lost (not ACKed) transmitted unicast data packets - // between the last two WifiLinkLayerStats. - optional int64 tx_bad_delta = 6; - - // Delta of received unicast data packets - // between the last two WifiLinkLayerStats. - optional int64 rx_success_delta = 7; - - // Time in millisecond between the last two WifiLinkLayerStats. - optional int64 packet_update_time_delta = 8; - - // The timestamp at which the last WifiLinkLayerStats was updated. - // Measured in milliseconds that have elapsed since the device booted. - optional int64 last_link_layer_stats_update_time = 9; - - // Firmware alert code. Only valid when the event was triggered by a firmware alert, otherwise -1. - optional int32 firmware_alert_code = 10 [default = -1]; - - // NetworkAgent wifi usability score of connected wifi. - // Defaults to -1 if the score was never set. - optional int32 last_wifi_usability_score = 11 [default = -1]; - - // Prediction horizon (in second) of Wifi usability score provided by external - // system app - optional int32 last_prediction_horizon_sec = 12 [default = -1]; - - // Whether screen status is on when WifiIsUnusableEvent happens. - optional bool screen_on = 13 [default = false]; -} - -message PasspointProfileTypeCount { - enum EapMethod { - // Unknown Type - TYPE_UNKNOWN = 0; - - // EAP_TLS (13) - TYPE_EAP_TLS = 1; - - // EAP_TTLS (21) - TYPE_EAP_TTLS = 2; - - // EAP_SIM (18) - TYPE_EAP_SIM = 3; - - // EAP_AKA (23) - TYPE_EAP_AKA = 4; - - // EAP_AKA_PRIME (50) - TYPE_EAP_AKA_PRIME = 5; - } - - // Eap method type set in Passpoint profile - optional EapMethod eap_method_type = 1; - - // Num of installed Passpoint profile with same eap method - optional int32 count = 2; -} - -message WifiLinkLayerUsageStats { - // Total logging duration in ms. - optional int64 logging_duration_ms = 1; - - // Total time the wifi radio is on in ms over the logging duration. - optional int64 radio_on_time_ms = 2; - - // Total time the wifi radio is doing tx in ms over the logging duration. - optional int64 radio_tx_time_ms = 3; - - // Total time the wifi radio is doing rx in ms over the logging duration. - optional int64 radio_rx_time_ms = 4; - - // Total time the wifi radio is scanning in ms over the logging duration. - optional int64 radio_scan_time_ms = 5; - - // Total time the wifi radio spent doing nan scans in ms over the logging duration. - optional int64 radio_nan_scan_time_ms = 6; - - // Total time the wifi radio spent doing background scans in ms over the logging duration. - optional int64 radio_background_scan_time_ms = 7; - - // Total time the wifi radio spent doing roam scans in ms over the logging duration. - optional int64 radio_roam_scan_time_ms = 8; - - // Total time the wifi radio spent doing pno scans in ms over the logging duration. - optional int64 radio_pno_scan_time_ms = 9; - - // Total time the wifi radio spent doing hotspot 2.0 scans and GAS exchange - // in ms over the logging duration. - optional int64 radio_hs20_scan_time_ms = 10; -} - -message WifiUsabilityStatsEntry { - // Status codes for link probe status - enum LinkProbeStatus { - // Link probe status is unknown - PROBE_STATUS_UNKNOWN = 0; - - // Link probe is not triggered - PROBE_STATUS_NO_PROBE = 1; - - // Link probe is triggered and the result is success - PROBE_STATUS_SUCCESS = 2; - - // Link probe is triggered and the result is failure - PROBE_STATUS_FAILURE = 3; - } - - // Codes for cellular data network type - enum CellularDataNetworkType { - // Unknown network - NETWORK_TYPE_UNKNOWN = 0; - - // GSM network - NETWORK_TYPE_GSM = 1; - - // CDMA network - NETWORK_TYPE_CDMA = 2; - - // CDMA EVDO network - NETWORK_TYPE_EVDO_0 = 3; - - // WCDMA network - NETWORK_TYPE_UMTS = 4; - - // TDSCDMA network - NETWORK_TYPE_TD_SCDMA = 5; - - // LTE network - NETWORK_TYPE_LTE = 6; - - // NR network - NETWORK_TYPE_NR = 7; - } - - // Absolute milliseconds from device boot when these stats were sampled - optional int64 time_stamp_ms = 1; - - // The RSSI at the sample time - optional int32 rssi = 2; - - // Link speed at the sample time in Mbps - optional int32 link_speed_mbps = 3; - - // The total number of tx success counted from the last radio chip reset - optional int64 total_tx_success = 4; - - // The total number of MPDU data packet retries counted from the last radio chip reset - optional int64 total_tx_retries = 5; - - // The total number of tx bad counted from the last radio chip reset - optional int64 total_tx_bad = 6; - - // The total number of rx success counted from the last radio chip reset - optional int64 total_rx_success = 7; - - // The total time the wifi radio is on in ms counted from the last radio chip reset - optional int64 total_radio_on_time_ms = 8; - - // The total time the wifi radio is doing tx in ms counted from the last radio chip reset - optional int64 total_radio_tx_time_ms = 9; - - // The total time the wifi radio is doing rx in ms counted from the last radio chip reset - optional int64 total_radio_rx_time_ms = 10; - - // The total time spent on all types of scans in ms counted from the last radio chip reset - optional int64 total_scan_time_ms = 11; - - // The total time spent on nan scans in ms counted from the last radio chip reset - optional int64 total_nan_scan_time_ms = 12; - - // The total time spent on background scans in ms counted from the last radio chip reset - optional int64 total_background_scan_time_ms = 13; - - // The total time spent on roam scans in ms counted from the last radio chip reset - optional int64 total_roam_scan_time_ms = 14; - - // The total time spent on pno scans in ms counted from the last radio chip reset - optional int64 total_pno_scan_time_ms = 15; - - // The total time spent on hotspot2.0 scans and GAS exchange in ms counted from the last radio - // chip reset - optional int64 total_hotspot_2_scan_time_ms = 16; - - // Internal framework Wifi score - optional int32 wifi_score = 17; - - // Wifi usability score provided by external system app - optional int32 wifi_usability_score = 18; - - // Sequence number from external system app to framework - optional int32 seq_num_to_framework = 19; - - // The total time CCA is on busy status on the current frequency in ms - // counted from the last radio chip reset - optional int64 total_cca_busy_freq_time_ms = 20; - - // The total radio on time of the current frequency from the last radio - // chip reset - optional int64 total_radio_on_freq_time_ms = 21; - - // The total number of beacons received from the last radio chip reset - optional int64 total_beacon_rx = 22; - - // Prediction horizon (in second) of Wifi usability score provided by external - // system app - optional int32 prediction_horizon_sec = 23; - - // The link probe status since last stats update - optional LinkProbeStatus probe_status_since_last_update = 24; - - // The elapsed time of the most recent link probe since last stats update; - optional int32 probe_elapsed_time_since_last_update_ms = 25; - - // The MCS rate of the most recent link probe since last stats update - optional int32 probe_mcs_rate_since_last_update = 26; - - // Rx link speed at the sample time in Mbps - optional int32 rx_link_speed_mbps = 27; - - // Sequence number generated by framework - optional int32 seq_num_inside_framework = 28; - - // Whether current entry is for the same BSSID on the same frequency compared - // to last entry - optional bool is_same_bssid_and_freq = 29; - - // Cellular data network type currently in use on the device for data transmission - optional CellularDataNetworkType cellular_data_network_type = 30; - - // Cellular signal strength in dBm, NR: CsiRsrp, LTE: Rsrp, WCDMA/TDSCDMA: Rscp, - // CDMA: Rssi, EVDO: Rssi, GSM: Rssi - optional int32 cellular_signal_strength_dbm = 31; - - // Cellular signal strength in dB, NR: CsiSinr, LTE: Rsrq, WCDMA: EcNo, TDSCDMA: invalid, - // CDMA: Ecio, EVDO: SNR, GSM: invalid */ - optional int32 cellular_signal_strength_db = 32; - - // Whether the primary registered cell of current entry is same as that of previous entry - optional bool is_same_registered_cell = 33; - - // The device mobility state - optional DeviceMobilityStatePnoScanStats.DeviceMobilityState - device_mobility_state = 34; -} - -message WifiUsabilityStats { - enum Label { - // Default label - LABEL_UNKNOWN = 0; - - // Wifi is usable - LABEL_GOOD = 1; - - // Wifi is unusable - LABEL_BAD = 2; - } - - enum UsabilityStatsTriggerType { - // Default/Invalid event - TYPE_UNKNOWN = 0; - - // There is a data stall from tx failures - TYPE_DATA_STALL_BAD_TX = 1; - - // There is a data stall from rx failures - TYPE_DATA_STALL_TX_WITHOUT_RX = 2; - - // There is a data stall from both tx and rx failures - TYPE_DATA_STALL_BOTH = 3; - - // Firmware generated an alert - TYPE_FIRMWARE_ALERT = 4; - - // IP Manager lost reachability to network neighbors - TYPE_IP_REACHABILITY_LOST = 5; - } - - // The current wifi usability state - optional Label label = 1; - - // The list of timestamped wifi usability stats - repeated WifiUsabilityStatsEntry stats = 2; - - // What event triggered WifiUsabilityStats. - optional UsabilityStatsTriggerType trigger_type = 3; - - // Firmware alert code. Only valid when the stats was triggered by a firmware - // alert, otherwise -1. - optional int32 firmware_alert_code = 4 [default = -1]; - - // Absolute milliseconds from device boot when these stats were sampled - optional int64 time_stamp_ms = 5; -} - -message DeviceMobilityStatePnoScanStats { - // see WifiManager.DEVICE_MOBILITY_STATE_* constants - enum DeviceMobilityState { - // Unknown mobility - UNKNOWN = 0; - - // High movement - HIGH_MVMT = 1; - - // Low movement - LOW_MVMT = 2; - - // Stationary - STATIONARY = 3; - } - - // The device mobility state - optional DeviceMobilityState device_mobility_state = 1; - - // The number of times that this state was entered - optional int32 num_times_entered_state = 2; - - // The total duration elapsed while in this mobility state, in ms - optional int64 total_duration_ms = 3; - - // the total duration elapsed while in this mobility state with PNO scans running, in ms - optional int64 pno_duration_ms = 4; -} - -// The information about the Wifi P2p events. -message WifiP2pStats { - - // Group event list tracking sessions and client counts in tethered mode. - repeated GroupEvent group_event = 1; - - // Session information that gets logged for every Wifi P2p connection. - repeated P2pConnectionEvent connection_event = 2; - - // Number of persistent group in the user profile. - optional int32 num_persistent_group = 3; - - // Number of peer scan. - optional int32 num_total_peer_scans = 4; - - // Number of service scan. - optional int32 num_total_service_scans = 5; -} - -message P2pConnectionEvent { - - enum ConnectionType { - - // fresh new connection. - CONNECTION_FRESH = 0; - - // reinvoke a group. - CONNECTION_REINVOKE = 1; - - // create a group with the current device as the group owner locally. - CONNECTION_LOCAL = 2; - - // create a group or join a group with config. - CONNECTION_FAST = 3; - } - - enum ConnectivityLevelFailure { - - // Failure is unknown. - CLF_UNKNOWN = 0; - - // No failure. - CLF_NONE = 1; - - // Timeout for current connecting request. - CLF_TIMEOUT = 2; - - // The connecting request is canceled by the user. - CLF_CANCEL = 3; - - // Provision discovery failure, e.g. no pin code, timeout, rejected by the peer. - CLF_PROV_DISC_FAIL = 4; - - // Invitation failure, e.g. rejected by the peer. - CLF_INVITATION_FAIL = 5; - - // Incoming request is rejected by the user. - CLF_USER_REJECT = 6; - - // New connection request is issued before ending previous connecting request. - CLF_NEW_CONNECTION_ATTEMPT = 7; - } - - // WPS method. - enum WpsMethod { - // WPS is skipped for Group Reinvoke. - WPS_NA = -1; - - // Push button configuration. - WPS_PBC = 0; - - // Display pin method configuration - pin is generated and displayed on device. - WPS_DISPLAY = 1; - - // Keypad pin method configuration - pin is entered on device. - WPS_KEYPAD = 2; - - // Label pin method configuration - pin is labelled on device. - WPS_LABEL = 3; - } - - // Start time of the connection. - optional int64 start_time_millis = 1; - - // Type of the connection. - optional ConnectionType connection_type = 2; - - // WPS method. - optional WpsMethod wps_method = 3 [default = WPS_NA]; - - // Duration to connect. - optional int32 duration_taken_to_connect_millis = 4; - - // Failures that happen at the connectivity layer. - optional ConnectivityLevelFailure connectivity_level_failure_code = 5; -} - -// GroupEvent tracking group information from GroupStarted to GroupRemoved. -message GroupEvent { - - enum GroupRole { - - GROUP_OWNER = 0; - - GROUP_CLIENT = 1; - } - - // The ID of network in supplicant for this group. - optional int32 net_id = 1; - - // Start time of the group. - optional int64 start_time_millis = 2; - - // Channel frequency used for Group. - optional int32 channel_frequency = 3; - - // Is group owner or group client. - optional GroupRole group_role = 5; - - // Number of connected clients. - optional int32 num_connected_clients = 6; - - // Cumulative number of connected clients. - optional int32 num_cumulative_clients = 7; - - // The session duration. - optional int32 session_duration_millis = 8; - - // The idle duration. A group without any client is in idle. - optional int32 idle_duration_millis = 9; -} - -// Easy Connect (DPP) -message WifiDppLog { - reserved 6; - - // Number of Configurator-Initiator requests - optional int32 num_dpp_configurator_initiator_requests = 1; - - // Number of Enrollee-Initiator requests - optional int32 num_dpp_enrollee_initiator_requests = 2; - - // Easy Connect (DPP) Enrollee success - optional int32 num_dpp_enrollee_success = 3; - - // Easy Connect (DPP) Configurator success code bucket - repeated DppConfiguratorSuccessStatusHistogramBucket dpp_configurator_success_code = 4; - - // Easy Connect (DPP) failure code bucket - repeated DppFailureStatusHistogramBucket dpp_failure_code = 5; - - // Easy Connect (DPP) operation time bucket - repeated HistogramBucketInt32 dpp_operation_time = 7; - - // Histogram bucket for Wi-Fi DPP configurator success - message DppConfiguratorSuccessStatusHistogramBucket { - // status type defining the bucket - optional DppConfiguratorSuccessCode dpp_status_type = 1; - - // number of samples in the bucket - optional int32 count = 2; - } - - // Histogram bucket for Wi-Fi DPP failures - message DppFailureStatusHistogramBucket { - // status type defining the bucket - optional DppFailureCode dpp_status_type = 1; - - // number of samples in the bucket - optional int32 count = 2; - } - - enum DppConfiguratorSuccessCode { - // Unknown success code - EASY_CONNECT_EVENT_SUCCESS_UNKNOWN = 0; - - // Easy Connect Configurator success event: Configuration sent to enrollee - EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT = 1; - } - - enum DppFailureCode { - // Unknown failure - EASY_CONNECT_EVENT_FAILURE_UNKNOWN = 0; - - // Easy Connect Failure event: Scanned QR code is either not a Easy Connect URI, or the Easy - // Connect URI has errors. - EASY_CONNECT_EVENT_FAILURE_INVALID_URI = 1; - - // Easy Connect Failure event: Bootstrapping/Authentication initialization process failure. - EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION = 2; - - // Easy Connect Failure event: Both devices are implementing the same role and are - // incompatible. - EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE = 3; - - // Easy Connect Failure event: Configuration process has failed due to malformed message. - EASY_CONNECT_EVENT_FAILURE_CONFIGURATION = 4; - - // Easy Connect Failure event: Easy Connect request while in another Easy Connect exchange. - EASY_CONNECT_EVENT_FAILURE_BUSY = 5; - - // Easy Connect Failure event: No response from the peer. - EASY_CONNECT_EVENT_FAILURE_TIMEOUT = 6; - - // Easy Connect Failure event: General protocol failure. - EASY_CONNECT_EVENT_FAILURE_GENERIC = 7; - - // Easy Connect Failure event: Feature or option is not supported. - EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED = 8; - - // Easy Connect Failure event: Invalid network provided to Easy Connect configurator. - // Network must either be WPA3-Personal (SAE) or WPA2-Personal (PSK). - EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = 9; - } -} - -// WifiConfigStore read/write metrics. -message WifiConfigStoreIO { - // Histogram of config store read durations. - repeated DurationBucket read_durations = 1; - - // Histogram of config store write durations. - repeated DurationBucket write_durations = 2; - - // Total Number of instances of write/read duration in this duration bucket. - message DurationBucket { - // Bucket covers duration : [range_start_ms, range_end_ms) - // The (inclusive) lower bound of read/write duration represented by this bucket - optional int32 range_start_ms = 1; - - // The (exclusive) upper bound of read/write duration represented by this bucket - optional int32 range_end_ms = 2; - - // Number of read/write durations that fit into this bucket - optional int32 count = 3; - } -} - -// Histogram bucket counting with int32. Range is [start, end) -message HistogramBucketInt32 { - // lower range of the bucket (inclusive) - optional int32 start = 1; - - // upper range of the bucket (exclusive) - optional int32 end = 2; - - // number of samples in the bucket - optional int32 count = 3; -} - -// Counts occurrences of a int32 key -message Int32Count { - // the key - optional int32 key = 1; - - // the count - optional int32 count = 2; -} - -message LinkProbeStats { - enum LinkProbeFailureReason { - // unknown reason - LINK_PROBE_FAILURE_REASON_UNKNOWN = 0; - - // Specified MCS rate when it is unsupported by the driver. - LINK_PROBE_FAILURE_REASON_MCS_UNSUPPORTED = 1; - - // Driver reported that no ACK was received for the transmitted probe. - LINK_PROBE_FAILURE_REASON_NO_ACK = 2; - - // Driver failed to report on the status of the transmitted probe within the timeout. - LINK_PROBE_FAILURE_REASON_TIMEOUT = 3; - - // An existing link probe is in progress. - LINK_PROBE_FAILURE_REASON_ALREADY_STARTED = 4; - } - - // Counts the number of failures for each failure reason. - message LinkProbeFailureReasonCount { - // The failure reason. - optional LinkProbeFailureReason failure_reason = 1; - - // The number of occurrences for this failure reason. - optional int32 count = 2; - } - - // Counts the number of link probes for each experiment. - message ExperimentProbeCounts { - // The experiment ID. - optional string experiment_id = 1; - - // The number of link probes triggered for this experiment. - optional int32 probe_count = 2; - } - - // Counts the occurrences of RSSI values when a link probe succeeds. - repeated Int32Count success_rssi_counts = 1; - - // Counts the occurrences of RSSI values when a link probe fails. - repeated Int32Count failure_rssi_counts = 2; - - // Counts the occurrences of Link Speed values when a link probe succeeds. - repeated Int32Count success_link_speed_counts = 3; - - // Counts the occurrences of Link Speed values when a link probe fails. - repeated Int32Count failure_link_speed_counts = 4; - - // Histogram for the number of seconds since the last TX success when a link probe succeeds. - repeated HistogramBucketInt32 success_seconds_since_last_tx_success_histogram = 5; - - // Histogram for the number of seconds since the last TX success when a link probe fails. - repeated HistogramBucketInt32 failure_seconds_since_last_tx_success_histogram = 6; - - // Histogram for the elapsed time of successful link probes, in ms. - repeated HistogramBucketInt32 success_elapsed_time_ms_histogram = 7; - - // Counts the occurrences of error codes for failed link probes. - repeated LinkProbeFailureReasonCount failure_reason_counts = 8; - - // Counts the number of link probes for each experiment. - repeated ExperimentProbeCounts experiment_probe_counts = 9; -} - -// Stores the decisions that were made by a experiment when compared against another experiment -message NetworkSelectionExperimentDecisions { - // the id of one experiment - optional int32 experiment1_id = 1; - - // the id of the other experiment - optional int32 experiment2_id = 2; - - // Counts occurrences of the number of network choices there were when experiment1 makes the - // same network selection as experiment2. - // The keys are the number of network choices, and the values are the number of occurrences of - // this number of network choices when exp1 and exp2 make the same network selection. - repeated Int32Count same_selection_num_choices_counter = 3; - - // Counts occurrences of the number of network choices there were when experiment1 makes the - // same network selection as experiment2. - // The keys are the number of network choices, and the values are the number of occurrences of - // this number of network choices when exp1 and exp2 make different network selections. - // Note that it is possible for the network selection to be different even when there only exists - // a single network choice, since choosing not to connect to that network is a valid choice. - repeated Int32Count different_selection_num_choices_counter = 4; -} - -// NetworkRequest API metrics. -message WifiNetworkRequestApiLog { - // Number of requests via this API surface. - optional int32 num_request = 1; - - // Histogram of requests via this API surface to number of networks matched in scan results. - repeated HistogramBucketInt32 network_match_size_histogram = 2; - - // Number of successful network connection from this API. - optional int32 num_connect_success = 3; - - // Number of requests via this API surface that bypassed user approval. - optional int32 num_user_approval_bypass = 4; - - // Number of requests via this API surface that was rejected by the user. - optional int32 num_user_reject = 5; - - // Number of unique apps using this API surface. - optional int32 num_apps = 6; -} - -// NetworkSuggestion API metrics. -message WifiNetworkSuggestionApiLog { - // Number of modifications to their suggestions by apps. - optional int32 num_modification = 1; - - // Number of successful network connection from app suggestions. - optional int32 num_connect_success = 2; - - // Number of network connection failures from app suggestions. - optional int32 num_connect_failure = 3; - - // Histogram for size of the network lists provided by various apps on the device. - repeated HistogramBucketInt32 network_list_size_histogram = 4; -} - -// WifiLock metrics -message WifiLockStats { - // Amount of time wifi is actively in HIGH_PERF mode (ms) - // This means the lock takes effect and the device takes the actions required for this mode - optional int64 high_perf_active_time_ms = 1; - - // Amount of time wifi is actively in LOW_LATENCY mode (ms) - // This means the lock takes effect and the device takes the actions required for this mode - optional int64 low_latency_active_time_ms = 2; - - // Histogram of HIGH_PERF lock acquisition duration (seconds) - // Note that acquiring the lock does not necessarily mean that device is actively in that mode - repeated HistogramBucketInt32 high_perf_lock_acq_duration_sec_histogram = 3; - - // Histogram of LOW_LATENCY lock acquisition duration (seconds) - // Note that acquiring the lock does not necessarily mean that device is actively in that mode - repeated HistogramBucketInt32 low_latency_lock_acq_duration_sec_histogram = 4; - - // Histogram of HIGH_PERF active session duration (seconds) - // This means the lock takes effect and the device takes the actions required for this mode - repeated HistogramBucketInt32 high_perf_active_session_duration_sec_histogram = 5; - - // Histogram of LOW_LATENCY active session duration (seconds) - // This means the lock takes effect and the device takes the actions required for this mode - repeated HistogramBucketInt32 low_latency_active_session_duration_sec_histogram = 6; -} - -// Stats on number of times Wi-Fi is turned on/off though the WifiManager#setWifiEnabled API -message WifiToggleStats { - // Number of time Wi-Fi is turned on by privileged apps - optional int32 num_toggle_on_privileged = 1; - - // Number of time Wi-Fi is turned off by privileged apps - optional int32 num_toggle_off_privileged = 2; - - // Number of time Wi-Fi is turned on by normal apps - optional int32 num_toggle_on_normal = 3; - - // Number of time Wi-Fi is turned off by normal apps - optional int32 num_toggle_off_normal = 4; -} - -// Information about the Passpoint provision metrics. -message PasspointProvisionStats { - enum ProvisionFailureCode { - // provisioning failure for unknown reason. - OSU_FAILURE_UNKNOWN = 0; - - // The reason code for Provisioning Failure due to connection failure to OSU AP. - OSU_FAILURE_AP_CONNECTION = 1; - - // The reason code for invalid server URL address. - OSU_FAILURE_SERVER_URL_INVALID = 2; - - // The reason code for provisioning failure due to connection failure to the server. - OSU_FAILURE_SERVER_CONNECTION = 3; - - // The reason code for provisioning failure due to invalid server certificate. - OSU_FAILURE_SERVER_VALIDATION = 4; - - // The reason code for provisioning failure due to invalid service provider. - OSU_FAILURE_SERVICE_PROVIDER_VERIFICATION = 5; - - // The reason code for provisioning failure when a provisioning flow is aborted. - OSU_FAILURE_PROVISIONING_ABORTED = 6; - - // The reason code for provisioning failure when a provisioning flow is not possible. - OSU_FAILURE_PROVISIONING_NOT_AVAILABLE = 7; - - // The reason code for provisioning failure due to invalid web url format for an OSU web page. - OSU_FAILURE_INVALID_URL_FORMAT_FOR_OSU = 8; - - // The reason code for provisioning failure when a command received is not the expected command - // type. - OSU_FAILURE_UNEXPECTED_COMMAND_TYPE = 9; - - // The reason code for provisioning failure when a SOAP message is not the expected message - // type. - OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_TYPE = 10; - - // The reason code for provisioning failure when a SOAP message exchange fails. - OSU_FAILURE_SOAP_MESSAGE_EXCHANGE = 11; - - // The reason code for provisioning failure when a redirect listener fails to start. - OSU_FAILURE_START_REDIRECT_LISTENER = 12; - - // The reason code for provisioning failure when a redirect listener timed out to receive a HTTP - // redirect response. - OSU_FAILURE_TIMED_OUT_REDIRECT_LISTENER = 13; - - // The reason code for provisioning failure when there is no OSU activity to listen to intent. - OSU_FAILURE_NO_OSU_ACTIVITY_FOUND = 14; - - // The reason code for provisioning failure when the status of a SOAP message is not the - // expected message status. - OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_STATUS = 15; - - // The reason code for provisioning failure when there is no PPS MO. - OSU_FAILURE_NO_PPS_MO = 16; - - // The reason code for provisioning failure when there is no AAAServerTrustRoot node in a PPS - // MO. - OSU_FAILURE_NO_AAA_SERVER_TRUST_ROOT_NODE = 17; - - // The reason code for provisioning failure when there is no TrustRoot node for remediation - // server in a PPS MO. - OSU_FAILURE_NO_REMEDIATION_SERVER_TRUST_ROOT_NODE = 18; - - // The reason code for provisioning failure when there is no TrustRoot node for policy server in - // a PPS MO. - OSU_FAILURE_NO_POLICY_SERVER_TRUST_ROOT_NODE = 19; - - // The reason code for provisioning failure when failing to retrieve trust root certificates - // used for validating server certificate for AAA, Remediation and Policy server. - OSU_FAILURE_RETRIEVE_TRUST_ROOT_CERTIFICATES = 20; - - // The reason code for provisioning failure when there is no trust root certificate for AAA - // server. - OSU_FAILURE_NO_AAA_TRUST_ROOT_CERTIFICATE = 21; - - // The reason code for provisioning failure when a {@link PasspointConfiguration} is failed to - // install. - OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION = 22; - - // The reason code for provisioning failure when an {@link OsuProvider} is not found for - // provisioning. - OSU_FAILURE_OSU_PROVIDER_NOT_FOUND = 23; - } - - // Number of passpoint provisioning success - optional int32 num_provision_success = 1; - - // Count for passpoint provisioning failure - repeated ProvisionFailureCount provision_failure_count = 2; - - // Number of occurrences of a specific passpoint provision failure code - message ProvisionFailureCount { - // Failure code - optional ProvisionFailureCode failure_code = 1; - - // Number of failure for the failure_code. - optional int32 count = 2; - } -} diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 68e11df32d79..6f435294268f 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -67,8 +67,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.provider.Settings; -import android.provider.SettingsStringUtil; -import android.provider.SettingsStringUtil.ComponentNameSet; import android.provider.SettingsStringUtil.SettingStringHelper; import android.text.TextUtils; import android.text.TextUtils.SimpleStringSplitter; @@ -2219,12 +2217,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * Enables accessibility service specified by {@param componentName} for the {@param userId}. */ private void enableAccessibilityServiceLocked(ComponentName componentName, int userId) { - final SettingStringHelper setting = - new SettingStringHelper( - mContext.getContentResolver(), - Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, - userId); - setting.write(ComponentNameSet.add(setting.read(), componentName)); + mTempComponentNameSet.clear(); + readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, + userId, mTempComponentNameSet); + mTempComponentNameSet.add(componentName); + persistComponentNamesToSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, + mTempComponentNameSet, userId); AccessibilityUserState userState = getUserStateLocked(userId); if (userState.mEnabledServices.add(componentName)) { @@ -2236,12 +2234,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * Disables accessibility service specified by {@param componentName} for the {@param userId}. */ private void disableAccessibilityServiceLocked(ComponentName componentName, int userId) { - final SettingsStringUtil.SettingStringHelper setting = - new SettingStringHelper( - mContext.getContentResolver(), - Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, - userId); - setting.write(ComponentNameSet.remove(setting.read(), componentName)); + mTempComponentNameSet.clear(); + readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, + userId, mTempComponentNameSet); + mTempComponentNameSet.remove(componentName); + persistComponentNamesToSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, + mTempComponentNameSet, userId); AccessibilityUserState userState = getUserStateLocked(userId); if (userState.mEnabledServices.remove(componentName)) { diff --git a/services/core/Android.bp b/services/core/Android.bp index 3067bebcf19e..c86538438835 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -80,6 +80,7 @@ java_library_static { ":vold_aidl", ":gsiservice_aidl", ":platform-compat-config", + ":tethering-servicescore-srcs", "java/com/android/server/EventLogTags.logtags", "java/com/android/server/am/EventLogTags.logtags", "java/com/android/server/policy/EventLogTags.logtags", @@ -155,3 +156,11 @@ prebuilt_etc { name: "protolog.conf.json.gz", src: ":services.core.json.gz", } + +// TODO: this should be removed after tethering migration done. +filegroup { + name: "servicescore-tethering-src", + srcs: [ + "java/com/android/server/connectivity/MockableSystemProperties.java", + ], +} diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index ff0044f6f1ad..1ce11378efda 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -154,7 +154,7 @@ class AlarmManagerService extends SystemService { static final int TICK_HISTORY_DEPTH = 10; static final long MILLIS_IN_DAY = 24 * 60 * 60 * 1000; - // Indices into the APP_STANDBY_MIN_DELAYS and KEYS_APP_STANDBY_DELAY arrays + // Indices into the KEYS_APP_STANDBY_QUOTAS array. static final int ACTIVE_INDEX = 0; static final int WORKING_INDEX = 1; static final int FREQUENT_INDEX = 2; @@ -401,8 +401,6 @@ class AlarmManagerService extends SystemService { static final String KEY_LISTENER_TIMEOUT = "listener_timeout"; @VisibleForTesting static final String KEY_MAX_ALARMS_PER_UID = "max_alarms_per_uid"; - @VisibleForTesting - static final String KEY_APP_STANDBY_QUOTAS_ENABLED = "app_standby_quotas_enabled"; private static final String KEY_APP_STANDBY_WINDOW = "app_standby_window"; @VisibleForTesting final String[] KEYS_APP_STANDBY_QUOTAS = { @@ -413,15 +411,6 @@ class AlarmManagerService extends SystemService { "standby_never_quota", }; - // Keys for specifying throttling delay based on app standby bucketing - private final String[] KEYS_APP_STANDBY_DELAY = { - "standby_active_delay", - "standby_working_delay", - "standby_frequent_delay", - "standby_rare_delay", - "standby_never_delay", - }; - private static final long DEFAULT_MIN_FUTURITY = 5 * 1000; private static final long DEFAULT_MIN_INTERVAL = 60 * 1000; private static final long DEFAULT_MAX_INTERVAL = 365 * DateUtils.DAY_IN_MILLIS; @@ -430,7 +419,6 @@ class AlarmManagerService extends SystemService { private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10*1000; private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000; private static final int DEFAULT_MAX_ALARMS_PER_UID = 500; - private static final boolean DEFAULT_APP_STANDBY_QUOTAS_ENABLED = true; private static final long DEFAULT_APP_STANDBY_WINDOW = 60 * 60 * 1000; // 1 hr /** * Max number of times an app can receive alarms in {@link #APP_STANDBY_WINDOW} @@ -442,13 +430,6 @@ class AlarmManagerService extends SystemService { 1, // Rare 0 // Never }; - private final long[] DEFAULT_APP_STANDBY_DELAYS = { - 0, // Active - 6 * 60_000, // Working - 30 * 60_000, // Frequent - 2 * 60 * 60_000, // Rare - 10 * 24 * 60 * 60_000 // Never - }; // Minimum futurity of a new alarm public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY; @@ -473,10 +454,7 @@ class AlarmManagerService extends SystemService { public long LISTENER_TIMEOUT = DEFAULT_LISTENER_TIMEOUT; public int MAX_ALARMS_PER_UID = DEFAULT_MAX_ALARMS_PER_UID; - public boolean APP_STANDBY_QUOTAS_ENABLED = DEFAULT_APP_STANDBY_QUOTAS_ENABLED; - public long APP_STANDBY_WINDOW = DEFAULT_APP_STANDBY_WINDOW; - public long[] APP_STANDBY_MIN_DELAYS = new long[DEFAULT_APP_STANDBY_DELAYS.length]; public int[] APP_STANDBY_QUOTAS = new int[DEFAULT_APP_STANDBY_QUOTAS.length]; private ContentResolver mResolver; @@ -532,16 +510,6 @@ class AlarmManagerService extends SystemService { DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION); LISTENER_TIMEOUT = mParser.getLong(KEY_LISTENER_TIMEOUT, DEFAULT_LISTENER_TIMEOUT); - APP_STANDBY_MIN_DELAYS[ACTIVE_INDEX] = mParser.getDurationMillis( - KEYS_APP_STANDBY_DELAY[ACTIVE_INDEX], - DEFAULT_APP_STANDBY_DELAYS[ACTIVE_INDEX]); - for (int i = WORKING_INDEX; i < KEYS_APP_STANDBY_DELAY.length; i++) { - APP_STANDBY_MIN_DELAYS[i] = mParser.getDurationMillis(KEYS_APP_STANDBY_DELAY[i], - Math.max(APP_STANDBY_MIN_DELAYS[i - 1], DEFAULT_APP_STANDBY_DELAYS[i])); - } - - APP_STANDBY_QUOTAS_ENABLED = mParser.getBoolean(KEY_APP_STANDBY_QUOTAS_ENABLED, - DEFAULT_APP_STANDBY_QUOTAS_ENABLED); APP_STANDBY_WINDOW = mParser.getLong(KEY_APP_STANDBY_WINDOW, DEFAULT_APP_STANDBY_WINDOW); @@ -614,15 +582,6 @@ class AlarmManagerService extends SystemService { pw.print(KEY_MAX_ALARMS_PER_UID); pw.print("="); pw.println(MAX_ALARMS_PER_UID); - for (int i = 0; i < KEYS_APP_STANDBY_DELAY.length; i++) { - pw.print(KEYS_APP_STANDBY_DELAY[i]); pw.print("="); - TimeUtils.formatDuration(APP_STANDBY_MIN_DELAYS[i], pw); - pw.println(); - } - - pw.print(KEY_APP_STANDBY_QUOTAS_ENABLED); pw.print("="); - pw.println(APP_STANDBY_QUOTAS_ENABLED); - pw.print(KEY_APP_STANDBY_WINDOW); pw.print("="); TimeUtils.formatDuration(APP_STANDBY_WINDOW, pw); pw.println(); @@ -1826,27 +1785,6 @@ class AlarmManagerService extends SystemService { } /** - * Return the minimum time that should elapse before an app in the specified bucket - * can receive alarms again - */ - @VisibleForTesting - long getMinDelayForBucketLocked(int bucket) { - // UsageStats bucket values are treated as floors of their behavioral range. - // In other words, a bucket value between WORKING and ACTIVE is treated as - // WORKING, not as ACTIVE. The ACTIVE and NEVER bucket apply only at specific - // values. - final int index; - - if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) index = NEVER_INDEX; - else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) index = RARE_INDEX; - else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) index = FREQUENT_INDEX; - else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) index = WORKING_INDEX; - else index = ACTIVE_INDEX; - - return mConstants.APP_STANDBY_MIN_DELAYS[index]; - } - - /** * Adjusts the alarm delivery time based on the current app standby bucket. * @param alarm The alarm to adjust * @return true if the alarm delivery time was updated. @@ -1872,50 +1810,34 @@ class AlarmManagerService extends SystemService { final int standbyBucket = mUsageStatsManagerInternal.getAppStandbyBucket( sourcePackage, sourceUserId, mInjector.getElapsedRealtime()); - if (mConstants.APP_STANDBY_QUOTAS_ENABLED) { - // Quota deferring implementation: - final int wakeupsInWindow = mAppWakeupHistory.getTotalWakeupsInWindow(sourcePackage, - sourceUserId); - final int quotaForBucket = getQuotaForBucketLocked(standbyBucket); - boolean deferred = false; - if (wakeupsInWindow >= quotaForBucket) { - final long minElapsed; - if (quotaForBucket <= 0) { - // Just keep deferring for a day till the quota changes - minElapsed = mInjector.getElapsedRealtime() + MILLIS_IN_DAY; - } else { - // Suppose the quota for window was q, and the qth last delivery time for this - // package was t(q) then the next delivery must be after t(q) + <window_size> - final long t = mAppWakeupHistory.getLastWakeupForPackage(sourcePackage, - sourceUserId, quotaForBucket); - minElapsed = t + 1 + mConstants.APP_STANDBY_WINDOW; - } - if (alarm.expectedWhenElapsed < minElapsed) { - alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed; - deferred = true; - } - } - if (!deferred) { - // Restore original requirements in case they were changed earlier. - alarm.whenElapsed = alarm.expectedWhenElapsed; - alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed; + // Quota deferring implementation: + final int wakeupsInWindow = mAppWakeupHistory.getTotalWakeupsInWindow(sourcePackage, + sourceUserId); + final int quotaForBucket = getQuotaForBucketLocked(standbyBucket); + boolean deferred = false; + if (wakeupsInWindow >= quotaForBucket) { + final long minElapsed; + if (quotaForBucket <= 0) { + // Just keep deferring for a day till the quota changes + minElapsed = mInjector.getElapsedRealtime() + MILLIS_IN_DAY; + } else { + // Suppose the quota for window was q, and the qth last delivery time for this + // package was t(q) then the next delivery must be after t(q) + <window_size> + final long t = mAppWakeupHistory.getLastWakeupForPackage(sourcePackage, + sourceUserId, quotaForBucket); + minElapsed = t + 1 + mConstants.APP_STANDBY_WINDOW; } - } else { - // Minimum delay deferring implementation: - final long lastElapsed = mAppWakeupHistory.getLastWakeupForPackage(sourcePackage, - sourceUserId, 1); - if (lastElapsed > 0) { - final long minElapsed = lastElapsed + getMinDelayForBucketLocked(standbyBucket); - if (alarm.expectedWhenElapsed < minElapsed) { - alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed; - } else { - // app is now eligible to run alarms at the originally requested window. - // Restore original requirements in case they were changed earlier. - alarm.whenElapsed = alarm.expectedWhenElapsed; - alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed; - } + if (alarm.expectedWhenElapsed < minElapsed) { + alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed; + deferred = true; } } + if (!deferred) { + // Restore original requirements in case they were changed earlier. + alarm.whenElapsed = alarm.expectedWhenElapsed; + alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed; + } + return (oldWhenElapsed != alarm.whenElapsed || oldMaxWhenElapsed != alarm.maxWhenElapsed); } diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java index aeb3e7fd94de..028d41234dd0 100644 --- a/services/core/java/com/android/server/DropBoxManagerService.java +++ b/services/core/java/com/android/server/DropBoxManagerService.java @@ -23,6 +23,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; import android.net.Uri; @@ -40,11 +41,13 @@ import android.os.StatFs; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; +import android.service.dropbox.DropBoxManagerServiceDumpProto; import android.text.TextUtils; import android.text.format.TimeMigrationUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; +import android.util.proto.ProtoOutputStream; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -65,6 +68,7 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.SortedSet; import java.util.TreeSet; import java.util.zip.GZIPOutputStream; @@ -85,6 +89,9 @@ public final class DropBoxManagerService extends SystemService { private static final boolean PROFILE_DUMP = false; + // Max number of bytes of a dropbox entry to write into protobuf. + private static final int PROTO_MAX_DATA_BYTES = 256 * 1024; + // TODO: This implementation currently uses one file per entry, which is // inefficient for smallish entries -- consider using a single queue file // per tag (or even globally) instead. @@ -464,6 +471,14 @@ public final class DropBoxManagerService extends SystemService { } private boolean checkPermission(int callingUid, String callingPackage) { + // If callers have this permission, then we don't need to check + // USAGE_STATS, because they are part of the system and have agreed to + // check USAGE_STATS before passing the data along. + if (getContext().checkCallingPermission(android.Manifest.permission.PEEK_DROPBOX_DATA) + == PackageManager.PERMISSION_GRANTED) { + return true; + } + // Callers always need this permission getContext().enforceCallingOrSelfPermission( android.Manifest.permission.READ_LOGS, TAG); @@ -547,18 +562,22 @@ public final class DropBoxManagerService extends SystemService { StringBuilder out = new StringBuilder(); boolean doPrint = false, doFile = false; + boolean dumpProto = false; ArrayList<String> searchArgs = new ArrayList<String>(); for (int i = 0; args != null && i < args.length; i++) { if (args[i].equals("-p") || args[i].equals("--print")) { doPrint = true; } else if (args[i].equals("-f") || args[i].equals("--file")) { doFile = true; + } else if (args[i].equals("--proto")) { + dumpProto = true; } else if (args[i].equals("-h") || args[i].equals("--help")) { pw.println("Dropbox (dropbox) dump options:"); pw.println(" [-h|--help] [-p|--print] [-f|--file] [timestamp]"); pw.println(" -h|--help: print this help"); pw.println(" -p|--print: print full contents of each entry"); pw.println(" -f|--file: print path of each entry's file"); + pw.println(" --proto: dump data to proto"); pw.println(" [timestamp] optionally filters to only those entries."); return; } else if (args[i].startsWith("-")) { @@ -568,6 +587,11 @@ public final class DropBoxManagerService extends SystemService { } } + if (dumpProto) { + dumpProtoLocked(fd, searchArgs); + return; + } + out.append("Drop box contents: ").append(mAllFiles.contents.size()).append(" entries\n"); out.append("Max entries: ").append(mMaxFiles).append("\n"); @@ -581,19 +605,15 @@ public final class DropBoxManagerService extends SystemService { out.append("\n"); } - int numFound = 0, numArgs = searchArgs.size(); + int numFound = 0; out.append("\n"); for (EntryFile entry : mAllFiles.contents) { - String date = TimeMigrationUtils.formatMillisWithFixedFormat(entry.timestampMillis); - boolean match = true; - for (int i = 0; i < numArgs && match; i++) { - String arg = searchArgs.get(i); - match = (date.contains(arg) || arg.equals(entry.tag)); - } - if (!match) continue; + if (!matchEntry(entry, searchArgs)) continue; numFound++; if (doPrint) out.append("========================================\n"); + + String date = TimeMigrationUtils.formatMillisWithFixedFormat(entry.timestampMillis); out.append(date).append(" ").append(entry.tag == null ? "(no tag)" : entry.tag); final File file = entry.getFile(mDropBoxDir); @@ -679,6 +699,55 @@ public final class DropBoxManagerService extends SystemService { if (PROFILE_DUMP) Debug.stopMethodTracing(); } + private boolean matchEntry(EntryFile entry, ArrayList<String> searchArgs) { + String date = TimeMigrationUtils.formatMillisWithFixedFormat(entry.timestampMillis); + boolean match = true; + int numArgs = searchArgs.size(); + for (int i = 0; i < numArgs && match; i++) { + String arg = searchArgs.get(i); + match = (date.contains(arg) || arg.equals(entry.tag)); + } + return match; + } + + private void dumpProtoLocked(FileDescriptor fd, ArrayList<String> searchArgs) { + final ProtoOutputStream proto = new ProtoOutputStream(fd); + + for (EntryFile entry : mAllFiles.contents) { + if (!matchEntry(entry, searchArgs)) continue; + + final File file = entry.getFile(mDropBoxDir); + if ((file == null) || ((entry.flags & DropBoxManager.IS_EMPTY) != 0)) { + continue; + } + + final long bToken = proto.start(DropBoxManagerServiceDumpProto.ENTRIES); + proto.write(DropBoxManagerServiceDumpProto.Entry.TIME_MS, entry.timestampMillis); + try ( + DropBoxManager.Entry dbe = new DropBoxManager.Entry( + entry.tag, entry.timestampMillis, file, entry.flags); + InputStream is = dbe.getInputStream(); + ) { + if (is != null) { + byte[] buf = new byte[PROTO_MAX_DATA_BYTES]; + int readBytes = 0; + int n = 0; + while (n >= 0 && (readBytes += n) < PROTO_MAX_DATA_BYTES) { + n = is.read(buf, readBytes, PROTO_MAX_DATA_BYTES - readBytes); + } + proto.write(DropBoxManagerServiceDumpProto.Entry.DATA, + Arrays.copyOf(buf, readBytes)); + } + } catch (IOException e) { + Slog.e(TAG, "Can't read: " + file, e); + } + + proto.end(bToken); + } + + proto.flush(); + } + /////////////////////////////////////////////////////////////////////////// /** Chronologically sorted list of {@link EntryFile} */ diff --git a/services/core/java/com/android/server/GnssManagerService.java b/services/core/java/com/android/server/GnssManagerService.java index 274e2f1b53f8..cbf2a622fd2e 100644 --- a/services/core/java/com/android/server/GnssManagerService.java +++ b/services/core/java/com/android/server/GnssManagerService.java @@ -67,7 +67,7 @@ import java.util.function.Function; /** Manages Gnss providers and related Gnss functions for LocationManagerService. */ public class GnssManagerService { - private static final String TAG = "LocationManagerService"; + private static final String TAG = "GnssManagerService"; private static final boolean D = Log.isLoggable(TAG, Log.DEBUG); // Providers @@ -124,7 +124,7 @@ public class GnssManagerService { // Can use this constructor to inject GnssLocationProvider for testing @VisibleForTesting - GnssManagerService(LocationManagerService locationManagerService, + public GnssManagerService(LocationManagerService locationManagerService, Context context, GnssLocationProvider gnssLocationProvider, LocationUsageLogger locationUsageLogger) { @@ -719,8 +719,10 @@ public class GnssManagerService { * @param listener called when navigation message is received */ public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) { - removeGnssDataListener( - listener, mGnssNavigationMessageProvider, mGnssNavigationMessageListeners); + synchronized (mGnssNavigationMessageListeners) { + removeGnssDataListener( + listener, mGnssNavigationMessageProvider, mGnssNavigationMessageListeners); + } } /** diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 0a63bf89b21f..e9cca66438f8 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -128,6 +128,32 @@ import java.util.concurrent.TimeUnit; * updates and alerts. */ public class LocationManagerService extends ILocationManager.Stub { + + /** + * Controls lifecycle of LocationManagerService. + */ + public static class Lifecycle extends SystemService { + + private LocationManagerService mService; + + public Lifecycle(Context context) { + super(context); + mService = new LocationManagerService(context); + } + + @Override + public void onStart() { + publishBinderService(Context.LOCATION_SERVICE, mService); + } + + @Override + public void onBootPhase(int phase) { + if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { + mService.systemRunning(); + } + } + } + private static final String TAG = "LocationManagerService"; public static final boolean D = Log.isLoggable(TAG, Log.DEBUG); @@ -234,8 +260,7 @@ public class LocationManagerService extends ILocationManager.Stub { @GuardedBy("mLock") private final LocationUsageLogger mLocationUsageLogger; - public LocationManagerService(Context context) { - super(); + private LocationManagerService(Context context) { mContext = context; mHandler = FgThread.getHandler(); mLocationUsageLogger = new LocationUsageLogger(); @@ -254,7 +279,7 @@ public class LocationManagerService extends ILocationManager.Stub { // most startup is deferred until systemRunning() } - public void systemRunning() { + private void systemRunning() { synchronized (mLock) { initializeLocked(); } @@ -2051,6 +2076,18 @@ public class LocationManagerService extends ILocationManager.Stub { * Note: must be constructed with lock held. */ private UpdateRecord(String provider, LocationRequest request, Receiver receiver) { + // translate expireIn value into expireAt + long elapsedRealtime = SystemClock.elapsedRealtime(); + long expireAt; + // Check for > Long.MAX_VALUE overflow (elapsedRealtime > 0): + if (request.getExpireIn() > Long.MAX_VALUE - elapsedRealtime) { + expireAt = Long.MAX_VALUE; + } else { + expireAt = Math.max(request.getExpireIn() + elapsedRealtime, 0); + } + request.setExpireAt(Math.min(request.getExpireAt(), expireAt)); + request.setExpireIn(Long.MAX_VALUE); + mProvider = provider; mRealRequest = request; mRequest = request; diff --git a/services/core/java/com/android/server/LocationUsageLogger.java b/services/core/java/com/android/server/LocationUsageLogger.java index 4ca74bf3aa91..a8a3cc47cfb0 100644 --- a/services/core/java/com/android/server/LocationUsageLogger.java +++ b/services/core/java/com/android/server/LocationUsageLogger.java @@ -30,7 +30,7 @@ import java.time.Instant; /** * Logger for Location API usage logging. */ -class LocationUsageLogger { +public class LocationUsageLogger { private static final String TAG = "LocationUsageLogger"; private static final boolean D = Log.isLoggable(TAG, Log.DEBUG); diff --git a/services/core/java/com/android/server/NetworkScorerAppManager.java b/services/core/java/com/android/server/NetworkScorerAppManager.java index bfd4247abf40..3bcb36fd0e00 100644 --- a/services/core/java/com/android/server/NetworkScorerAppManager.java +++ b/services/core/java/com/android/server/NetworkScorerAppManager.java @@ -18,10 +18,10 @@ package com.android.server; import android.Manifest.permission; import android.annotation.Nullable; -import android.app.AppOpsManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.PermissionChecker; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; @@ -210,14 +210,9 @@ public class NetworkScorerAppManager { } private boolean canAccessLocation(int uid, String packageName) { - final PackageManager pm = mContext.getPackageManager(); - final AppOpsManager appOpsManager = - (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); - return isLocationModeEnabled() - && pm.checkPermission(permission.ACCESS_COARSE_LOCATION, packageName) - == PackageManager.PERMISSION_GRANTED - && appOpsManager.noteOp(AppOpsManager.OP_COARSE_LOCATION, uid, packageName) - == AppOpsManager.MODE_ALLOWED; + return isLocationModeEnabled() && PermissionChecker.checkPermissionForPreflight(mContext, + permission.ACCESS_COARSE_LOCATION, PermissionChecker.PID_UNKNOWN, uid, packageName) + == PermissionChecker.PERMISSION_GRANTED; } private boolean isLocationModeEnabled() { diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java index e3dc3b7a9848..7f51aa9068fc 100644 --- a/services/core/java/com/android/server/ServiceWatcher.java +++ b/services/core/java/com/android/server/ServiceWatcher.java @@ -51,6 +51,8 @@ import java.util.Objects; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * Find the best Service, and bind to it. @@ -64,6 +66,7 @@ public class ServiceWatcher implements ServiceConnection { public static final String EXTRA_SERVICE_VERSION = "serviceVersion"; public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser"; + private static final long BLOCKING_BINDER_TIMEOUT_MS = 30 * 1000; /** Function to run on binder interface. */ public interface BinderRunner { @@ -402,7 +405,7 @@ public class ServiceWatcher implements ServiceConnection { return defaultValue; } }); - } catch (InterruptedException e) { + } catch (InterruptedException | TimeoutException e) { return defaultValue; } } @@ -439,7 +442,8 @@ public class ServiceWatcher implements ServiceConnection { } } - private <T> T runOnHandlerBlocking(Callable<T> c) throws InterruptedException { + private <T> T runOnHandlerBlocking(Callable<T> c) + throws InterruptedException, TimeoutException { if (Looper.myLooper() == mHandler.getLooper()) { try { return c.call(); @@ -451,7 +455,12 @@ public class ServiceWatcher implements ServiceConnection { FutureTask<T> task = new FutureTask<>(c); mHandler.post(task); try { - return task.get(); + // timeout will unblock callers, in particular if the caller is a binder thread to + // help reduce binder contention. this will still result in blocking the handler + // thread which may result in ANRs, but should make problems slightly more rare. + // the underlying solution is simply not to use this API at all, but that would + // require large refactors to very legacy code. + return task.get(BLOCKING_BINDER_TIMEOUT_MS, TimeUnit.MILLISECONDS); } catch (ExecutionException e) { // Function cannot throw exception, this should never happen throw new IllegalStateException(e); diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 4f54e64f6911..b5cab1f8d62b 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -3248,7 +3248,7 @@ public class AccountManagerService UserAccounts accounts = getUserAccounts(userId); logRecordWithUid( accounts, AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS, - userId); + uid); new Session(accounts, response, accountType, expectActivityLaunch, true /* stripAuthTokenFromResult */, null /* accountName */, false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 11cfe12f6e67..b8d38d9a04e7 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -139,6 +139,7 @@ import static com.android.server.wm.ActivityTaskManagerService.relaunchReasonToS import android.Manifest; import android.Manifest.permission; +import android.annotation.BroadcastBehavior; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -299,10 +300,8 @@ import android.view.View; import android.view.WindowManager; import android.view.autofill.AutofillManagerInternal; -import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.app.DumpHeapActivity; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.app.ProcessMap; @@ -503,6 +502,41 @@ public class ActivityManagerService extends IActivityManager.Stub static final int PERSISTENT_MASK = ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT; + // Intent sent when remote bugreport collection has been completed + private static final String INTENT_REMOTE_BUGREPORT_FINISHED = + "com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED"; + + /** + * Broadcast sent when heap dump collection has been completed. + */ + @BroadcastBehavior(includeBackground = true, protectedBroadcast = true) + private static final String ACTION_HEAP_DUMP_FINISHED = + "com.android.internal.intent.action.HEAP_DUMP_FINISHED"; + + /** + * The process we are reporting + */ + private static final String EXTRA_HEAP_DUMP_PROCESS_NAME = + "com.android.internal.extra.heap_dump.PROCESS_NAME"; + + /** + * The size limit the process reached. + */ + private static final String EXTRA_HEAP_DUMP_SIZE_BYTES = + "com.android.internal.extra.heap_dump.SIZE_BYTES"; + + /** + * Whether the user initiated the dump or not. + */ + private static final String EXTRA_HEAP_DUMP_IS_USER_INITIATED = + "com.android.internal.extra.heap_dump.IS_USER_INITIATED"; + + /** + * Optional name of package to directly launch. + */ + private static final String EXTRA_HEAP_DUMP_REPORT_PACKAGE = + "com.android.internal.extra.heap_dump.REPORT_PACKAGE"; + // If set, we will push process association information in to procstats. static final boolean TRACK_PROCSTATS_ASSOCIATIONS = true; @@ -1332,7 +1366,7 @@ public class ActivityManagerService extends IActivityManager.Stub int mProfileType = 0; final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>(); String mMemWatchDumpProcName; - String mMemWatchDumpFile; + Uri mMemWatchDumpUri; int mMemWatchDumpPid; int mMemWatchDumpUid; private boolean mMemWatchIsUserInitiated; @@ -1519,7 +1553,7 @@ public class ActivityManagerService extends IActivityManager.Stub static final int UPDATE_TIME_PREFERENCE_MSG = 41; static final int NOTIFY_CLEARTEXT_NETWORK_MSG = 49; static final int POST_DUMP_HEAP_NOTIFICATION_MSG = 50; - static final int DELETE_DUMPHEAP_MSG = 51; + static final int ABORT_DUMPHEAP_MSG = 51; static final int DISPATCH_UIDS_CHANGED_UI_MSG = 53; static final int SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG = 56; static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG = 57; @@ -1788,11 +1822,7 @@ public class ActivityManagerService extends IActivityManager.Stub final boolean isUserInitiated; synchronized (ActivityManagerService.this) { uid = mMemWatchDumpUid; - if (uid == SYSTEM_UID) { - procName = mContext.getString(R.string.android_system_label); - } else { - procName = mMemWatchDumpProcName; - } + procName = mMemWatchDumpProcName; Pair<Long, String> val = mMemWatchProcesses.get(procName, uid); if (val == null) { val = mMemWatchProcesses.get(procName, 0); @@ -1805,6 +1835,11 @@ public class ActivityManagerService extends IActivityManager.Stub reportPackage = null; } isUserInitiated = mMemWatchIsUserInitiated; + + mMemWatchDumpUri = null; + mMemWatchDumpProcName = null; + mMemWatchDumpPid = -1; + mMemWatchDumpUid = -1; } if (procName == null) { return; @@ -1813,65 +1848,29 @@ public class ActivityManagerService extends IActivityManager.Stub if (DEBUG_PSS) Slog.d(TAG_PSS, "Showing dump heap notification from " + procName + "/" + uid); - INotificationManager inm = NotificationManager.getService(); - if (inm == null) { - return; - } - - final int titleId = isUserInitiated - ? R.string.dump_heap_ready_notification : R.string.dump_heap_notification; - String text = mContext.getString(titleId, procName); - - Intent deleteIntent = new Intent(); - deleteIntent.setAction(DumpHeapActivity.ACTION_DELETE_DUMPHEAP); - Intent intent = new Intent(); - intent.setClassName("android", DumpHeapActivity.class.getName()); - intent.putExtra(DumpHeapActivity.KEY_PROCESS, procName); - intent.putExtra(DumpHeapActivity.KEY_SIZE, memLimit); - intent.putExtra(DumpHeapActivity.KEY_IS_USER_INITIATED, isUserInitiated); - intent.putExtra(DumpHeapActivity.KEY_IS_SYSTEM_PROCESS, uid == SYSTEM_UID); - if (reportPackage != null) { - intent.putExtra(DumpHeapActivity.KEY_DIRECT_LAUNCH, reportPackage); - } - int userId = UserHandle.getUserId(uid); - Notification notification = - new Notification.Builder(mContext, SystemNotificationChannels.DEVELOPER) - .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) - .setAutoCancel(true) - .setTicker(text) - .setColor(mContext.getColor( - com.android.internal.R.color.system_notification_accent_color)) - .setContentTitle(text) - .setContentText( - mContext.getText(R.string.dump_heap_notification_detail)) - .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, - intent, PendingIntent.FLAG_CANCEL_CURRENT, null, - new UserHandle(userId))) - .setDeleteIntent(PendingIntent.getBroadcastAsUser(mContext, 0, - deleteIntent, 0, UserHandle.SYSTEM)) - .build(); - - try { - inm.enqueueNotificationWithTag("android", "android", null, - SystemMessage.NOTE_DUMP_HEAP_NOTIFICATION, - notification, userId); - } catch (RuntimeException e) { - Slog.w(ActivityManagerService.TAG, - "Error showing notification for dump heap", e); - } catch (RemoteException e) { - } + Intent dumpFinishedIntent = new Intent(ACTION_HEAP_DUMP_FINISHED); + // Send this only to the Shell package. + dumpFinishedIntent.setPackage("com.android.shell"); + dumpFinishedIntent.putExtra(Intent.EXTRA_UID, uid); + dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_IS_USER_INITIATED, isUserInitiated); + dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_SIZE_BYTES, memLimit); + dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_REPORT_PACKAGE, reportPackage); + dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_PROCESS_NAME, procName); + + mContext.sendBroadcastAsUser(dumpFinishedIntent, + UserHandle.getUserHandleForUid(uid)); } break; - case DELETE_DUMPHEAP_MSG: { - revokeUriPermission(ActivityThread.currentActivityThread().getApplicationThread(), - null, DumpHeapActivity.JAVA_URI, - Intent.FLAG_GRANT_READ_URI_PERMISSION - | Intent.FLAG_GRANT_WRITE_URI_PERMISSION, - UserHandle.myUserId()); - synchronized (ActivityManagerService.this) { - mMemWatchDumpFile = null; - mMemWatchDumpProcName = null; - mMemWatchDumpPid = -1; - mMemWatchDumpUid = -1; + case ABORT_DUMPHEAP_MSG: { + String procName = (String) msg.obj; + if (procName != null) { + synchronized (ActivityManagerService.this) { + if (procName.equals(mMemWatchDumpProcName)) { + mMemWatchDumpProcName = null; + mMemWatchDumpUri = null; + mMemWatchDumpPid = -1; + mMemWatchDumpUid = -1; + } + } } } break; case SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG: { @@ -5245,17 +5244,6 @@ public class ActivityManagerService extends IActivityManager.Stub } }, pkgFilter); - IntentFilter dumpheapFilter = new IntentFilter(); - dumpheapFilter.addAction(DumpHeapActivity.ACTION_DELETE_DUMPHEAP); - mContext.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final long delay = intent.getBooleanExtra( - DumpHeapActivity.EXTRA_DELAY_DELETE, false) ? 5 * 60 * 1000 : 0; - mHandler.sendEmptyMessageDelayed(DELETE_DUMPHEAP_MSG, delay); - } - }, dumpheapFilter); - // Inform checkpointing systems of success try { // This line is needed to CTS test for the correct exception handling @@ -7758,7 +7746,7 @@ public class ActivityManagerService extends IActivityManager.Stub holder = getContentProviderExternalUnchecked(name, null, callingUid, "*checkContentProviderUriPermission*", userId); if (holder != null) { - return holder.provider.checkUriPermission(null, uri, callingUid, modeFlags); + return holder.provider.checkUriPermission(null, null, uri, callingUid, modeFlags); } } catch (RemoteException e) { Log.w(TAG, "Content provider dead retrieving " + uri, e); @@ -7923,7 +7911,7 @@ public class ActivityManagerService extends IActivityManager.Stub sCallerIdentity.set(new Identity( token, Binder.getCallingPid(), Binder.getCallingUid())); try { - pfd = cph.provider.openFile(null, uri, "r", null, token); + pfd = cph.provider.openFile(null, null, uri, "r", null, token); } catch (FileNotFoundException e) { // do nothing; pfd will be returned null } finally { @@ -10953,7 +10941,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } pw.print(" mMemWatchDumpProcName="); pw.println(mMemWatchDumpProcName); - pw.print(" mMemWatchDumpFile="); pw.println(mMemWatchDumpFile); + pw.print(" mMemWatchDumpUri="); pw.println(mMemWatchDumpUri); pw.print(" mMemWatchDumpPid="); pw.println(mMemWatchDumpPid); pw.print(" mMemWatchDumpUid="); pw.println(mMemWatchDumpUid); pw.print(" mMemWatchIsUserInitiated="); pw.println(mMemWatchIsUserInitiated); @@ -11250,10 +11238,14 @@ public class ActivityManagerService extends IActivityManager.Stub } final long dtoken = proto.start(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.DUMP); - proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PROC_NAME, mMemWatchDumpProcName); - proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.FILE, mMemWatchDumpFile); - proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PID, mMemWatchDumpPid); - proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.UID, mMemWatchDumpUid); + proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PROC_NAME, + mMemWatchDumpProcName); + proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.URI, + mMemWatchDumpUri.toString()); + proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PID, + mMemWatchDumpPid); + proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.UID, + mMemWatchDumpUid); proto.write( ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.IS_USER_INITIATED, mMemWatchIsUserInitiated); @@ -16395,56 +16387,44 @@ public class ActivityManagerService extends IActivityManager.Stub } } - private static final class RecordPssRunnable implements Runnable { - private final ActivityManagerService mService; + /** @hide */ + public static Uri makeHeapDumpUri(String procName) { + return Uri.parse("content://com.android.shell.heapdump/" + procName + "_javaheap.bin"); + } + + private final class RecordPssRunnable implements Runnable { private final ProcessRecord mProc; - private final File mHeapdumpFile; + private final Uri mDumpUri; + private final ContentResolver mContentResolver; - RecordPssRunnable(ActivityManagerService service, ProcessRecord proc, File heapdumpFile) { - this.mService = service; - this.mProc = proc; - this.mHeapdumpFile = heapdumpFile; + RecordPssRunnable(ProcessRecord proc, Uri dumpUri, ContentResolver contentResolver) { + mProc = proc; + mDumpUri = dumpUri; + mContentResolver = contentResolver; } @Override public void run() { - mService.revokeUriPermission(ActivityThread.currentActivityThread() - .getApplicationThread(), - null, DumpHeapActivity.JAVA_URI, - Intent.FLAG_GRANT_READ_URI_PERMISSION - | Intent.FLAG_GRANT_WRITE_URI_PERMISSION, - UserHandle.myUserId()); - ParcelFileDescriptor fd = null; - try { - mHeapdumpFile.delete(); - fd = ParcelFileDescriptor.open(mHeapdumpFile, - ParcelFileDescriptor.MODE_CREATE - | ParcelFileDescriptor.MODE_TRUNCATE - | ParcelFileDescriptor.MODE_WRITE_ONLY - | ParcelFileDescriptor.MODE_APPEND); + try (ParcelFileDescriptor fd = mContentResolver.openFileDescriptor(mDumpUri, "rw")) { IApplicationThread thread = mProc.thread; if (thread != null) { try { if (DEBUG_PSS) { Slog.d(TAG_PSS, "Requesting dump heap from " - + mProc + " to " + mHeapdumpFile); + + mProc + " to " + mDumpUri.getPath()); } thread.dumpHeap(/* managed= */ true, /* mallocInfo= */ false, /* runGc= */ false, - mHeapdumpFile.toString(), fd, + mDumpUri.getPath(), fd, /* finishCallback= */ null); } catch (RemoteException e) { } } - } catch (FileNotFoundException e) { - e.printStackTrace(); - } finally { - if (fd != null) { - try { - fd.close(); - } catch (IOException e) { - } - } + } catch (IOException e) { + Slog.e(TAG, "Failed to dump heap", e); + // Need to clear the heap dump variables, otherwise no further heap dumps will be + // attempted. + abortHeapDump(mProc.processName); } } } @@ -16513,13 +16493,20 @@ public class ActivityManagerService extends IActivityManager.Stub } private void startHeapDumpLocked(ProcessRecord proc, boolean isUserInitiated) { - final File heapdumpFile = DumpHeapProvider.getJavaFile(); mMemWatchDumpProcName = proc.processName; - mMemWatchDumpFile = heapdumpFile.toString(); + mMemWatchDumpUri = makeHeapDumpUri(proc.processName); mMemWatchDumpPid = proc.pid; mMemWatchDumpUid = proc.uid; mMemWatchIsUserInitiated = isUserInitiated; - BackgroundThread.getHandler().post(new RecordPssRunnable(this, proc, heapdumpFile)); + Context ctx; + try { + ctx = mContext.createPackageContextAsUser("android", 0, + UserHandle.getUserHandleForUid(mMemWatchDumpUid)); + } catch (NameNotFoundException e) { + throw new RuntimeException("android package not found."); + } + BackgroundThread.getHandler().post( + new RecordPssRunnable(proc, mMemWatchDumpUri, ctx.getContentResolver())); } /** @@ -17715,9 +17702,9 @@ public class ActivityManagerService extends IActivityManager.Stub + " does not match last pid " + mMemWatchDumpPid); return; } - if (mMemWatchDumpFile == null || !mMemWatchDumpFile.equals(path)) { + if (mMemWatchDumpUri == null || !mMemWatchDumpUri.getPath().equals(path)) { Slog.w(TAG, "dumpHeapFinished: Calling path " + path - + " does not match last path " + mMemWatchDumpFile); + + " does not match last path " + mMemWatchDumpUri); return; } if (DEBUG_PSS) Slog.d(TAG_PSS, "Dump heap finished for " + path); @@ -17728,6 +17715,13 @@ public class ActivityManagerService extends IActivityManager.Stub } } + /** Clear the currently executing heap dump variables so a new heap dump can be started. */ + private void abortHeapDump(String procName) { + Message msg = mHandler.obtainMessage(ABORT_DUMPHEAP_MSG); + msg.obj = procName; + mHandler.sendMessage(msg); + } + /** In this method we try to acquire our lock to make sure that we have not deadlocked */ public void monitor() { synchronized (this) { } diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 6a29c75b702a..8095020ce4ce 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -383,7 +383,11 @@ class AppErrors { // and then the delayed summary kill will be a no-op. final ProcessRecord p = proc; mService.mHandler.postDelayed( - () -> killAppImmediateLocked(p, "forced", "killed for invalid state"), + () -> { + synchronized (mService) { + killAppImmediateLocked(p, "forced", "killed for invalid state"); + } + }, 5000L); } } diff --git a/services/core/java/com/android/server/am/DumpHeapProvider.java b/services/core/java/com/android/server/am/DumpHeapProvider.java deleted file mode 100644 index a8b639ecf8d4..000000000000 --- a/services/core/java/com/android/server/am/DumpHeapProvider.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.am; - -import android.content.ContentProvider; -import android.content.ContentValues; -import android.database.Cursor; -import android.net.Uri; -import android.os.Environment; -import android.os.ParcelFileDescriptor; - -import java.io.File; -import java.io.FileNotFoundException; - -public class DumpHeapProvider extends ContentProvider { - static final Object sLock = new Object(); - static File sHeapDumpJavaFile; - - static public File getJavaFile() { - synchronized (sLock) { - return sHeapDumpJavaFile; - } - } - - @Override - public boolean onCreate() { - synchronized (sLock) { - File dataDir = Environment.getDataDirectory(); - File systemDir = new File(dataDir, "system"); - File heapdumpDir = new File(systemDir, "heapdump"); - heapdumpDir.mkdir(); - sHeapDumpJavaFile = new File(heapdumpDir, "javaheap.bin"); - } - return true; - } - - @Override - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { - return null; - } - - @Override - public String getType(Uri uri) { - return "application/octet-stream"; - } - - @Override - public Uri insert(Uri uri, ContentValues values) { - return null; - } - - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - return 0; - } - - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - return 0; - } - - @Override - public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { - synchronized (sLock) { - String path = uri.getEncodedPath(); - final String tag = Uri.decode(path); - if (tag.equals("/java")) { - return ParcelFileDescriptor.open(sHeapDumpJavaFile, - ParcelFileDescriptor.MODE_READ_ONLY); - } else { - throw new FileNotFoundException("Invalid path for " + uri); - } - } - } -} diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 857506889033..eecfdbded750 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -1182,8 +1182,8 @@ public class AppOpsService extends IAppOpsService.Stub { } } else { for (int j=0; j<ops.length; j++) { - int code = uidState.opModes.keyAt(j); - if (code >= 0) { + int code = ops[j]; + if (uidState.opModes.indexOfKey(code) >= 0) { if (resOps == null) { resOps = new ArrayList<>(); } diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index f7ac04041ed6..6010b1dc88c4 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -57,6 +57,13 @@ import java.io.PrintWriter; private final @NonNull AudioService mAudioService; private final @NonNull Context mContext; + /** Forced device usage for communications sent to AudioSystem */ + private int mForcedUseForComm; + /** + * Externally reported force device usage state returned by getters: always consistent + * with requests by setters */ + private int mForcedUseForCommExt; + // Manages all connected devices, only ever accessed on the message loop private final AudioDeviceInventory mDeviceInventory; // Manages notifications to BT service @@ -64,34 +71,24 @@ import java.io.PrintWriter; //------------------------------------------------------------------- - /** - * Lock to guard: - * - any changes to the message queue: enqueueing or removing any message - * - state of A2DP enabled - * - force use for communication + SCO changes - */ - private final Object mDeviceBrokerLock = new Object(); - - @GuardedBy("mDeviceBrokerLock") + // we use a different lock than mDeviceStateLock so as not to create + // lock contention between enqueueing a message and handling them + private static final Object sLastDeviceConnectionMsgTimeLock = new Object(); + @GuardedBy("sLastDeviceConnectionMsgTimeLock") private static long sLastDeviceConnectMsgTime = 0; + // General lock to be taken whenever the state of the audio devices is to be checked or changed + private final Object mDeviceStateLock = new Object(); - /** Request to override default use of A2DP for media */ - @GuardedBy("mDeviceBrokerLock") + // Request to override default use of A2DP for media. + @GuardedBy("mDeviceStateLock") private boolean mBluetoothA2dpEnabled; - /** Forced device usage for communications sent to AudioSystem */ - @GuardedBy("mDeviceBrokerLock") - private int mForcedUseForComm; - /** - * Externally reported force device usage state returned by getters: always consistent - * with requests by setters */ - @GuardedBy("mDeviceBrokerLock") - private int mForcedUseForCommExt; - + // lock always taken when accessing AudioService.mSetModeDeathHandlers + // TODO do not "share" the lock between AudioService and BtHelpr, see b/123769055 + /*package*/ final Object mSetModeLock = new Object(); //------------------------------------------------------------------- - /** Normal constructor used by AudioService */ /*package*/ AudioDeviceBroker(@NonNull Context context, @NonNull AudioService service) { mContext = context; mAudioService = service; @@ -130,37 +127,38 @@ import java.io.PrintWriter; // All post* methods are asynchronous /*package*/ void onSystemReady() { - mBtHelper.onSystemReady(); + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.onSystemReady(); + } + } } /*package*/ void onAudioServerDied() { // Restore forced usage for communications and record - synchronized (mDeviceBrokerLock) { + synchronized (mDeviceStateLock) { AudioSystem.setParameters( "BT_SCO=" + (mForcedUseForComm == AudioSystem.FORCE_BT_SCO ? "on" : "off")); onSetForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, "onAudioServerDied"); onSetForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm, "onAudioServerDied"); - - // restore devices - sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE); } + // restore devices + sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE); } /*package*/ void setForceUse_Async(int useCase, int config, String eventSource) { - synchronized (mDeviceBrokerLock) { - sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE, - useCase, config, eventSource); - } + sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE, + useCase, config, eventSource); } /*package*/ void toggleHdmiIfConnected_Async() { - synchronized (mDeviceBrokerLock) { - sendMsgNoDelay(MSG_TOGGLE_HDMI, SENDMSG_QUEUE); - } + sendMsgNoDelay(MSG_TOGGLE_HDMI, SENDMSG_QUEUE); } /*package*/ void disconnectAllBluetoothProfiles() { + synchronized (mDeviceStateLock) { mBtHelper.disconnectAllBluetoothProfiles(); + } } /** @@ -170,11 +168,15 @@ import java.io.PrintWriter; * @param intent */ /*package*/ void receiveBtEvent(@NonNull Intent intent) { - mBtHelper.receiveBtEvent(intent); + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.receiveBtEvent(intent); + } + } } /*package*/ void setBluetoothA2dpOn_Async(boolean on, String source) { - synchronized (mDeviceBrokerLock) { + synchronized (mDeviceStateLock) { if (mBluetoothA2dpEnabled == on) { return; } @@ -194,7 +196,7 @@ import java.io.PrintWriter; * @return true if speakerphone state changed */ /*package*/ boolean setSpeakerphoneOn(boolean on, String eventSource) { - synchronized (mDeviceBrokerLock) { + synchronized (mDeviceStateLock) { final boolean wasOn = isSpeakerphoneOn(); if (on) { if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) { @@ -212,7 +214,7 @@ import java.io.PrintWriter; } /*package*/ boolean isSpeakerphoneOn() { - synchronized (mDeviceBrokerLock) { + synchronized (mDeviceStateLock) { return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER); } } @@ -221,7 +223,9 @@ import java.io.PrintWriter; @AudioService.ConnectionState int state, String address, String name, String caller) { //TODO move logging here just like in setBluetooth* methods - mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller); + synchronized (mDeviceStateLock) { + mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller); + } } private static final class BtDeviceConnectionInfo { @@ -255,24 +259,27 @@ import java.io.PrintWriter; final BtDeviceConnectionInfo info = new BtDeviceConnectionInfo(device, state, profile, suppressNoisyIntent, a2dpVolume); - synchronized (mDeviceBrokerLock) { - // when receiving a request to change the connection state of a device, this last - // request is the source of truth, so cancel all previous requests - mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION, - device); - mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION, - device); - mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED, - device); - mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED, - device); - - sendLMsgNoDelay( - state == BluetoothProfile.STATE_CONNECTED - ? MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION - : MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION, - SENDMSG_QUEUE, info); - } + // when receiving a request to change the connection state of a device, this last request + // is the source of truth, so cancel all previous requests + removeAllA2dpConnectionEvents(device); + + sendLMsgNoDelay( + state == BluetoothProfile.STATE_CONNECTED + ? MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION + : MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION, + SENDMSG_QUEUE, info); + } + + /** remove all previously scheduled connection and disconnection events for the given device */ + private void removeAllA2dpConnectionEvents(@NonNull BluetoothDevice device) { + mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION, + device); + mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION, + device); + mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED, + device); + mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED, + device); } private static final class HearingAidDeviceConnectionInfo { @@ -298,31 +305,28 @@ import java.io.PrintWriter; boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) { final HearingAidDeviceConnectionInfo info = new HearingAidDeviceConnectionInfo( device, state, suppressNoisyIntent, musicDevice, eventSource); - synchronized (mDeviceBrokerLock) { - sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info); - } + sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info); } // never called by system components /*package*/ void setBluetoothScoOnByApp(boolean on) { - synchronized (mDeviceBrokerLock) { + synchronized (mDeviceStateLock) { mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE; } } /*package*/ boolean isBluetoothScoOnForApp() { - synchronized (mDeviceBrokerLock) { + synchronized (mDeviceStateLock) { return mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO; } } /*package*/ void setBluetoothScoOn(boolean on, String eventSource) { //Log.i(TAG, "setBluetoothScoOnInt: " + on + " " + eventSource); - final boolean isBtScoOn = mBtHelper.isBluetoothScoOn(); - synchronized (mDeviceBrokerLock) { + synchronized (mDeviceStateLock) { if (on) { // do not accept SCO ON if SCO audio is not connected - if (!isBtScoOn) { + if (!mBtHelper.isBluetoothScoOn()) { mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO; return; } @@ -342,55 +346,58 @@ import java.io.PrintWriter; } /*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) { - return mDeviceInventory.startWatchingRoutes(observer); - + synchronized (mDeviceStateLock) { + return mDeviceInventory.startWatchingRoutes(observer); + } } /*package*/ AudioRoutesInfo getCurAudioRoutes() { - return mDeviceInventory.getCurAudioRoutes(); + synchronized (mDeviceStateLock) { + return mDeviceInventory.getCurAudioRoutes(); + } } /*package*/ boolean isAvrcpAbsoluteVolumeSupported() { - return mBtHelper.isAvrcpAbsoluteVolumeSupported(); + synchronized (mDeviceStateLock) { + return mBtHelper.isAvrcpAbsoluteVolumeSupported(); + } } /*package*/ boolean isBluetoothA2dpOn() { - synchronized (mDeviceBrokerLock) { + synchronized (mDeviceStateLock) { return mBluetoothA2dpEnabled; } } /*package*/ void postSetAvrcpAbsoluteVolumeIndex(int index) { - synchronized (mDeviceBrokerLock) { - sendIMsgNoDelay(MSG_I_SET_AVRCP_ABSOLUTE_VOLUME, SENDMSG_REPLACE, index); - } + sendIMsgNoDelay(MSG_I_SET_AVRCP_ABSOLUTE_VOLUME, SENDMSG_REPLACE, index); } /*package*/ void postSetHearingAidVolumeIndex(int index, int streamType) { - synchronized (mDeviceBrokerLock) { - sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType); - } + sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType); } /*package*/ void postDisconnectBluetoothSco(int exceptPid) { - synchronized (mDeviceBrokerLock) { - sendIMsgNoDelay(MSG_I_DISCONNECT_BT_SCO, SENDMSG_REPLACE, exceptPid); - } + sendIMsgNoDelay(MSG_I_DISCONNECT_BT_SCO, SENDMSG_REPLACE, exceptPid); } /*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) { - synchronized (mDeviceBrokerLock) { - sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device); - } + sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device); } + @GuardedBy("mSetModeLock") /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode, @NonNull String eventSource) { - mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource); + synchronized (mDeviceStateLock) { + mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource); + } } + @GuardedBy("mSetModeLock") /*package*/ void stopBluetoothScoForClient_Sync(IBinder cb, @NonNull String eventSource) { - mBtHelper.stopBluetoothScoForClient(cb, eventSource); + synchronized (mDeviceStateLock) { + mBtHelper.stopBluetoothScoForClient(cb, eventSource); + } } //--------------------------------------------------------------------- @@ -453,109 +460,77 @@ import java.io.PrintWriter; //--------------------------------------------------------------------- // Message handling on behalf of helper classes /*package*/ void postBroadcastScoConnectionState(int state) { - synchronized (mDeviceBrokerLock) { - sendIMsgNoDelay(MSG_I_BROADCAST_BT_CONNECTION_STATE, SENDMSG_QUEUE, state); - } + sendIMsgNoDelay(MSG_I_BROADCAST_BT_CONNECTION_STATE, SENDMSG_QUEUE, state); } /*package*/ void postBroadcastBecomingNoisy() { - synchronized (mDeviceBrokerLock) { - sendMsgNoDelay(MSG_BROADCAST_AUDIO_BECOMING_NOISY, SENDMSG_REPLACE); - } + sendMsgNoDelay(MSG_BROADCAST_AUDIO_BECOMING_NOISY, SENDMSG_REPLACE); } /*package*/ void postA2dpSinkConnection(@AudioService.BtProfileConnectionState int state, @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) { - synchronized (mDeviceBrokerLock) { - sendILMsg(state == BluetoothA2dp.STATE_CONNECTED - ? MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED - : MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED, - SENDMSG_QUEUE, - state, btDeviceInfo, delay); - } + sendILMsg(state == BluetoothA2dp.STATE_CONNECTED + ? MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED + : MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED, + SENDMSG_QUEUE, + state, btDeviceInfo, delay); } /*package*/ void postA2dpSourceConnection(@AudioService.BtProfileConnectionState int state, @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) { - synchronized (mDeviceBrokerLock) { - sendILMsg(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, - state, btDeviceInfo, delay); - } + sendILMsg(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, + state, btDeviceInfo, delay); } /*package*/ void postSetWiredDeviceConnectionState( AudioDeviceInventory.WiredDeviceConnectionState connectionState, int delay) { - synchronized (mDeviceBrokerLock) { - sendLMsg(MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE, SENDMSG_QUEUE, - connectionState, delay); - } + sendLMsg(MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE, SENDMSG_QUEUE, connectionState, delay); } /*package*/ void postSetHearingAidConnectionState( @AudioService.BtProfileConnectionState int state, @NonNull BluetoothDevice device, int delay) { - synchronized (mDeviceBrokerLock) { - sendILMsg(MSG_IL_SET_HEARING_AID_CONNECTION_STATE, SENDMSG_QUEUE, - state, - device, - delay); - } + sendILMsg(MSG_IL_SET_HEARING_AID_CONNECTION_STATE, SENDMSG_QUEUE, + state, + device, + delay); } /*package*/ void postDisconnectA2dp() { - synchronized (mDeviceBrokerLock) { - sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE); - } + sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE); } /*package*/ void postDisconnectA2dpSink() { - synchronized (mDeviceBrokerLock) { - sendMsgNoDelay(MSG_DISCONNECT_A2DP_SINK, SENDMSG_QUEUE); - } + sendMsgNoDelay(MSG_DISCONNECT_A2DP_SINK, SENDMSG_QUEUE); } /*package*/ void postDisconnectHearingAid() { - synchronized (mDeviceBrokerLock) { - sendMsgNoDelay(MSG_DISCONNECT_BT_HEARING_AID, SENDMSG_QUEUE); - } + sendMsgNoDelay(MSG_DISCONNECT_BT_HEARING_AID, SENDMSG_QUEUE); } /*package*/ void postDisconnectHeadset() { - synchronized (mDeviceBrokerLock) { - sendMsgNoDelay(MSG_DISCONNECT_BT_HEADSET, SENDMSG_QUEUE); - } + sendMsgNoDelay(MSG_DISCONNECT_BT_HEADSET, SENDMSG_QUEUE); } /*package*/ void postBtA2dpProfileConnected(BluetoothA2dp a2dpProfile) { - synchronized (mDeviceBrokerLock) { - sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP, SENDMSG_QUEUE, a2dpProfile); - } + sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP, SENDMSG_QUEUE, a2dpProfile); } /*package*/ void postBtA2dpSinkProfileConnected(BluetoothProfile profile) { - synchronized (mDeviceBrokerLock) { - sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK, SENDMSG_QUEUE, profile); - } + sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK, SENDMSG_QUEUE, profile); } /*package*/ void postBtHeasetProfileConnected(BluetoothHeadset headsetProfile) { - synchronized (mDeviceBrokerLock) { - sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET, SENDMSG_QUEUE, - headsetProfile); - } + sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET, SENDMSG_QUEUE, headsetProfile); } /*package*/ void postBtHearingAidProfileConnected(BluetoothHearingAid hearingAidProfile) { - synchronized (mDeviceBrokerLock) { - sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID, SENDMSG_QUEUE, - hearingAidProfile); - } + sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID, SENDMSG_QUEUE, + hearingAidProfile); } /*package*/ void postScoClientDied(Object obj) { - synchronized (mDeviceBrokerLock) { - sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj); - } + sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj); } //--------------------------------------------------------------------- @@ -570,7 +545,7 @@ import java.io.PrintWriter; .append(") from u/pid:").append(Binder.getCallingUid()).append("/") .append(Binder.getCallingPid()).append(" src:").append(source).toString(); - synchronized (mDeviceBrokerLock) { + synchronized (mDeviceStateLock) { mBluetoothA2dpEnabled = on; mBrokerHandler.removeMessages(MSG_IIL_SET_FORCE_BT_A2DP_USE); onSetForceUse( @@ -582,85 +557,71 @@ import java.io.PrintWriter; /*package*/ boolean handleDeviceConnection(boolean connect, int device, String address, String deviceName) { - return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName); + synchronized (mDeviceStateLock) { + return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName); + } } /*package*/ void postSetA2dpSourceConnectionState(@BluetoothProfile.BtProfileState int state, @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) { final int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0; - synchronized (mDeviceBrokerLock) { - sendILMsgNoDelay(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, state, - btDeviceInfo); - } + sendILMsgNoDelay(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, state, + btDeviceInfo); } /*package*/ void handleFailureToConnectToBtHeadsetService(int delay) { - synchronized (mDeviceBrokerLock) { - sendMsg(MSG_BT_HEADSET_CNCT_FAILED, SENDMSG_REPLACE, delay); - } + sendMsg(MSG_BT_HEADSET_CNCT_FAILED, SENDMSG_REPLACE, delay); } /*package*/ void handleCancelFailureToConnectToBtHeadsetService() { - synchronized (mDeviceBrokerLock) { - mBrokerHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED); - } + mBrokerHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED); } /*package*/ void postReportNewRoutes() { - synchronized (mDeviceBrokerLock) { - sendMsgNoDelay(MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP); - } + sendMsgNoDelay(MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP); } /*package*/ void cancelA2dpDockTimeout() { - synchronized (mDeviceBrokerLock) { - mBrokerHandler.removeMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT); - } + mBrokerHandler.removeMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT); } - // FIXME: used by? /*package*/ void postA2dpActiveDeviceChange( @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) { - synchronized (mDeviceBrokerLock) { - sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo); - } + sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo); } /*package*/ boolean hasScheduledA2dpDockTimeout() { - synchronized (mDeviceBrokerLock) { - return mBrokerHandler.hasMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT); - } + return mBrokerHandler.hasMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT); } // must be called synchronized on mConnectedDevices /*package*/ boolean hasScheduledA2dpSinkConnectionState(BluetoothDevice btDevice) { - synchronized (mDeviceBrokerLock) { - return (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED, - new BtHelper.BluetoothA2dpDeviceInfo(btDevice)) - || mBrokerHandler.hasMessages( - MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED, - new BtHelper.BluetoothA2dpDeviceInfo(btDevice))); - } + return (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED, + new BtHelper.BluetoothA2dpDeviceInfo(btDevice)) + || mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED, + new BtHelper.BluetoothA2dpDeviceInfo(btDevice))); } /*package*/ void setA2dpDockTimeout(String address, int a2dpCodec, int delayMs) { - synchronized (mDeviceBrokerLock) { - sendILMsg(MSG_IL_BTA2DP_DOCK_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs); - } + sendILMsg(MSG_IL_BTA2DP_DOCK_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs); } /*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) { - mBtHelper.setAvrcpAbsoluteVolumeSupported(supported); + synchronized (mDeviceStateLock) { + mBtHelper.setAvrcpAbsoluteVolumeSupported(supported); + } } /*package*/ boolean getBluetoothA2dpEnabled() { - synchronized (mDeviceBrokerLock) { + synchronized (mDeviceStateLock) { return mBluetoothA2dpEnabled; } } /*package*/ int getA2dpCodec(@NonNull BluetoothDevice device) { - return mBtHelper.getA2dpCodec(device); + synchronized (mDeviceStateLock) { + return mBtHelper.getA2dpCodec(device); + } } /*package*/ void dump(PrintWriter pw, String prefix) { @@ -748,101 +709,156 @@ import java.io.PrintWriter; public void handleMessage(Message msg) { switch (msg.what) { case MSG_RESTORE_DEVICES: - mDeviceInventory.onRestoreDevices(); - mBtHelper.onAudioServerDiedRestoreA2dp(); + synchronized (mDeviceStateLock) { + mDeviceInventory.onRestoreDevices(); + mBtHelper.onAudioServerDiedRestoreA2dp(); + } break; case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE: - mDeviceInventory.onSetWiredDeviceConnectionState( - (AudioDeviceInventory.WiredDeviceConnectionState) msg.obj); + synchronized (mDeviceStateLock) { + mDeviceInventory.onSetWiredDeviceConnectionState( + (AudioDeviceInventory.WiredDeviceConnectionState) msg.obj); + } break; case MSG_I_BROADCAST_BT_CONNECTION_STATE: - mBtHelper.onBroadcastScoConnectionState(msg.arg1); + synchronized (mDeviceStateLock) { + mBtHelper.onBroadcastScoConnectionState(msg.arg1); + } break; case MSG_IIL_SET_FORCE_USE: // intended fall-through case MSG_IIL_SET_FORCE_BT_A2DP_USE: onSetForceUse(msg.arg1, msg.arg2, (String) msg.obj); break; case MSG_REPORT_NEW_ROUTES: - mDeviceInventory.onReportNewRoutes(); + synchronized (mDeviceStateLock) { + mDeviceInventory.onReportNewRoutes(); + } break; case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED: case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED: - mDeviceInventory.onSetA2dpSinkConnectionState( - (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1); + synchronized (mDeviceStateLock) { + mDeviceInventory.onSetA2dpSinkConnectionState( + (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1); + } break; case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE: - mDeviceInventory.onSetA2dpSourceConnectionState( - (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1); + synchronized (mDeviceStateLock) { + mDeviceInventory.onSetA2dpSourceConnectionState( + (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1); + } break; case MSG_IL_SET_HEARING_AID_CONNECTION_STATE: - mDeviceInventory.onSetHearingAidConnectionState( - (BluetoothDevice) msg.obj, msg.arg1, - mAudioService.getHearingAidStreamType()); + synchronized (mDeviceStateLock) { + mDeviceInventory.onSetHearingAidConnectionState( + (BluetoothDevice) msg.obj, msg.arg1, + mAudioService.getHearingAidStreamType()); + } break; case MSG_BT_HEADSET_CNCT_FAILED: - mBtHelper.resetBluetoothSco(); + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.resetBluetoothSco(); + } + } break; case MSG_IL_BTA2DP_DOCK_TIMEOUT: // msg.obj == address of BTA2DP device - mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1); + synchronized (mDeviceStateLock) { + mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1); + } break; case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: final int a2dpCodec; final BluetoothDevice btDevice = (BluetoothDevice) msg.obj; - // FIXME why isn't the codec coming with the request? codec should be - // provided by BT when it calls - // AudioManager.handleBluetoothA2dpDeviceConfigChange(BluetoothDevice) - a2dpCodec = mBtHelper.getA2dpCodec(btDevice); - mDeviceInventory.onBluetoothA2dpActiveDeviceChange( - new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec), - BtHelper.EVENT_DEVICE_CONFIG_CHANGE); + synchronized (mDeviceStateLock) { + a2dpCodec = mBtHelper.getA2dpCodec(btDevice); + mDeviceInventory.onBluetoothA2dpActiveDeviceChange( + new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec), + BtHelper.EVENT_DEVICE_CONFIG_CHANGE); + } break; case MSG_BROADCAST_AUDIO_BECOMING_NOISY: onSendBecomingNoisyIntent(); break; case MSG_II_SET_HEARING_AID_VOLUME: - mBtHelper.setHearingAidVolume(msg.arg1, msg.arg2); + synchronized (mDeviceStateLock) { + mBtHelper.setHearingAidVolume(msg.arg1, msg.arg2); + } break; case MSG_I_SET_AVRCP_ABSOLUTE_VOLUME: - mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1); + synchronized (mDeviceStateLock) { + mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1); + } break; case MSG_I_DISCONNECT_BT_SCO: - mBtHelper.disconnectBluetoothSco(msg.arg1); + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.disconnectBluetoothSco(msg.arg1); + } + } break; case MSG_L_SCOCLIENT_DIED: - mBtHelper.scoClientDied(msg.obj); + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.scoClientDied(msg.obj); + } + } break; case MSG_TOGGLE_HDMI: - mDeviceInventory.onToggleHdmi(); + synchronized (mDeviceStateLock) { + mDeviceInventory.onToggleHdmi(); + } break; case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE: - mDeviceInventory.onBluetoothA2dpActiveDeviceChange( - (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, - BtHelper.EVENT_ACTIVE_DEVICE_CHANGE); + synchronized (mDeviceStateLock) { + mDeviceInventory.onBluetoothA2dpActiveDeviceChange( + (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, + BtHelper.EVENT_ACTIVE_DEVICE_CHANGE); + } break; case MSG_DISCONNECT_A2DP: - mDeviceInventory.disconnectA2dp(); + synchronized (mDeviceStateLock) { + mDeviceInventory.disconnectA2dp(); + } break; case MSG_DISCONNECT_A2DP_SINK: - mDeviceInventory.disconnectA2dpSink(); + synchronized (mDeviceStateLock) { + mDeviceInventory.disconnectA2dpSink(); + } break; case MSG_DISCONNECT_BT_HEARING_AID: - mDeviceInventory.disconnectHearingAid(); + synchronized (mDeviceStateLock) { + mDeviceInventory.disconnectHearingAid(); + } break; case MSG_DISCONNECT_BT_HEADSET: - mBtHelper.disconnectHeadset(); + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.disconnectHeadset(); + } + } break; case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP: - mBtHelper.onA2dpProfileConnected((BluetoothA2dp) msg.obj); + synchronized (mDeviceStateLock) { + mBtHelper.onA2dpProfileConnected((BluetoothA2dp) msg.obj); + } break; case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK: - mBtHelper.onA2dpSinkProfileConnected((BluetoothProfile) msg.obj); + synchronized (mDeviceStateLock) { + mBtHelper.onA2dpSinkProfileConnected((BluetoothProfile) msg.obj); + } break; case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID: - mBtHelper.onHearingAidProfileConnected((BluetoothHearingAid) msg.obj); + synchronized (mDeviceStateLock) { + mBtHelper.onHearingAidProfileConnected((BluetoothHearingAid) msg.obj); + } break; case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET: - mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj); + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj); + } + } break; case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION: case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION: { @@ -855,9 +871,11 @@ import java.io.PrintWriter; + " addr=" + info.mDevice.getAddress() + " prof=" + info.mProfile + " supprNoisy=" + info.mSupprNoisy + " vol=" + info.mVolume)).printLog(TAG)); - mDeviceInventory.setBluetoothA2dpDeviceConnectionState( - info.mDevice, info.mState, info.mProfile, info.mSupprNoisy, - AudioSystem.DEVICE_NONE, info.mVolume); + synchronized (mDeviceStateLock) { + mDeviceInventory.setBluetoothA2dpDeviceConnectionState( + info.mDevice, info.mState, info.mProfile, info.mSupprNoisy, + AudioSystem.DEVICE_NONE, info.mVolume); + } } break; case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: { final HearingAidDeviceConnectionInfo info = @@ -867,8 +885,10 @@ import java.io.PrintWriter; + " addr=" + info.mDevice.getAddress() + " supprNoisy=" + info.mSupprNoisy + " src=" + info.mEventSource)).printLog(TAG)); - mDeviceInventory.setBluetoothHearingAidDeviceConnectionState( - info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice); + synchronized (mDeviceStateLock) { + mDeviceInventory.setBluetoothHearingAidDeviceConnectionState( + info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice); + } } break; default: Log.wtf(TAG, "Invalid message " + msg.what); @@ -953,57 +973,46 @@ import java.io.PrintWriter; /** If the msg is already queued, queue this one and leave the old. */ private static final int SENDMSG_QUEUE = 2; - @GuardedBy("mDeviceBrokerLock") private void sendMsg(int msg, int existingMsgPolicy, int delay) { sendIILMsg(msg, existingMsgPolicy, 0, 0, null, delay); } - @GuardedBy("mDeviceBrokerLock") private void sendILMsg(int msg, int existingMsgPolicy, int arg, Object obj, int delay) { sendIILMsg(msg, existingMsgPolicy, arg, 0, obj, delay); } - @GuardedBy("mDeviceBrokerLock") private void sendLMsg(int msg, int existingMsgPolicy, Object obj, int delay) { sendIILMsg(msg, existingMsgPolicy, 0, 0, obj, delay); } - @GuardedBy("mDeviceBrokerLock") private void sendIMsg(int msg, int existingMsgPolicy, int arg, int delay) { sendIILMsg(msg, existingMsgPolicy, arg, 0, null, delay); } - @GuardedBy("mDeviceBrokerLock") private void sendMsgNoDelay(int msg, int existingMsgPolicy) { sendIILMsg(msg, existingMsgPolicy, 0, 0, null, 0); } - @GuardedBy("mDeviceBrokerLock") private void sendIMsgNoDelay(int msg, int existingMsgPolicy, int arg) { sendIILMsg(msg, existingMsgPolicy, arg, 0, null, 0); } - @GuardedBy("mDeviceBrokerLock") private void sendIIMsgNoDelay(int msg, int existingMsgPolicy, int arg1, int arg2) { sendIILMsg(msg, existingMsgPolicy, arg1, arg2, null, 0); } - @GuardedBy("mDeviceBrokerLock") private void sendILMsgNoDelay(int msg, int existingMsgPolicy, int arg, Object obj) { sendIILMsg(msg, existingMsgPolicy, arg, 0, obj, 0); } - @GuardedBy("mDeviceBrokerLock") private void sendLMsgNoDelay(int msg, int existingMsgPolicy, Object obj) { sendIILMsg(msg, existingMsgPolicy, 0, 0, obj, 0); } - @GuardedBy("mDeviceBrokerLock") private void sendIILMsgNoDelay(int msg, int existingMsgPolicy, int arg1, int arg2, Object obj) { sendIILMsg(msg, existingMsgPolicy, arg1, arg2, obj, 0); } - @GuardedBy("mDeviceBrokerLock") private void sendIILMsg(int msg, int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) { if (existingMsgPolicy == SENDMSG_REPLACE) { @@ -1022,29 +1031,31 @@ import java.io.PrintWriter; Binder.restoreCallingIdentity(identity); } - long time = SystemClock.uptimeMillis() + delay; + synchronized (sLastDeviceConnectionMsgTimeLock) { + long time = SystemClock.uptimeMillis() + delay; - switch (msg) { - case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE: - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED: - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED: - case MSG_IL_SET_HEARING_AID_CONNECTION_STATE: - case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE: - case MSG_IL_BTA2DP_DOCK_TIMEOUT: - case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: - case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE: - if (sLastDeviceConnectMsgTime >= time) { - // add a little delay to make sure messages are ordered as expected - time = sLastDeviceConnectMsgTime + 30; - } - sLastDeviceConnectMsgTime = time; - break; - default: - break; - } + switch (msg) { + case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE: + case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED: + case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED: + case MSG_IL_SET_HEARING_AID_CONNECTION_STATE: + case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE: + case MSG_IL_BTA2DP_DOCK_TIMEOUT: + case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: + case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE: + if (sLastDeviceConnectMsgTime >= time) { + // add a little delay to make sure messages are ordered as expected + time = sLastDeviceConnectMsgTime + 30; + } + sLastDeviceConnectMsgTime = time; + break; + default: + break; + } - mBrokerHandler.sendMessageAtTime(mBrokerHandler.obtainMessage(msg, arg1, arg2, obj), - time); + mBrokerHandler.sendMessageAtTime(mBrokerHandler.obtainMessage(msg, arg1, arg2, obj), + time); + } } //------------------------------------------------------------- diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 3933fb2d5f46..90973a888a9d 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -159,6 +159,7 @@ public class AudioDeviceInventory { } // only public for mocking/spying + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") @VisibleForTesting public void onSetA2dpSinkConnectionState(@NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, @AudioService.BtProfileConnectionState int state) { @@ -283,6 +284,7 @@ public class AudioDeviceInventory { } } + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ void onBluetoothA2dpActiveDeviceChange( @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) { final BluetoothDevice btDevice = btInfo.getBtDevice(); @@ -555,6 +557,7 @@ public class AudioDeviceInventory { } // only public for mocking/spying + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") @VisibleForTesting public void setBluetoothA2dpDeviceConnectionState( @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index cc50e37d2b61..0d493b825133 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -470,11 +470,12 @@ public class AudioService extends IAudioService.Stub // List of binder death handlers for setMode() client processes. // The last process to have called setMode() is at the top of the list. - private final ArrayList<SetModeDeathHandler> mSetModeDeathHandlers = + // package-private so it can be accessed in AudioDeviceBroker.getSetModeDeathHandlers + //TODO candidate to be moved to separate class that handles synchronization + @GuardedBy("mDeviceBroker.mSetModeLock") + /*package*/ final ArrayList<SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList<SetModeDeathHandler>(); - private volatile int mCurrentModeOwnerPid = 0; - // true if boot sequence has been completed private boolean mSystemReady; // true if Intent.ACTION_USER_SWITCHED has ever been received @@ -3191,10 +3192,15 @@ public class AudioService extends IAudioService.Stub * @return 0 if nobody owns the mode */ /*package*/ int getModeOwnerPid() { - return mCurrentModeOwnerPid; + int modeOwnerPid = 0; + try { + modeOwnerPid = mSetModeDeathHandlers.get(0).getPid(); + } catch (Exception e) { + // nothing to do, modeOwnerPid is not modified + } + return modeOwnerPid; } - private class SetModeDeathHandler implements IBinder.DeathRecipient { private IBinder mCb; // To be notified of client's death private int mPid; @@ -3208,7 +3214,7 @@ public class AudioService extends IAudioService.Stub public void binderDied() { int oldModeOwnerPid = 0; int newModeOwnerPid = 0; - synchronized (mSetModeDeathHandlers) { + synchronized (mDeviceBroker.mSetModeLock) { Log.w(TAG, "setMode() client died"); if (!mSetModeDeathHandlers.isEmpty()) { oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid(); @@ -3219,15 +3225,11 @@ public class AudioService extends IAudioService.Stub } else { newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid, TAG); } - - if (newModeOwnerPid != oldModeOwnerPid) { - mCurrentModeOwnerPid = newModeOwnerPid; - // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all SCO - // connections not started by the application changing the mode when pid changes - if (newModeOwnerPid != 0) { - mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid); - } - } + } + // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all + // SCO connections not started by the application changing the mode when pid changes + if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) { + mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid); } } @@ -3250,17 +3252,15 @@ public class AudioService extends IAudioService.Stub /** @see AudioManager#setMode(int) */ public void setMode(int mode, IBinder cb, String callingPackage) { - if (DEBUG_MODE) { - Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")"); - } + if (DEBUG_MODE) { Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")"); } if (!checkAudioSettingsPermission("setMode()")) { return; } - if ((mode == AudioSystem.MODE_IN_CALL) - && (mContext.checkCallingOrSelfPermission( + if ( (mode == AudioSystem.MODE_IN_CALL) && + (mContext.checkCallingOrSelfPermission( android.Manifest.permission.MODIFY_PHONE_STATE) - != PackageManager.PERMISSION_GRANTED)) { + != PackageManager.PERMISSION_GRANTED)) { Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); return; @@ -3272,7 +3272,7 @@ public class AudioService extends IAudioService.Stub int oldModeOwnerPid = 0; int newModeOwnerPid = 0; - synchronized (mSetModeDeathHandlers) { + synchronized (mDeviceBroker.mSetModeLock) { if (!mSetModeDeathHandlers.isEmpty()) { oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid(); } @@ -3280,21 +3280,17 @@ public class AudioService extends IAudioService.Stub mode = mMode; } newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid(), callingPackage); - - if (newModeOwnerPid != oldModeOwnerPid) { - mCurrentModeOwnerPid = newModeOwnerPid; - // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all - // SCO connections not started by the application changing the mode when pid changes - if (newModeOwnerPid != 0) { - mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid); - } - } + } + // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all + // SCO connections not started by the application changing the mode when pid changes + if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) { + mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid); } } // setModeInt() returns a valid PID if the audio mode was successfully set to // any mode other than NORMAL. - @GuardedBy("mSetModeDeathHandlers") + @GuardedBy("mDeviceBroker.mSetModeLock") private int setModeInt(int mode, IBinder cb, int pid, String caller) { if (DEBUG_MODE) { Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid + ", caller=" + caller + ")"); } @@ -3633,7 +3629,9 @@ public class AudioService extends IAudioService.Stub !mSystemReady) { return; } - mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource); + synchronized (mDeviceBroker.mSetModeLock) { + mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource); + } } /** @see AudioManager#stopBluetoothSco() */ @@ -3645,7 +3643,9 @@ public class AudioService extends IAudioService.Stub final String eventSource = new StringBuilder("stopBluetoothSco()") .append(") from u/pid:").append(Binder.getCallingUid()).append("/") .append(Binder.getCallingPid()).toString(); - mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource); + synchronized (mDeviceBroker.mSetModeLock) { + mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource); + } } @@ -4406,7 +4406,7 @@ public class AudioService extends IAudioService.Stub // NOTE: Locking order for synchronized objects related to volume or ringer mode management: // 1 mScoclient OR mSafeMediaVolumeState - // 2 mSetModeDeathHandlers + // 2 mSetModeLock // 3 mSettingsLock // 4 VolumeStreamState.class private class VolumeStreamState { diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 625b6b690443..9f1a6bd15ac3 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -171,6 +171,8 @@ public class BtHelper { //---------------------------------------------------------------------- // Interface for AudioDeviceBroker + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void onSystemReady() { mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR; resetBluetoothSco(); @@ -243,6 +245,8 @@ public class BtHelper { return mapBluetoothCodecToAudioFormat(btCodecConfig.getCodecType()); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void receiveBtEvent(Intent intent) { final String action = intent.getAction(); if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) { @@ -329,6 +333,8 @@ public class BtHelper { * * @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept */ + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void disconnectBluetoothSco(int exceptPid) { checkScoAudioState(); if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) { @@ -337,6 +343,8 @@ public class BtHelper { clearAllScoClients(exceptPid, true); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode, @NonNull String eventSource) { ScoClient client = getScoClient(cb, true); @@ -356,6 +364,8 @@ public class BtHelper { Binder.restoreCallingIdentity(ident); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void stopBluetoothScoForClient(IBinder cb, @NonNull String eventSource) { ScoClient client = getScoClient(cb, false); @@ -413,6 +423,8 @@ public class BtHelper { mDeviceBroker.postDisconnectHearingAid(); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void resetBluetoothSco() { clearAllScoClients(0, false); mScoAudioState = SCO_STATE_INACTIVE; @@ -421,6 +433,8 @@ public class BtHelper { mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco"); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void disconnectHeadset() { setBtScoActiveDevice(null); mBluetoothHeadset = null; @@ -466,6 +480,8 @@ public class BtHelper { /*eventSource*/ "mBluetoothProfileServiceListener"); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) { // Discard timeout message mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService(); @@ -552,6 +568,8 @@ public class BtHelper { return result; } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") @GuardedBy("BtHelper.this") private void setBtScoActiveDevice(BluetoothDevice btDevice) { Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice); @@ -634,6 +652,8 @@ public class BtHelper { }; //---------------------------------------------------------------------- + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void scoClientDied(Object obj) { final ScoClient client = (ScoClient) obj; Log.w(TAG, "SCO client died"); @@ -664,6 +684,8 @@ public class BtHelper { mDeviceBroker.postScoClientDied(this); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + // @GuardedBy("AudioDeviceBroker.mDeviceStateLock") @GuardedBy("BtHelper.this") void incCount(int scoAudioMode) { if (!requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode)) { @@ -683,6 +705,8 @@ public class BtHelper { mStartcount++; } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + // @GuardedBy("AudioDeviceBroker.mDeviceStateLock") @GuardedBy("BtHelper.this") void decCount() { if (mStartcount == 0) { @@ -702,6 +726,8 @@ public class BtHelper { } } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + // @GuardedBy("AudioDeviceBroker.mDeviceStateLock") @GuardedBy("BtHelper.this") void clearCount(boolean stopSco) { if (mStartcount != 0) { @@ -738,6 +764,8 @@ public class BtHelper { return count; } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") @GuardedBy("BtHelper.this") private boolean requestScoState(int state, int scoAudioMode) { checkScoAudioState(); @@ -931,6 +959,8 @@ public class BtHelper { return null; } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") @GuardedBy("BtHelper.this") private void clearAllScoClients(int exceptPid, boolean stopSco) { ScoClient savedClient = null; diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java index bc5973d1b270..87624359ef85 100644 --- a/services/core/java/com/android/server/compat/CompatChange.java +++ b/services/core/java/com/android/server/compat/CompatChange.java @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.compat.annotation.EnabledAfter; import android.content.pm.ApplicationInfo; +import com.android.internal.compat.CompatibilityChangeInfo; import com.android.server.compat.config.Change; import java.util.HashMap; @@ -35,12 +36,8 @@ import java.util.Map; * * <p>Note, this class is not thread safe so callers must ensure thread safety. */ -public final class CompatChange { +public final class CompatChange extends CompatibilityChangeInfo { - private final long mChangeId; - @Nullable private final String mName; - private final int mEnableAfterTargetSdk; - private final boolean mDisabled; private Map<String, Boolean> mPackageOverrides; public CompatChange(long changeId) { @@ -56,29 +53,15 @@ public final class CompatChange { */ public CompatChange(long changeId, @Nullable String name, int enableAfterTargetSdk, boolean disabled) { - mChangeId = changeId; - mName = name; - mEnableAfterTargetSdk = enableAfterTargetSdk; - mDisabled = disabled; + super(changeId, name, enableAfterTargetSdk, disabled); } /** * @param change an object generated by services/core/xsd/platform-compat-config.xsd */ public CompatChange(Change change) { - mChangeId = change.getId(); - mName = change.getName(); - mEnableAfterTargetSdk = change.getEnableAfterTargetSdk(); - mDisabled = change.getDisabled(); - } - - long getId() { - return mChangeId; - } - - @Nullable - String getName() { - return mName; + super(change.getId(), change.getName(), change.getEnableAfterTargetSdk(), + change.getDisabled()); } /** @@ -121,11 +104,11 @@ public final class CompatChange { if (mPackageOverrides != null && mPackageOverrides.containsKey(app.packageName)) { return mPackageOverrides.get(app.packageName); } - if (mDisabled) { + if (getDisabled()) { return false; } - if (mEnableAfterTargetSdk != -1) { - return app.targetSdkVersion > mEnableAfterTargetSdk; + if (getEnableAfterTargetSdk() != -1) { + return app.targetSdkVersion > getEnableAfterTargetSdk(); } return true; } @@ -133,14 +116,14 @@ public final class CompatChange { @Override public String toString() { StringBuilder sb = new StringBuilder("ChangeId(") - .append(mChangeId); - if (mName != null) { - sb.append("; name=").append(mName); + .append(getId()); + if (getName() != null) { + sb.append("; name=").append(getName()); } - if (mEnableAfterTargetSdk != -1) { - sb.append("; enableAfterTargetSdk=").append(mEnableAfterTargetSdk); + if (getEnableAfterTargetSdk() != -1) { + sb.append("; enableAfterTargetSdk=").append(getEnableAfterTargetSdk()); } - if (mDisabled) { + if (getDisabled()) { sb.append("; disabled"); } if (mPackageOverrides != null && mPackageOverrides.size() > 0) { diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index 0fabd9aef373..d6ec22b078ea 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -16,6 +16,7 @@ package com.android.server.compat; +import android.compat.Compatibility.ChangeConfig; import android.content.pm.ApplicationInfo; import android.os.Environment; import android.text.TextUtils; @@ -26,6 +27,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.compat.CompatibilityChangeConfig; +import com.android.internal.compat.CompatibilityChangeInfo; import com.android.server.compat.config.Change; import com.android.server.compat.config.XmlParser; @@ -37,6 +39,8 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; +import java.util.HashSet; +import java.util.Set; import javax.xml.datatype.DatatypeConfigurationException; /** @@ -243,6 +247,49 @@ public final class CompatConfig { } } + /** + * Get the config for a given app. + * + * @param applicationInfo the {@link ApplicationInfo} for which the info should be dumped. + * @return A {@link CompatibilityChangeConfig} which contains the compat config info for the + * given app. + */ + + public CompatibilityChangeConfig getAppConfig(ApplicationInfo applicationInfo) { + Set<Long> enabled = new HashSet<>(); + Set<Long> disabled = new HashSet<>(); + synchronized (mChanges) { + for (int i = 0; i < mChanges.size(); ++i) { + CompatChange c = mChanges.valueAt(i); + if (c.isEnabled(applicationInfo)) { + enabled.add(c.getId()); + } else { + disabled.add(c.getId()); + } + } + } + return new CompatibilityChangeConfig(new ChangeConfig(enabled, disabled)); + } + + /** + * Dumps all the compatibility change information. + * + * @return An array of {@link CompatibilityChangeInfo} with the current changes. + */ + public CompatibilityChangeInfo[] dumpChanges() { + synchronized (mChanges) { + CompatibilityChangeInfo[] changeInfos = new CompatibilityChangeInfo[mChanges.size()]; + for (int i = 0; i < mChanges.size(); ++i) { + CompatChange change = mChanges.valueAt(i); + changeInfos[i] = new CompatibilityChangeInfo(change.getId(), + change.getName(), + change.getEnableAfterTargetSdk(), + change.getDisabled()); + } + return changeInfos; + } + } + CompatConfig initConfigFromLib(File libraryDir) { if (!libraryDir.exists() || !libraryDir.isDirectory()) { Slog.e(TAG, "No directory " + libraryDir + ", skipping"); diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 9ac9955493cc..75e2d220898d 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -25,6 +25,7 @@ import android.util.StatsLog; import com.android.internal.compat.ChangeReporter; import com.android.internal.compat.CompatibilityChangeConfig; +import com.android.internal.compat.CompatibilityChangeInfo; import com.android.internal.compat.IPlatformCompat; import com.android.internal.util.DumpUtils; @@ -114,6 +115,16 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override + public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) { + return CompatConfig.get().getAppConfig(appInfo); + } + + @Override + public CompatibilityChangeInfo[] listAllChanges() { + return CompatConfig.get().dumpChanges(); + } + + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return; CompatConfig.get().dumpConfig(pw); diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java index e09c661c627b..ebaa5a1bd475 100644 --- a/services/core/java/com/android/server/content/SyncStorageEngine.java +++ b/services/core/java/com/android/server/content/SyncStorageEngine.java @@ -2032,13 +2032,17 @@ public class SyncStorageEngine { int token; while ((token=in.readInt()) != STATUS_FILE_END) { if (token == STATUS_FILE_ITEM) { - SyncStatusInfo status = new SyncStatusInfo(in); - if (mAuthorities.indexOfKey(status.authorityId) >= 0) { - status.pending = false; - if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) { - Slog.v(TAG_FILE, "Adding status for id " + status.authorityId); + try { + SyncStatusInfo status = new SyncStatusInfo(in); + if (mAuthorities.indexOfKey(status.authorityId) >= 0) { + status.pending = false; + if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) { + Slog.v(TAG_FILE, "Adding status for id " + status.authorityId); + } + mSyncStatus.put(status.authorityId, status); } - mSyncStatus.put(status.authorityId, status); + } catch (Exception e) { + Slog.e(TAG, "Unable to parse some sync status.", e); } } else { // Ooops. 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 0bf43b6d1b9c..2dc2cf0d8e90 100644 --- a/services/core/java/com/android/server/display/color/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java @@ -589,16 +589,18 @@ public final class ColorDisplayService extends SystemService { if (immediate) { dtm.setColorMatrix(tintController.getLevel(), to); } else { - tintController.setAnimator(ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR, - from == null ? MATRIX_IDENTITY : from, to)); - tintController.getAnimator().setDuration(TRANSITION_DURATION); - tintController.getAnimator().setInterpolator(AnimationUtils.loadInterpolator( + TintValueAnimator valueAnimator = TintValueAnimator.ofMatrix(COLOR_MATRIX_EVALUATOR, + from == null ? MATRIX_IDENTITY : from, to); + tintController.setAnimator(valueAnimator); + valueAnimator.setDuration(TRANSITION_DURATION); + valueAnimator.setInterpolator(AnimationUtils.loadInterpolator( getContext(), android.R.interpolator.fast_out_slow_in)); - tintController.getAnimator().addUpdateListener((ValueAnimator animator) -> { + valueAnimator.addUpdateListener((ValueAnimator animator) -> { final float[] value = (float[]) animator.getAnimatedValue(); dtm.setColorMatrix(tintController.getLevel(), value); + ((TintValueAnimator) animator).updateMinMaxComponents(); }); - tintController.getAnimator().addListener(new AnimatorListenerAdapter() { + valueAnimator.addListener(new AnimatorListenerAdapter() { private boolean mIsCancelled; @@ -609,9 +611,14 @@ public final class ColorDisplayService extends SystemService { @Override public void onAnimationEnd(Animator animator) { + TintValueAnimator t = (TintValueAnimator) animator; Slog.d(TAG, tintController.getClass().getSimpleName() + " Animation cancelled: " + mIsCancelled - + " to matrix: " + TintController.matrixToString(to, 16)); + + " to matrix: " + TintController.matrixToString(to, 16) + + " min matrix coefficients: " + + TintController.matrixToString(t.getMin(), 16) + + " max matrix coefficients: " + + TintController.matrixToString(t.getMax(), 16)); if (!mIsCancelled) { // Ensure final color matrix is set at the end of the animation. If the // animation is cancelled then don't set the final color matrix so the new @@ -621,7 +628,7 @@ public final class ColorDisplayService extends SystemService { tintController.setAnimator(null); } }); - tintController.getAnimator().start(); + valueAnimator.start(); } } @@ -1109,6 +1116,51 @@ public final class ColorDisplayService extends SystemService { } /** + * Only animates matrices and saves min and max coefficients for logging. + */ + static class TintValueAnimator extends ValueAnimator { + private float[] min; + private float[] max; + + public static TintValueAnimator ofMatrix(ColorMatrixEvaluator evaluator, + Object... values) { + TintValueAnimator anim = new TintValueAnimator(); + anim.setObjectValues(values); + anim.setEvaluator(evaluator); + if (values == null || values.length == 0) { + return null; + } + float[] m = (float[]) values[0]; + anim.min = new float[m.length]; + anim.max = new float[m.length]; + for (int i = 0; i < m.length; ++i) { + anim.min[i] = Float.MAX_VALUE; + anim.max[i] = Float.MIN_VALUE; + } + return anim; + } + + public void updateMinMaxComponents() { + float[] value = (float[]) getAnimatedValue(); + if (value == null) { + return; + } + for (int i = 0; i < value.length; ++i) { + min[i] = Math.min(min[i], value[i]); + max[i] = Math.max(max[i], value[i]); + } + } + + public float[] getMin() { + return min; + } + + public float[] getMax() { + return max; + } + } + + /** * Interpolates between two 4x4 color transform matrices (in column-major order). */ private static class ColorMatrixEvaluator implements TypeEvaluator<float[]> { diff --git a/services/core/java/com/android/server/display/color/DisplayTransformManager.java b/services/core/java/com/android/server/display/color/DisplayTransformManager.java index d5706a59b987..3b0069cbf001 100644 --- a/services/core/java/com/android/server/display/color/DisplayTransformManager.java +++ b/services/core/java/com/android/server/display/color/DisplayTransformManager.java @@ -111,6 +111,8 @@ public class DisplayTransformManager { @GuardedBy("mDaltonizerModeLock") private int mDaltonizerMode = -1; + private static final IBinder sFlinger = ServiceManager.getService(SURFACE_FLINGER); + /* package */ DisplayTransformManager() { } @@ -195,25 +197,22 @@ public class DisplayTransformManager { * Propagates the provided color transformation matrix to the SurfaceFlinger. */ private static void applyColorMatrix(float[] m) { - final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER); - if (flinger != null) { - final Parcel data = Parcel.obtain(); - data.writeInterfaceToken("android.ui.ISurfaceComposer"); - if (m != null) { - data.writeInt(1); - for (int i = 0; i < 16; i++) { - data.writeFloat(m[i]); - } - } else { - data.writeInt(0); - } - try { - flinger.transact(SURFACE_FLINGER_TRANSACTION_COLOR_MATRIX, data, null, 0); - } catch (RemoteException ex) { - Slog.e(TAG, "Failed to set color transform", ex); - } finally { - data.recycle(); + final Parcel data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + if (m != null) { + data.writeInt(1); + for (int i = 0; i < 16; i++) { + data.writeFloat(m[i]); } + } else { + data.writeInt(0); + } + try { + sFlinger.transact(SURFACE_FLINGER_TRANSACTION_COLOR_MATRIX, data, null, 0); + } catch (RemoteException ex) { + Slog.e(TAG, "Failed to set color transform", ex); + } finally { + data.recycle(); } } @@ -221,18 +220,15 @@ public class DisplayTransformManager { * Propagates the provided Daltonization mode to the SurfaceFlinger. */ private static void applyDaltonizerMode(int mode) { - final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER); - if (flinger != null) { - final Parcel data = Parcel.obtain(); - data.writeInterfaceToken("android.ui.ISurfaceComposer"); - data.writeInt(mode); - try { - flinger.transact(SURFACE_FLINGER_TRANSACTION_DALTONIZER, data, null, 0); - } catch (RemoteException ex) { - Slog.e(TAG, "Failed to set Daltonizer mode", ex); - } finally { - data.recycle(); - } + final Parcel data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + data.writeInt(mode); + try { + sFlinger.transact(SURFACE_FLINGER_TRANSACTION_DALTONIZER, data, null, 0); + } catch (RemoteException ex) { + Slog.e(TAG, "Failed to set Daltonizer mode", ex); + } finally { + data.recycle(); } } @@ -286,20 +282,17 @@ public class DisplayTransformManager { * #SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGED}. */ public boolean isDeviceColorManaged() { - final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER); - if (flinger != null) { - final Parcel data = Parcel.obtain(); - final Parcel reply = Parcel.obtain(); - data.writeInterfaceToken("android.ui.ISurfaceComposer"); - try { - flinger.transact(SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGED, data, reply, 0); - return reply.readBoolean(); - } catch (RemoteException ex) { - Slog.e(TAG, "Failed to query wide color support", ex); - } finally { - data.recycle(); - reply.recycle(); - } + final Parcel data = Parcel.obtain(); + final Parcel reply = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + try { + sFlinger.transact(SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGED, data, reply, 0); + return reply.readBoolean(); + } catch (RemoteException ex) { + Slog.e(TAG, "Failed to query wide color support", ex); + } finally { + data.recycle(); + reply.recycle(); } return false; } @@ -309,18 +302,15 @@ public class DisplayTransformManager { */ private void applySaturation(float saturation) { SystemProperties.set(PERSISTENT_PROPERTY_SATURATION, Float.toString(saturation)); - final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER); - if (flinger != null) { - final Parcel data = Parcel.obtain(); - data.writeInterfaceToken("android.ui.ISurfaceComposer"); - data.writeFloat(saturation); - try { - flinger.transact(SURFACE_FLINGER_TRANSACTION_SATURATION, data, null, 0); - } catch (RemoteException ex) { - Slog.e(TAG, "Failed to set saturation", ex); - } finally { - data.recycle(); - } + final Parcel data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + data.writeFloat(saturation); + try { + sFlinger.transact(SURFACE_FLINGER_TRANSACTION_SATURATION, data, null, 0); + } catch (RemoteException ex) { + Slog.e(TAG, "Failed to set saturation", ex); + } finally { + data.recycle(); } } @@ -334,21 +324,18 @@ public class DisplayTransformManager { Integer.toString(compositionColorMode)); } - final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER); - if (flinger != null) { - final Parcel data = Parcel.obtain(); - data.writeInterfaceToken("android.ui.ISurfaceComposer"); - data.writeInt(color); - if (compositionColorMode != Display.COLOR_MODE_INVALID) { - data.writeInt(compositionColorMode); - } - try { - flinger.transact(SURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR, data, null, 0); - } catch (RemoteException ex) { - Slog.e(TAG, "Failed to set display color", ex); - } finally { - data.recycle(); - } + final Parcel data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + data.writeInt(color); + if (compositionColorMode != Display.COLOR_MODE_INVALID) { + data.writeInt(compositionColorMode); + } + try { + sFlinger.transact(SURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR, data, null, 0); + } catch (RemoteException ex) { + Slog.e(TAG, "Failed to set display color", ex); + } finally { + data.recycle(); } } diff --git a/services/core/java/com/android/server/display/color/TintController.java b/services/core/java/com/android/server/display/color/TintController.java index 8d8b9b2af04e..422dd328d2b6 100644 --- a/services/core/java/com/android/server/display/color/TintController.java +++ b/services/core/java/com/android/server/display/color/TintController.java @@ -24,14 +24,14 @@ import java.io.PrintWriter; abstract class TintController { - private ValueAnimator mAnimator; + private ColorDisplayService.TintValueAnimator mAnimator; private Boolean mIsActivated; - public ValueAnimator getAnimator() { + public ColorDisplayService.TintValueAnimator getAnimator() { return mAnimator; } - public void setAnimator(ValueAnimator animator) { + public void setAnimator(ColorDisplayService.TintValueAnimator animator) { mAnimator = animator; } diff --git a/services/core/java/com/android/server/location/GeocoderProxy.java b/services/core/java/com/android/server/location/GeocoderProxy.java index d9602b897303..e6f0ed9d14b0 100644 --- a/services/core/java/com/android/server/location/GeocoderProxy.java +++ b/services/core/java/com/android/server/location/GeocoderProxy.java @@ -21,7 +21,7 @@ import android.location.Address; import android.location.GeocoderParams; import android.location.IGeocodeProvider; -import com.android.server.FgThread; +import com.android.internal.os.BackgroundThread; import com.android.server.ServiceWatcher; import java.util.List; @@ -53,7 +53,7 @@ public class GeocoderProxy { int initialPackageNamesResId) { mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, overlaySwitchResId, defaultServicePackageNameResId, initialPackageNamesResId, - FgThread.getHandler()); + BackgroundThread.getHandler()); } private boolean bind() { diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 8bf01a366f65..11b9ac44ff7f 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -67,6 +67,7 @@ import android.util.StatsLog; import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.location.GpsNetInitiatedHandler; import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification; @@ -304,6 +305,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private final ExponentialBackOff mPsdsBackOff = new ExponentialBackOff(RETRY_INTERVAL, MAX_RETRY_INTERVAL); + private static boolean sIsInitialized = false; + private static boolean sStaticTestOverride = false; + // True if we are enabled @GuardedBy("mLock") private boolean mGpsEnabled; @@ -576,7 +580,15 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } } + @VisibleForTesting + public static void setIsSupportedForTest(boolean override) { + sStaticTestOverride = override; + } + public static boolean isSupported() { + if (sStaticTestOverride) { + return true; + } return native_is_supported(); } @@ -598,6 +610,13 @@ public class GnssLocationProvider extends AbstractLocationProvider implements Looper looper) { super(context, locationProviderManager); + synchronized (mLock) { + if (!sIsInitialized) { + class_init_native(); + } + sIsInitialized = true; + } + mLooper = looper; // Create a wake lock @@ -2224,10 +2243,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements // preallocated to avoid memory allocation in reportNmea() private byte[] mNmeaBuffer = new byte[120]; - static { - class_init_native(); - } - private static native void class_init_native(); private static native boolean native_is_supported(); diff --git a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java index ec05c310e68d..55e427f70180 100644 --- a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java +++ b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java @@ -47,7 +47,7 @@ public abstract class GnssMeasurementsProvider } @VisibleForTesting - GnssMeasurementsProvider( + public GnssMeasurementsProvider( Context context, Handler handler, GnssMeasurementProviderNative aNative) { super(context, handler, TAG); mNative = aNative; @@ -158,7 +158,7 @@ public abstract class GnssMeasurementsProvider } @VisibleForTesting - static class GnssMeasurementProviderNative { + public static class GnssMeasurementProviderNative { public boolean isMeasurementSupported() { return native_is_measurement_supported(); } diff --git a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java index 4c45ef665789..983d1daa2367 100644 --- a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java +++ b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java @@ -45,7 +45,7 @@ public abstract class GnssNavigationMessageProvider } @VisibleForTesting - GnssNavigationMessageProvider(Context context, Handler handler, + public GnssNavigationMessageProvider(Context context, Handler handler, GnssNavigationMessageProviderNative aNative) { super(context, handler, TAG); mNative = aNative; @@ -142,7 +142,7 @@ public abstract class GnssNavigationMessageProvider } @VisibleForTesting - static class GnssNavigationMessageProviderNative { + public static class GnssNavigationMessageProviderNative { public boolean isNavigationMessageSupported() { return native_is_navigation_message_supported(); } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 9bbe62839db7..d6650017e18d 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -1011,8 +1011,12 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public boolean getSeparateProfileChallengeEnabled(int userId) { checkReadPermission(SEPARATE_PROFILE_CHALLENGE_KEY, userId); + return getSeparateProfileChallengeEnabledInternal(userId); + } + + private boolean getSeparateProfileChallengeEnabledInternal(int userId) { synchronized (mSeparateChallengeLock) { - return getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userId); + return getBooleanUnchecked(SEPARATE_PROFILE_CHALLENGE_KEY, false, userId); } } @@ -1096,6 +1100,10 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public boolean getBoolean(String key, boolean defaultValue, int userId) { checkReadPermission(key, userId); + return getBooleanUnchecked(key, defaultValue, userId); + } + + private boolean getBooleanUnchecked(String key, boolean defaultValue, int userId) { String value = getStringUnchecked(key, null, userId); return TextUtils.isEmpty(value) ? defaultValue : (value.equals("1") || value.equals("true")); @@ -3159,7 +3167,7 @@ public class LockSettingsService extends ILockSettings.Stub { // observe it from the keyguard directly. pw.println("Quality: " + getKeyguardStoredQuality(userId)); pw.println("CredentialType: " + getCredentialTypeInternal(userId)); - pw.println("SeparateChallenge: " + getSeparateProfileChallengeEnabled(userId)); + pw.println("SeparateChallenge: " + getSeparateProfileChallengeEnabledInternal(userId)); pw.println(String.format("Metrics: %s", getUserPasswordMetrics(userId) != null ? "known" : "unknown")); pw.decreaseIndent(); diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java new file mode 100644 index 000000000000..99b1ef4a1d2e --- /dev/null +++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java @@ -0,0 +1,300 @@ +/* + * 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.notification; + +import android.app.NotificationHistory; +import android.app.NotificationHistory.HistoricalNotification; +import android.os.Handler; +import android.util.AtomicFile; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Arrays; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.Iterator; +import java.util.LinkedList; + +/** + * Provides an interface to write and query for notification history data for a user from a Protocol + * Buffer database. + * + * Periodically writes the buffered history to disk but can also accept force writes based on + * outside changes (like a pending shutdown). + */ +public class NotificationHistoryDatabase { + private static final int DEFAULT_CURRENT_VERSION = 1; + + private static final String TAG = "NotiHistoryDatabase"; + private static final boolean DEBUG = NotificationManagerService.DBG; + private static final int HISTORY_RETENTION_DAYS = 2; + private static final long WRITE_BUFFER_INTERVAL_MS = 1000 * 60 * 20; + + private final Object mLock = new Object(); + private Handler mFileWriteHandler; + @VisibleForTesting + // List of files holding history information, sorted newest to oldest + final LinkedList<AtomicFile> mHistoryFiles; + private final GregorianCalendar mCal; + private final File mHistoryDir; + private final File mVersionFile; + // Current version of the database files schema + private int mCurrentVersion; + private final WriteBufferRunnable mWriteBufferRunnable; + + // Object containing posted notifications that have not yet been written to disk + @VisibleForTesting + NotificationHistory mBuffer; + + public NotificationHistoryDatabase(File dir) { + mCurrentVersion = DEFAULT_CURRENT_VERSION; + mVersionFile = new File(dir, "version"); + mHistoryDir = new File(dir, "history"); + mHistoryFiles = new LinkedList<>(); + mCal = new GregorianCalendar(); + mBuffer = new NotificationHistory(); + mWriteBufferRunnable = new WriteBufferRunnable(); + } + + public void init(Handler fileWriteHandler) { + synchronized (mLock) { + mFileWriteHandler = fileWriteHandler; + + try { + mHistoryDir.mkdir(); + mVersionFile.createNewFile(); + } catch (Exception e) { + Slog.e(TAG, "could not create needed files", e); + } + + checkVersionAndBuildLocked(); + indexFilesLocked(); + prune(HISTORY_RETENTION_DAYS, System.currentTimeMillis()); + } + } + + private void indexFilesLocked() { + mHistoryFiles.clear(); + final File[] files = mHistoryDir.listFiles(); + if (files == null) { + return; + } + + // Sort with newest files first + Arrays.sort(files, (lhs, rhs) -> Long.compare(rhs.lastModified(), lhs.lastModified())); + + for (File file : files) { + mHistoryFiles.addLast(new AtomicFile(file)); + } + } + + private void checkVersionAndBuildLocked() { + int version; + try (BufferedReader reader = new BufferedReader(new FileReader(mVersionFile))) { + version = Integer.parseInt(reader.readLine()); + } catch (NumberFormatException | IOException e) { + version = 0; + } + + if (version != mCurrentVersion && mVersionFile.exists()) { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(mVersionFile))) { + writer.write(Integer.toString(mCurrentVersion)); + writer.write("\n"); + writer.flush(); + } catch (IOException e) { + Slog.e(TAG, "Failed to write new version"); + throw new RuntimeException(e); + } + } + } + + void forceWriteToDisk() { + if (!mFileWriteHandler.hasCallbacks(mWriteBufferRunnable)) { + mFileWriteHandler.post(mWriteBufferRunnable); + } + } + + void onPackageRemoved(String packageName) { + RemovePackageRunnable rpr = new RemovePackageRunnable(packageName); + mFileWriteHandler.post(rpr); + } + + public void addNotification(final HistoricalNotification notification) { + synchronized (mLock) { + mBuffer.addNotificationToWrite(notification); + // Each time we have new history to write to disk, schedule a write in [interval] ms + if (mBuffer.getHistoryCount() == 1) { + mFileWriteHandler.postDelayed(mWriteBufferRunnable, WRITE_BUFFER_INTERVAL_MS); + } + } + } + + public NotificationHistory readNotificationHistory() { + synchronized (mLock) { + NotificationHistory notifications = new NotificationHistory(); + + for (AtomicFile file : mHistoryFiles) { + try { + readLocked( + file, notifications, new NotificationHistoryFilter.Builder().build()); + } catch (Exception e) { + Slog.e(TAG, "error reading " + file.getBaseFile().getName(), e); + } + } + + return notifications; + } + } + + public NotificationHistory readNotificationHistory(String packageName, String channelId, + int maxNotifications) { + synchronized (mLock) { + NotificationHistory notifications = new NotificationHistory(); + + for (AtomicFile file : mHistoryFiles) { + try { + readLocked(file, notifications, + new NotificationHistoryFilter.Builder() + .setPackage(packageName) + .setChannel(packageName, channelId) + .setMaxNotifications(maxNotifications) + .build()); + if (maxNotifications == notifications.getHistoryCount()) { + // No need to read any more files + break; + } + } catch (Exception e) { + Slog.e(TAG, "error reading " + file.getBaseFile().getName(), e); + } + } + + return notifications; + } + } + + /** + * Remove any files that are too old. + */ + public void prune(final int retentionDays, final long currentTimeMillis) { + synchronized (mLock) { + mCal.setTimeInMillis(currentTimeMillis); + mCal.add(Calendar.DATE, -1 * retentionDays); + + while (!mHistoryFiles.isEmpty()) { + final AtomicFile currentOldestFile = mHistoryFiles.getLast(); + final long age = currentTimeMillis + - currentOldestFile.getBaseFile().lastModified(); + if (age > mCal.getTimeInMillis()) { + if (DEBUG) { + Slog.d(TAG, "Removed " + currentOldestFile.getBaseFile().getName()); + } + currentOldestFile.delete(); + mHistoryFiles.removeLast(); + } else { + // all remaining files are newer than the cut off + return; + } + } + } + } + + private void writeLocked(AtomicFile file, NotificationHistory notifications) + throws IOException { + FileOutputStream fos = file.startWrite(); + try { + NotificationHistoryProtoHelper.write(fos, notifications, mCurrentVersion); + file.finishWrite(fos); + fos = null; + } finally { + // When fos is null (successful write), this will no-op + file.failWrite(fos); + } + } + + private static void readLocked(AtomicFile file, NotificationHistory notificationsOut, + NotificationHistoryFilter filter) throws IOException { + try (FileInputStream in = file.openRead()) { + NotificationHistoryProtoHelper.read(in, notificationsOut, filter); + } catch (FileNotFoundException e) { + Slog.e(TAG, "Cannot file " + file.getBaseFile().getName(), e); + throw e; + } + } + + private final class WriteBufferRunnable implements Runnable { + @Override + public void run() { + if (DEBUG) Slog.d(TAG, "WriteBufferRunnable"); + synchronized (mLock) { + final AtomicFile latestNotificationsFiles = new AtomicFile( + new File(mHistoryDir, String.valueOf(System.currentTimeMillis()))); + try { + writeLocked(latestNotificationsFiles, mBuffer); + mHistoryFiles.addFirst(latestNotificationsFiles); + mBuffer = new NotificationHistory(); + } catch (IOException e) { + Slog.e(TAG, "Failed to write buffer to disk. not flushing buffer", e); + } + } + } + } + + private final class RemovePackageRunnable implements Runnable { + private String mPkg; + + public RemovePackageRunnable(String pkg) { + mPkg = pkg; + } + + @Override + public void run() { + if (DEBUG) Slog.d(TAG, "RemovePackageRunnable"); + synchronized (mLock) { + // Remove packageName entries from pending history + mBuffer.removeNotificationsFromWrite(mPkg); + + // Remove packageName entries from files on disk, and rewrite them to disk + // Since we sort by modified date, we have to update the files oldest to newest to + // maintain the original ordering + Iterator<AtomicFile> historyFileItr = mHistoryFiles.descendingIterator(); + while (historyFileItr.hasNext()) { + final AtomicFile af = historyFileItr.next(); + try { + final NotificationHistory notifications = new NotificationHistory(); + readLocked(af, notifications, + new NotificationHistoryFilter.Builder().build()); + notifications.removeNotificationsFromWrite(mPkg); + writeLocked(af, notifications); + } catch (Exception e) { + Slog.e(TAG, "Cannot clean up file on pkg removal " + + af.getBaseFile().getName(), e); + } + } + } + } + } +} diff --git a/services/core/java/com/android/server/notification/NotificationHistoryFilter.java b/services/core/java/com/android/server/notification/NotificationHistoryFilter.java new file mode 100644 index 000000000000..c3b2e73b5354 --- /dev/null +++ b/services/core/java/com/android/server/notification/NotificationHistoryFilter.java @@ -0,0 +1,125 @@ +/* + * 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.notification; + +import android.annotation.NonNull; +import android.app.NotificationHistory; +import android.app.NotificationHistory.HistoricalNotification; +import android.text.TextUtils; + +import com.android.internal.util.Preconditions; + +public final class NotificationHistoryFilter { + private String mPackage; + private String mChannel; + private int mNotificationCount; + + private NotificationHistoryFilter() {} + + public String getPackage() { + return mPackage; + } + + public String getChannel() { + return mChannel; + } + + public int getMaxNotifications() { + return mNotificationCount; + } + + /** + * Returns whether any of the filtering conditions are set + */ + public boolean isFiltering() { + return getPackage() != null || getChannel() != null + || mNotificationCount < Integer.MAX_VALUE; + } + + /** + * Returns true if this notification passes the package and channel name filter, false + * otherwise. + */ + public boolean matchesPackageAndChannelFilter(HistoricalNotification notification) { + if (!TextUtils.isEmpty(getPackage())) { + if (!getPackage().equals(notification.getPackage())) { + return false; + } else { + if (!TextUtils.isEmpty(getChannel()) + && !getChannel().equals(notification.getChannelId())) { + return false; + } + } + } + + return true; + } + + /** + * Returns true if the NotificationHistory can accept another notification. + */ + public boolean matchesCountFilter(NotificationHistory notifications) { + return notifications.getHistoryCount() < mNotificationCount; + } + + public static final class Builder { + private String mPackage = null; + private String mChannel = null; + private int mNotificationCount = Integer.MAX_VALUE; + + /** + * Constructor + */ + public Builder() {} + + /** + * Sets a package name filter + */ + public Builder setPackage(String aPackage) { + mPackage = aPackage; + return this; + } + + /** + * Sets a channel name filter. Only valid if there is also a package name filter + */ + public Builder setChannel(String pkg, String channel) { + setPackage(pkg); + mChannel = channel; + return this; + } + + /** + * Sets the max historical notifications + */ + public Builder setMaxNotifications(int notificationCount) { + mNotificationCount = notificationCount; + return this; + } + + /** + * Makes a NotificationHistoryFilter + */ + public NotificationHistoryFilter build() { + NotificationHistoryFilter filter = new NotificationHistoryFilter(); + filter.mPackage = mPackage; + filter.mChannel = mChannel; + filter.mNotificationCount = mNotificationCount; + return filter; + } + } +} diff --git a/services/core/java/com/android/server/notification/NotificationHistoryProtoHelper.java b/services/core/java/com/android/server/notification/NotificationHistoryProtoHelper.java new file mode 100644 index 000000000000..2831d37ed70b --- /dev/null +++ b/services/core/java/com/android/server/notification/NotificationHistoryProtoHelper.java @@ -0,0 +1,321 @@ +/* + * 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.notification; + +import android.app.NotificationHistory; +import android.app.NotificationHistory.HistoricalNotification; +import android.content.res.Resources; +import android.graphics.drawable.Icon; +import android.util.Slog; +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoOutputStream; + +import com.android.server.notification.NotificationHistoryProto.Notification; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Notification history reader/writer for Protocol Buffer format + */ +final class NotificationHistoryProtoHelper { + private static final String TAG = "NotifHistoryProto"; + + // Static-only utility class. + private NotificationHistoryProtoHelper() {} + + private static List<String> readStringPool(ProtoInputStream proto) throws IOException { + final long token = proto.start(NotificationHistoryProto.STRING_POOL); + List<String> stringPool; + if (proto.nextField(NotificationHistoryProto.StringPool.SIZE)) { + stringPool = new ArrayList(proto.readInt(NotificationHistoryProto.StringPool.SIZE)); + } else { + stringPool = new ArrayList(); + } + while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (proto.getFieldNumber()) { + case (int) NotificationHistoryProto.StringPool.STRINGS: + stringPool.add(proto.readString(NotificationHistoryProto.StringPool.STRINGS)); + break; + } + } + proto.end(token); + return stringPool; + } + + private static void writeStringPool(ProtoOutputStream proto, + final NotificationHistory notifications) { + final long token = proto.start(NotificationHistoryProto.STRING_POOL); + final String[] pooledStrings = notifications.getPooledStringsToWrite(); + proto.write(NotificationHistoryProto.StringPool.SIZE, pooledStrings.length); + for (int i = 0; i < pooledStrings.length; i++) { + proto.write(NotificationHistoryProto.StringPool.STRINGS, pooledStrings[i]); + } + proto.end(token); + } + + private static void readNotification(ProtoInputStream proto, List<String> stringPool, + NotificationHistory notifications, NotificationHistoryFilter filter) + throws IOException { + final long token = proto.start(NotificationHistoryProto.NOTIFICATION); + try { + HistoricalNotification notification = readNotification(proto, stringPool); + if (filter.matchesPackageAndChannelFilter(notification) + && filter.matchesCountFilter(notifications)) { + notifications.addNotificationToWrite(notification); + } + } catch (Exception e) { + Slog.e(TAG, "Error reading notification", e); + } finally { + proto.end(token); + } + } + + private static HistoricalNotification readNotification(ProtoInputStream parser, + List<String> stringPool) throws IOException { + final HistoricalNotification.Builder notification = new HistoricalNotification.Builder(); + String pkg = null; + while (true) { + switch (parser.nextField()) { + case (int) NotificationHistoryProto.Notification.PACKAGE: + pkg = parser.readString(Notification.PACKAGE); + notification.setPackage(pkg); + stringPool.add(pkg); + break; + case (int) Notification.PACKAGE_INDEX: + pkg = stringPool.get(parser.readInt(Notification.PACKAGE_INDEX) - 1); + notification.setPackage(pkg); + break; + case (int) Notification.CHANNEL_NAME: + String channelName = parser.readString(Notification.CHANNEL_NAME); + notification.setChannelName(channelName); + stringPool.add(channelName); + break; + case (int) Notification.CHANNEL_NAME_INDEX: + notification.setChannelName(stringPool.get(parser.readInt( + Notification.CHANNEL_NAME_INDEX) - 1)); + break; + case (int) Notification.CHANNEL_ID: + String channelId = parser.readString(Notification.CHANNEL_ID); + notification.setChannelId(channelId); + stringPool.add(channelId); + break; + case (int) Notification.CHANNEL_ID_INDEX: + notification.setChannelId(stringPool.get(parser.readInt( + Notification.CHANNEL_ID_INDEX) - 1)); + break; + case (int) Notification.UID: + notification.setUid(parser.readInt(Notification.UID)); + break; + case (int) Notification.USER_ID: + notification.setUserId(parser.readInt(Notification.USER_ID)); + break; + case (int) Notification.POSTED_TIME_MS: + notification.setPostedTimeMs(parser.readLong(Notification.POSTED_TIME_MS)); + break; + case (int) Notification.TITLE: + notification.setTitle(parser.readString(Notification.TITLE)); + break; + case (int) Notification.TEXT: + notification.setText(parser.readString(Notification.TEXT)); + break; + case (int) Notification.ICON: + final long iconToken = parser.start(Notification.ICON); + loadIcon(parser, notification, pkg); + parser.end(iconToken); + break; + case ProtoInputStream.NO_MORE_FIELDS: + return notification.build(); + } + } + } + + private static void loadIcon(ProtoInputStream parser, + HistoricalNotification.Builder notification, String pkg) throws IOException { + int iconType = Notification.TYPE_UNKNOWN; + String imageBitmapFileName = null; + int imageResourceId = Resources.ID_NULL; + String imageResourceIdPackage = null; + byte[] imageByteData = null; + int imageByteDataLength = 0; + int imageByteDataOffset = 0; + String imageUri = null; + + while (true) { + switch (parser.nextField()) { + case (int) Notification.Icon.IMAGE_TYPE: + iconType = parser.readInt(Notification.Icon.IMAGE_TYPE); + break; + case (int) Notification.Icon.IMAGE_DATA: + imageByteData = parser.readBytes(Notification.Icon.IMAGE_DATA); + break; + case (int) Notification.Icon.IMAGE_DATA_LENGTH: + imageByteDataLength = parser.readInt(Notification.Icon.IMAGE_DATA_LENGTH); + break; + case (int) Notification.Icon.IMAGE_DATA_OFFSET: + imageByteDataOffset = parser.readInt(Notification.Icon.IMAGE_DATA_OFFSET); + break; + case (int) Notification.Icon.IMAGE_BITMAP_FILENAME: + imageBitmapFileName = parser.readString( + Notification.Icon.IMAGE_BITMAP_FILENAME); + break; + case (int) Notification.Icon.IMAGE_RESOURCE_ID: + imageResourceId = parser.readInt(Notification.Icon.IMAGE_RESOURCE_ID); + break; + case (int) Notification.Icon.IMAGE_RESOURCE_ID_PACKAGE: + imageResourceIdPackage = parser.readString( + Notification.Icon.IMAGE_RESOURCE_ID_PACKAGE); + break; + case (int) Notification.Icon.IMAGE_URI: + imageUri = parser.readString(Notification.Icon.IMAGE_URI); + break; + case ProtoInputStream.NO_MORE_FIELDS: + if (iconType == Icon.TYPE_DATA) { + + if (imageByteData != null) { + notification.setIcon(Icon.createWithData( + imageByteData, imageByteDataOffset, imageByteDataLength)); + } + } else if (iconType == Icon.TYPE_RESOURCE) { + if (imageResourceId != Resources.ID_NULL) { + notification.setIcon(Icon.createWithResource( + imageResourceIdPackage != null + ? imageResourceIdPackage + : pkg, + imageResourceId)); + } + } else if (iconType == Icon.TYPE_URI) { + if (imageUri != null) { + notification.setIcon(Icon.createWithContentUri(imageUri)); + } + } else if (iconType == Icon.TYPE_BITMAP) { + // TODO: read file from disk + } + return; + } + } + } + + private static void writeIcon(ProtoOutputStream proto, HistoricalNotification notification) { + final long token = proto.start(Notification.ICON); + + proto.write(Notification.Icon.IMAGE_TYPE, notification.getIcon().getType()); + switch (notification.getIcon().getType()) { + case Icon.TYPE_DATA: + proto.write(Notification.Icon.IMAGE_DATA, notification.getIcon().getDataBytes()); + proto.write(Notification.Icon.IMAGE_DATA_LENGTH, + notification.getIcon().getDataLength()); + proto.write(Notification.Icon.IMAGE_DATA_OFFSET, + notification.getIcon().getDataOffset()); + break; + case Icon.TYPE_RESOURCE: + proto.write(Notification.Icon.IMAGE_RESOURCE_ID, notification.getIcon().getResId()); + if (!notification.getPackage().equals(notification.getIcon().getResPackage())) { + proto.write(Notification.Icon.IMAGE_RESOURCE_ID_PACKAGE, + notification.getIcon().getResPackage()); + } + break; + case Icon.TYPE_URI: + proto.write(Notification.Icon.IMAGE_URI, notification.getIcon().getUriString()); + break; + case Icon.TYPE_BITMAP: + // TODO: write file to disk + break; + } + + proto.end(token); + } + + private static void writeNotification(ProtoOutputStream proto, + final String[] stringPool, final HistoricalNotification notification) { + final long token = proto.start(NotificationHistoryProto.NOTIFICATION); + final int packageIndex = Arrays.binarySearch(stringPool, notification.getPackage()); + if (packageIndex >= 0) { + proto.write(Notification.PACKAGE_INDEX, packageIndex + 1); + } else { + // Package not in Stringpool for some reason, write full string instead + Slog.w(TAG, "notification package name (" + notification.getPackage() + + ") not found in string cache"); + proto.write(Notification.PACKAGE, notification.getPackage()); + } + final int channelNameIndex = Arrays.binarySearch(stringPool, notification.getChannelName()); + if (channelNameIndex >= 0) { + proto.write(Notification.CHANNEL_NAME_INDEX, channelNameIndex + 1); + } else { + Slog.w(TAG, "notification channel name (" + notification.getChannelName() + + ") not found in string cache"); + proto.write(Notification.CHANNEL_NAME, notification.getChannelName()); + } + final int channelIdIndex = Arrays.binarySearch(stringPool, notification.getChannelId()); + if (channelIdIndex >= 0) { + proto.write(Notification.CHANNEL_ID_INDEX, channelIdIndex + 1); + } else { + Slog.w(TAG, "notification channel id (" + notification.getChannelId() + + ") not found in string cache"); + proto.write(Notification.CHANNEL_ID, notification.getChannelId()); + } + proto.write(Notification.UID, notification.getUid()); + proto.write(Notification.USER_ID, notification.getUserId()); + proto.write(Notification.POSTED_TIME_MS, notification.getPostedTimeMs()); + proto.write(Notification.TITLE, notification.getTitle()); + proto.write(Notification.TEXT, notification.getText()); + writeIcon(proto, notification); + proto.end(token); + } + + public static void read(InputStream in, NotificationHistory notifications, + NotificationHistoryFilter filter) throws IOException { + final ProtoInputStream proto = new ProtoInputStream(in); + List<String> stringPool = new ArrayList<>(); + while (true) { + switch (proto.nextField()) { + case (int) NotificationHistoryProto.STRING_POOL: + stringPool = readStringPool(proto); + break; + case (int) NotificationHistoryProto.NOTIFICATION: + readNotification(proto, stringPool, notifications, filter); + break; + case ProtoInputStream.NO_MORE_FIELDS: + if (filter.isFiltering()) { + notifications.poolStringsFromNotifications(); + } else { + notifications.addPooledStrings(stringPool); + } + return; + } + } + } + + public static void write(OutputStream out, NotificationHistory notifications, int version) { + final ProtoOutputStream proto = new ProtoOutputStream(out); + proto.write(NotificationHistoryProto.MAJOR_VERSION, version); + // String pool should be written before the history itself + writeStringPool(proto, notifications); + + List<HistoricalNotification> notificationsToWrite = notifications.getNotificationsToWrite(); + final int count = notificationsToWrite.size(); + for (int i = 0; i < count; i++) { + writeNotification(proto, notifications.getPooledStringsToWrite(), + notificationsToWrite.get(i)); + } + + proto.flush(); + } +} diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index c8179a767d23..dc0cd184c188 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -41,7 +41,6 @@ import android.util.SparseArray; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.server.FgThread; -import com.android.server.compat.CompatConfig; import java.io.PrintWriter; import java.util.ArrayList; @@ -131,11 +130,11 @@ public class AppsFilter { private static class FeatureConfigImpl implements FeatureConfig { private static final String FILTERING_ENABLED_NAME = "package_query_filtering_enabled"; + private final PackageManagerService.Injector mInjector; private volatile boolean mFeatureEnabled = false; - private CompatConfig mCompatibility; private FeatureConfigImpl(PackageManagerService.Injector injector) { - mCompatibility = injector.getCompatibility(); + mInjector = injector; } @Override @@ -158,7 +157,7 @@ public class AppsFilter { @Override public boolean packageIsEnabled(PackageParser.Package pkg) { - return mCompatibility.isChangeEnabled( + return mInjector.getCompatibility().isChangeEnabled( PackageManager.FILTER_APPLICATION_QUERY, pkg.applicationInfo); } } @@ -263,10 +262,10 @@ public class AppsFilter { * Grants access based on an interaction between a calling and target package, granting * visibility of the caller from the target. * - * @param callingPackage the package initiating the interaction - * @param targetPackage the package being interacted with and thus gaining visibility of the - * initiating package. - * @param userId the user in which this interaction was taking place + * @param callingPackage the package initiating the interaction + * @param targetPackage the package being interacted with and thus gaining visibility of the + * initiating package. + * @param userId the user in which this interaction was taking place */ public void grantImplicitAccess( String callingPackage, String targetPackage, int userId) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 74a85d58c016..b36958a69162 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -299,7 +299,7 @@ import com.android.server.ServiceThread; import com.android.server.SystemConfig; import com.android.server.SystemServerInitThreadPool; import com.android.server.Watchdog; -import com.android.server.compat.CompatConfig; +import com.android.server.compat.PlatformCompat; import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.Settings.DatabaseVersion; @@ -837,7 +837,7 @@ public class PackageManagerService extends IPackageManager.Stub private final Singleton<StorageManager> mStorageManagerProducer; private final Singleton<AppOpsManager> mAppOpsManagerProducer; private final Singleton<AppsFilter> mAppsFilterProducer; - private final Singleton<CompatConfig> mPlatformCompatProducer; + private final Singleton<PlatformCompat> mPlatformCompatProducer; Injector(Context context, Object lock, Installer installer, Object installLock, PackageAbiHelper abiHelper, @@ -855,7 +855,7 @@ public class PackageManagerService extends IPackageManager.Stub Producer<StorageManager> storageManagerProducer, Producer<AppOpsManager> appOpsManagerProducer, Producer<AppsFilter> appsFilterProducer, - Producer<CompatConfig> platformCompatProducer) { + Producer<PlatformCompat> platformCompatProducer) { mContext = context; mLock = lock; mInstaller = installer; @@ -966,7 +966,7 @@ public class PackageManagerService extends IPackageManager.Stub return mAppsFilterProducer.get(this, mPackageManager); } - public CompatConfig getCompatibility() { + public PlatformCompat getCompatibility() { return mPlatformCompatProducer.get(this, mPackageManager); } } @@ -2356,7 +2356,7 @@ public class PackageManagerService extends IPackageManager.Stub new Injector.SystemServiceProducer<>(StorageManager.class), new Injector.SystemServiceProducer<>(AppOpsManager.class), (i, pm) -> AppsFilter.create(i), - (i, pm) -> CompatConfig.get()); + (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat")); PackageManagerService m = new PackageManagerService(injector, factoryTest, onlyCore); t.traceEnd(); // "create package manager" diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 4d2512c26422..5c9b9c93e4fe 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -871,7 +871,7 @@ public class UserManagerService extends IUserManager.Stub { "target should only be specified when we are disabling quiet mode."); } - ensureCanModifyQuietMode(callingPackage, Binder.getCallingUid(), target != null); + ensureCanModifyQuietMode(callingPackage, Binder.getCallingUid(), userId, target != null); final long identity = Binder.clearCallingIdentity(); try { boolean result = false; @@ -903,13 +903,15 @@ public class UserManagerService extends IUserManager.Stub { * <li>Has system UID or root UID</li> * <li>Has {@link Manifest.permission#MODIFY_QUIET_MODE}</li> * <li>Has {@link Manifest.permission#MANAGE_USERS}</li> + * <li>Is the foreground default launcher app</li> * </ul> * <p> - * If caller wants to start an intent after disabling the quiet mode, it must has + * If caller wants to start an intent after disabling the quiet mode, or if it is targeting a + * user in a different profile group from the caller, it must have * {@link Manifest.permission#MANAGE_USERS}. */ private void ensureCanModifyQuietMode(String callingPackage, int callingUid, - boolean startIntent) { + @UserIdInt int targetUserId, boolean startIntent) { if (hasManageUsersPermission()) { return; } @@ -917,6 +919,10 @@ public class UserManagerService extends IUserManager.Stub { throw new SecurityException("MANAGE_USERS permission is required to start intent " + "after disabling quiet mode."); } + if (!isSameProfileGroupNoChecks(UserHandle.getUserId(callingUid), targetUserId)) { + throw new SecurityException("MANAGE_USERS permission is required to modify quiet mode " + + "for a different profile group."); + } final boolean hasModifyQuietModePermission = hasPermissionGranted( Manifest.permission.MODIFY_QUIET_MODE, callingUid); if (hasModifyQuietModePermission) { @@ -1587,6 +1593,7 @@ public class UserManagerService extends IUserManager.Stub { /** @return a specific user restriction that's in effect currently. */ @Override public boolean hasUserRestriction(String restrictionKey, @UserIdInt int userId) { + checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "hasUserRestriction"); return mLocalService.hasUserRestriction(restrictionKey, userId); } @@ -1711,6 +1718,7 @@ public class UserManagerService extends IUserManager.Stub { */ @Override public Bundle getUserRestrictions(@UserIdInt int userId) { + checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserRestrictions"); return UserRestrictionsUtils.clone(getEffectiveUserRestrictions(userId)); } @@ -3994,9 +4002,14 @@ public class UserManagerService extends IUserManager.Stub { long now = System.currentTimeMillis(); final long nowRealtime = SystemClock.elapsedRealtime(); - final int currentUser = LocalServices.getService(ActivityManagerInternal.class) - .getCurrentUserId(); - pw.print("Current user: "); pw.println(currentUser); + final ActivityManagerInternal amInternal = LocalServices + .getService(ActivityManagerInternal.class); + pw.print("Current user: "); + if (amInternal != null) { + pw.println(amInternal.getCurrentUserId()); + } else { + pw.println("N/A"); + } StringBuilder sb = new StringBuilder(); synchronized (mPackagesLock) { diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java index 5493afd1b123..403d3429a186 100644 --- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java +++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java @@ -30,6 +30,7 @@ import android.os.UserHandle; import android.service.textclassifier.ITextClassifierCallback; import android.service.textclassifier.ITextClassifierService; import android.service.textclassifier.TextClassifierService; +import android.service.textclassifier.TextClassifierService.ConnectionState; import android.util.ArrayMap; import android.util.Slog; import android.util.SparseArray; @@ -130,6 +131,10 @@ public final class TextClassificationManagerService extends ITextClassifierServi } @Override + public void onConnectedStateChanged(@ConnectionState int connected) { + } + + @Override public void onSuggestSelection( @Nullable TextClassificationSessionId sessionId, TextSelection.Request request, ITextClassifierCallback callback) @@ -579,6 +584,11 @@ public final class TextClassificationManagerService extends ITextClassifierServi @Override public void onServiceConnected(ComponentName name, IBinder service) { init(ITextClassifierService.Stub.asInterface(service)); + try { + mService.onConnectedStateChanged(TextClassifierService.CONNECTED); + } catch (RemoteException e) { + Slog.e(LOG_TAG, "error in onConnectedStateChanged"); + } } @Override diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 9d41d97bc1ed..f9b6bbaf54e9 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -403,8 +403,7 @@ class ActivityMetricsLogger { if (launchedActivity != null && launchedActivity.mDrawn) { // Launched activity is already visible. We cannot measure windows drawn delay. - reset(true /* abort */, info, "launched activity already visible", - 0L /* timestampNs */); + abort(info, "launched activity already visible"); return; } @@ -422,8 +421,7 @@ class ActivityMetricsLogger { if ((!isLoggableResultCode(resultCode) || launchedActivity == null || !processSwitch || windowingMode == WINDOWING_MODE_UNDEFINED) && !otherWindowModesLaunching) { // Failed to launch or it was not a process switch, so we don't care about the timing. - reset(true /* abort */, info, "failed to launch or not a process switch", - 0L /* timestampNs */); + abort(info, "failed to launch or not a process switch"); return; } else if (otherWindowModesLaunching) { // Don't log this windowing mode but continue with the other windowing modes. @@ -469,8 +467,7 @@ class ActivityMetricsLogger { final WindowingModeTransitionInfoSnapshot infoSnapshot = new WindowingModeTransitionInfoSnapshot(info); if (allWindowsDrawn() && mLoggedTransitionStarting) { - reset(false /* abort */, info, "notifyWindowsDrawn - all windows drawn", - timestampNs /* timestampNs */); + reset(false /* abort */, info, "notifyWindowsDrawn - all windows drawn", timestampNs); } return infoSnapshot; } @@ -478,13 +475,13 @@ class ActivityMetricsLogger { /** * Notifies the tracker that the starting window was drawn. */ - void notifyStartingWindowDrawn(@WindowingMode int windowingMode, long timestamp) { + void notifyStartingWindowDrawn(@WindowingMode int windowingMode, long timestampNs) { final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode); if (info == null || info.loggedStartingWindowDrawn) { return; } info.loggedStartingWindowDrawn = true; - info.startingWindowDelayMs = calculateDelay(timestamp); + info.startingWindowDelayMs = calculateDelay(timestampNs); } /** @@ -544,10 +541,11 @@ class ActivityMetricsLogger { mHandler.obtainMessage(MSG_CHECK_VISIBILITY, args).sendToTarget(); } - private boolean hasVisibleNonFinishingActivity(TaskRecord t) { + /** @return {@code true} if the given task has an activity will be drawn. */ + private static boolean hasActivityToBeDrawn(TaskRecord t) { for (int i = t.getChildCount() - 1; i >= 0; --i) { final ActivityRecord r = t.getChildAt(i); - if (r.visible && !r.finishing) { + if (r.visible && !r.mDrawn && !r.finishing) { return true; } } @@ -574,19 +572,20 @@ class ActivityMetricsLogger { return; } - // Check if there is any activity in the task that is visible and not finishing. If the - // launched activity finished before it is drawn and if there is another activity in - // the task then that activity will be draw on screen. - if (hasVisibleNonFinishingActivity(t)) { + // If the task of the launched activity contains any activity to be drawn, then the + // window drawn event should report later to complete the transition. Otherwise all + // activities in this task may be finished, invisible or drawn, so the transition event + // should be cancelled. + if (hasActivityToBeDrawn(t)) { return; } if (DEBUG_METRICS) Slog.i(TAG, "notifyVisibilityChanged to invisible activity=" + r); logAppTransitionCancel(info); - mWindowingModeTransitionInfo.remove(r.getWindowingMode()); - if (mWindowingModeTransitionInfo.size() == 0) { - reset(true /* abort */, info, "notifyVisibilityChanged to invisible", - 0L /* timestampNs */); + // Abort if this is the only one active transition. + if (mWindowingModeTransitionInfo.size() == 1 + && mWindowingModeTransitionInfo.get(r.getWindowingMode()) != null) { + abort(info, "notifyVisibilityChanged to invisible"); } } } @@ -622,19 +621,25 @@ class ActivityMetricsLogger { && mWindowingModeTransitionInfo.size() > 0; } + /** Aborts tracking of current launch metrics. */ + private void abort(WindowingModeTransitionInfo info, String cause) { + reset(true /* abort */, info, cause, 0L /* timestampNs */); + } + private void reset(boolean abort, WindowingModeTransitionInfo info, String cause, - long timestampNs) { + long timestampNs) { + final boolean isAnyTransitionActive = isAnyTransitionActive(); if (DEBUG_METRICS) { - Slog.i(TAG, - "reset abort=" + abort + ",cause=" + cause + ",timestamp=" + timestampNs); + Slog.i(TAG, "reset abort=" + abort + " cause=" + cause + " timestamp=" + timestampNs + + " active=" + isAnyTransitionActive); } - if (!abort && isAnyTransitionActive()) { + if (!abort && isAnyTransitionActive) { logAppTransitionMultiEvents(); } stopLaunchTrace(info); // Ignore reset-after reset. - if (isAnyTransitionActive()) { + if (isAnyTransitionActive) { // LaunchObserver callbacks. if (abort) { launchObserverNotifyActivityLaunchCancelled(info); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index eeee062769d0..eaf19be2338a 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -179,7 +179,7 @@ import static com.android.server.wm.AppWindowTokenProto.FILLS_PARENT; import static com.android.server.wm.AppWindowTokenProto.FROZEN_BOUNDS; import static com.android.server.wm.AppWindowTokenProto.HIDDEN_REQUESTED; import static com.android.server.wm.AppWindowTokenProto.HIDDEN_SET_FROM_TRANSFERRED_STARTING_WINDOW; -import static com.android.server.wm.AppWindowTokenProto.IS_REALLY_ANIMATING; +import static com.android.server.wm.AppWindowTokenProto.IS_ANIMATING; import static com.android.server.wm.AppWindowTokenProto.IS_WAITING_FOR_TRANSITION_START; import static com.android.server.wm.AppWindowTokenProto.LAST_ALL_DRAWN; import static com.android.server.wm.AppWindowTokenProto.LAST_SURFACE_SHOWING; @@ -205,16 +205,17 @@ import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW; import static com.android.server.wm.TaskPersister.DEBUG; import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION; +import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; +import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; +import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES; -import static com.android.server.wm.WindowManagerService.logWithStack; import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY; import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN; -import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; @@ -291,16 +292,17 @@ import android.view.IApplicationToken; import android.view.InputApplicationHandle; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationDefinition; +import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.view.animation.Animation; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ResolverActivity; import com.android.internal.content.ReferrerIntent; -import com.android.internal.R; import com.android.internal.util.ToBooleanFunction; import com.android.internal.util.XmlUtils; import com.android.server.AttributeCache; @@ -315,6 +317,7 @@ import com.android.server.uri.UriPermissionOwner; import com.android.server.wm.ActivityMetricsLogger.WindowingModeTransitionInfoSnapshot; import com.android.server.wm.ActivityStack.ActivityState; import com.android.server.wm.WindowManagerService.H; +import com.android.server.wm.utils.InsetUtils; import com.google.android.collect.Sets; @@ -332,7 +335,6 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Objects; -import java.util.function.Consumer; /** * An entry in the history stack, representing an activity. @@ -551,24 +553,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private boolean mCurrentLaunchCanTurnScreenOn = true; /** - * This gets used during some open/close transitions as well as during a change transition - * where it represents the starting-state snapshot. - */ - private AppWindowThumbnail mThumbnail; - private final Rect mTransitStartRect = new Rect(); - - /** - * If we are running an animation, this determines the transition type. Must be one of - * AppTransition.TRANSIT_* constants. - */ - private int mTransit; - - /** - * If we are running an animation, this determines the flags during this animation. Must be a - * bitwise combination of AppTransition.TRANSIT_FLAG_* constants. - */ - private int mTransitFlags; - /** * This leash is used to "freeze" the app surface in place after the state change, but before * the animation is ready to start. */ @@ -664,7 +648,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // TODO: Have a WindowContainer state for tracking exiting/deferred removal. boolean mIsExiting; - boolean mLaunchTaskBehind; boolean mEnteringAnimation; boolean mAppStopped; @@ -676,15 +659,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>(); ArrayDeque<Configuration> mFrozenMergedConfig = new ArrayDeque<>(); - /** Whether this token should be boosted at the top of all app window tokens. */ - @VisibleForTesting boolean mNeedsZBoost; - - /** Layer used to constrain the animation to a token's stack bounds. */ - SurfaceControl mAnimationBoundsLayer; - - /** Whether this token needs to create mAnimationBoundsLayer for cropping animations. */ - boolean mNeedsAnimationBoundsLayer; - private AppSaturationInfo mLastAppSaturationInfo; private final ColorDisplayService.ColorTransformController mColorTransformController = @@ -710,10 +684,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private final Configuration mTmpConfig = new Configuration(); private final Rect mTmpBounds = new Rect(); - private final Point mTmpPoint = new Point(); - private final Rect mTmpRect = new Rect(); - private final Rect mTmpPrevBounds = new Rect(); - // Token for targeting this activity for assist purposes. final Binder assistToken = new Binder(); @@ -2305,7 +2275,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * 2. App is delayed closing since it might enter PIP. */ boolean isClosingOrEnteringPip() { - return (isAnimating() && hiddenRequested) || mWillCloseOrEnterPip; + return (isAnimating(TRANSITION | PARENTS) && hiddenRequested) || mWillCloseOrEnterPip; } /** * @return Whether AppOps allows this package to enter picture-in-picture. @@ -2472,7 +2442,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } final ActivityStack stack = getActivityStack(); - final boolean mayAdjustFocus = (isState(RESUMED) || stack.mResumedActivity == null) + final boolean mayAdjustTop = (isState(RESUMED) || stack.mResumedActivity == null) + && stack.isFocusedStackOnDisplay(); + final boolean shouldAdjustGlobalFocus = mayAdjustTop // It must be checked before {@link #makeFinishingLocked} is called, because a stack // is not visible if it only contains finishing activities. && mRootActivityContainer.isTopDisplayFocusedStack(stack); @@ -2500,9 +2472,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // We are finishing the top focused activity and its stack has nothing to be focused so // the next focusable stack should be focused. - if (mayAdjustFocus + if (mayAdjustTop && (stack.topRunningActivityLocked() == null || !stack.isFocusable())) { - stack.adjustFocusToNextFocusableStack("finish-top"); + if (shouldAdjustGlobalFocus) { + // Move the entire hierarchy to top with updating global top resumed activity + // and focused application if needed. + stack.adjustFocusToNextFocusableStack("finish-top"); + } else { + // Only move the next stack to top in its display. + final ActivityDisplay display = stack.getDisplay(); + final ActivityRecord next = display.topRunningActivity(); + if (next != null) { + display.positionChildAtTop(next.getActivityStack(), + false /* includingParents */, "finish-display-top"); + } + } } finishActivityResults(resultCode, resultData); @@ -2632,20 +2616,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // implied that the current finishing activity should be added into stopping list rather // than destroy immediately. final boolean isNextNotYetVisible = next != null && (!next.nowVisible || !next.visible); - final boolean notGlobalFocusedStack = - getActivityStack() != mRootActivityContainer.getTopDisplayFocusedStack(); if (isVisible && isNextNotYetVisible) { // Add this activity to the list of stopping activities. It will be processed and // destroyed when the next activity reports idle. addToStopping(false /* scheduleIdle */, false /* idleDelayed */, "completeFinishing"); setState(STOPPING, "completeFinishing"); - if (notGlobalFocusedStack) { - // Ensuring visibility and configuration only for non-focused stacks since this - // method call is relatively expensive and not necessary for focused stacks. - mRootActivityContainer.ensureVisibilityAndConfig(next, getDisplayId(), - false /* markFrozenIfConfigChanged */, true /* deferResume */); - } } else if (addToFinishingAndWaitForIdle()) { // We added this activity to the finishing list and something else is becoming resumed. // The activity will complete finishing when the next activity reports idle. No need to @@ -3108,7 +3084,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Removing app %s delayed=%b animation=%s animating=%b", this, delayed, - getAnimation(), isSelfAnimating()); + getAnimation(), isAnimating(TRANSITION)); ProtoLog.v(WM_DEBUG_ADD_REMOVE, "removeAppToken: %s" + " delayed=%b Callers=%s", this, delayed, Debug.getCallers(4)); @@ -3120,7 +3096,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // If this window was animating, then we need to ensure that the app transition notifies // that animations have completed in DisplayContent.handleAnimatingStoppedAndTransition(), // so add to that list now - if (isSelfAnimating()) { + if (isAnimating(TRANSITION)) { getDisplayContent().mNoAnimationNotifyOnTransitionFinished.add(token); } @@ -3468,7 +3444,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * color mode set to avoid jank in the middle of the transition. */ boolean canShowWindows() { - return allDrawn && !(isReallyAnimating() && hasNonDefaultColorWindow()); + return allDrawn && !(isAnimating() && hasNonDefaultColorWindow()); } /** @@ -3530,17 +3506,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return forAllWindowsUnchecked(callback, traverseTopToBottom); } - @Override - void forAllActivities(Consumer<ActivityRecord> callback) { - callback.accept(this); - } - boolean forAllWindowsUnchecked(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) { return super.forAllWindows(callback, traverseTopToBottom); } @Override + boolean forAllActivities(ToBooleanFunction<ActivityRecord> callback) { + return callback.apply(this); + } + + @Override protected void setLayer(Transaction t, int layer) { if (!mSurfaceAnimator.hasLeash()) { t.setLayer(mSurfaceControl, layer); @@ -4121,8 +4097,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (transit != WindowManager.TRANSIT_UNSET) { if (mUseTransferredAnimation) { - runningAppAnimation = isReallyAnimating(); - } else if (applyAnimationLocked(lp, transit, visible, isVoiceInteraction)) { + runningAppAnimation = isAnimating(); + } else if (applyAnimation(lp, transit, visible, isVoiceInteraction)) { runningAppAnimation = true; } delayed = runningAppAnimation; @@ -4173,21 +4149,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } mUseTransferredAnimation = false; - if (isReallyAnimating()) { - delayed = true; - } else { + delayed = isAnimating(CHILDREN); + if (!delayed) { // We aren't animating anything, but exiting windows rely on the animation finished // callback being called in case the ActivityRecord was pretending to be animating, // which we might have done because we were in closing/opening apps list. onAnimationFinished(); } - for (int i = mChildren.size() - 1; i >= 0 && !delayed; i--) { - if ((mChildren.get(i)).isSelfOrChildAnimating()) { - delayed = true; - } - } - if (visibilityChanged) { if (visible && !delayed) { // The token was made immediately visible, there will be no entrance animation. @@ -4202,7 +4171,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // updated. // If we're becoming invisible, update the client visibility if we are not running an // animation. Otherwise, we'll update client visibility in onAnimationFinished. - if (visible || !isReallyAnimating()) { + if (visible || !isAnimating()) { setClientHidden(!visible); } @@ -5282,13 +5251,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (!allDrawn && w.mightAffectAllDrawn()) { if (DEBUG_VISIBILITY || WM_DEBUG_ORIENTATION.isLogToLogcat()) { Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw() - + ", isAnimationSet=" + isSelfAnimating()); + + ", isAnimationSet=" + isAnimating(TRANSITION)); if (!w.isDrawnLw()) { Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController + " pv=" + w.isVisibleByPolicy() + " mDrawState=" + winAnimator.drawStateToString() + " ph=" + w.isParentWindowHidden() + " th=" + hiddenRequested - + " a=" + isSelfAnimating()); + + " a=" + isAnimating(TRANSITION)); } } @@ -5312,7 +5281,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } } else if (w.isDrawnLw()) { - onStartingWindowDrawn(SystemClock.uptimeMillis()); + onStartingWindowDrawn(SystemClock.elapsedRealtimeNanos()); startingDisplayed = true; } } @@ -5321,10 +5290,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** Called when the starting window for this container is drawn. */ - private void onStartingWindowDrawn(long timestamp) { + private void onStartingWindowDrawn(long timestampNs) { synchronized (mAtmService.mGlobalLock) { mAtmService.mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn( - getWindowingMode(), timestamp); + getWindowingMode(), timestampNs); } } @@ -5716,204 +5685,55 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } - @VisibleForTesting boolean shouldAnimate(int transit) { + final Task task = getTask(); + if (task != null && !task.shouldAnimate()) { + return false; + } final boolean isSplitScreenPrimary = getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; final boolean allowSplitScreenPrimaryAnimation = transit != TRANSIT_WALLPAPER_OPEN; - // Don't animate while the task runs recents animation but only if we are in the mode - // where we cancel with deferred screenshot, which means that the controller has - // transformed the task. - final RecentsAnimationController controller = mWmService.getRecentsAnimationController(); - if (controller != null && controller.isAnimatingTask(getTask()) - && controller.shouldDeferCancelUntilNextTransition()) { - return false; - } - // We animate always if it's not split screen primary, and only some special cases in split // screen primary because it causes issues with stack clipping when we run an un-minimize // animation at the same time. return !isSplitScreenPrimary || allowSplitScreenPrimaryAnimation; } - /** - * Creates a layer to apply crop to an animation. - */ - private SurfaceControl createAnimationBoundsLayer(Transaction t) { - ProtoLog.i(WM_DEBUG_APP_TRANSITIONS_ANIM, "Creating animation bounds layer"); - final SurfaceControl.Builder builder = makeAnimationLeash() - .setParent(getAnimationLeashParent()) - .setName(getSurfaceControl() + " - animation-bounds"); - final SurfaceControl boundsLayer = builder.build(); - t.show(boundsLayer); - return boundsLayer; + @Override + boolean isChangingAppTransition() { + final Task task = getTask(); + if (task != null) { + return task.isChangingAppTransition(); + } + return super.isChangingAppTransition(); } - boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter, + @Override + boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, boolean isVoiceInteraction) { - if (mWmService.mDisableTransitionAnimation || !shouldAnimate(transit)) { ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: transition animation is disabled or skipped. " - + "atoken=%s", this); + + "container=%s", this); cancelAnimation(); return false; } - - // Only apply an animation if the display isn't frozen. If it is frozen, there is no reason - // to animate and it can cause strange artifacts when we unfreeze the display if some - // different animation is running. - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AWT#applyAnimationLocked"); - if (okToAnimate()) { - final AnimationAdapter adapter; - AnimationAdapter thumbnailAdapter = null; - - final int appStackClipMode = - getDisplayContent().mAppTransition.getAppStackClipMode(); - - // Separate position and size for use in animators. - mTmpRect.set(getAnimationBounds(appStackClipMode)); - mTmpPoint.set(mTmpRect.left, mTmpRect.top); - mTmpRect.offsetTo(0, 0); - - final boolean isChanging = AppTransition.isChangeTransit(transit) && enter - && getDisplayContent().mChangingApps.contains(this); - - // Delaying animation start isn't compatible with remote animations at all. - if (getDisplayContent().mAppTransition.getRemoteAnimationController() != null - && !mSurfaceAnimator.isAnimationStartDelayed()) { - RemoteAnimationController.RemoteAnimationRecord adapters = - getDisplayContent().mAppTransition.getRemoteAnimationController() - .createRemoteAnimationRecord(this, mTmpPoint, mTmpRect, - (isChanging ? mTransitStartRect : null)); - adapter = adapters.mAdapter; - thumbnailAdapter = adapters.mThumbnailAdapter; - } else if (isChanging) { - final float durationScale = mWmService.getTransitionAnimationScaleLocked(); - mTmpRect.offsetTo(mTmpPoint.x, mTmpPoint.y); - adapter = new LocalAnimationAdapter( - new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect, - getDisplayContent().getDisplayInfo(), durationScale, - true /* isAppAnimation */, false /* isThumbnail */), - mWmService.mSurfaceAnimationRunner); - if (mThumbnail != null) { - thumbnailAdapter = new LocalAnimationAdapter( - new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect, - getDisplayContent().getDisplayInfo(), durationScale, - true /* isAppAnimation */, true /* isThumbnail */), - mWmService.mSurfaceAnimationRunner); - } - mTransit = transit; - mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags(); - } else { - mNeedsAnimationBoundsLayer = (appStackClipMode == STACK_CLIP_AFTER_ANIM); - - final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction); - if (a != null) { - // Only apply corner radius to animation if we're not in multi window mode. - // We don't want rounded corners when in pip or split screen. - final float windowCornerRadius = !inMultiWindowMode() - ? getDisplayContent().getWindowCornerRadius() - : 0; - adapter = new LocalAnimationAdapter( - new WindowAnimationSpec(a, mTmpPoint, mTmpRect, - getDisplayContent().mAppTransition.canSkipFirstFrame(), - appStackClipMode, - true /* isAppAnimation */, - windowCornerRadius), - mWmService.mSurfaceAnimationRunner); - if (a.getZAdjustment() == Animation.ZORDER_TOP) { - mNeedsZBoost = true; - } - mTransit = transit; - mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags(); - } else { - adapter = null; - } - } - if (adapter != null) { - startAnimation(getPendingTransaction(), adapter, !isVisible()); - if (adapter.getShowWallpaper()) { - mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; - } - if (thumbnailAdapter != null) { - mThumbnail.startAnimation( - getPendingTransaction(), thumbnailAdapter, !isVisible()); - } - } - } else { - cancelAnimation(); - } - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - - return isReallyAnimating(); + return super.applyAnimation(lp, transit, enter, isVoiceInteraction); } - private Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, - boolean isVoiceInteraction) { - final DisplayContent displayContent = getTask().getDisplayContent(); - final DisplayInfo displayInfo = displayContent.getDisplayInfo(); - final int width = displayInfo.appWidth; - final int height = displayInfo.appHeight; - ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, - "applyAnimation: atoken=%s", this); - - // Determine the visible rect to calculate the thumbnail clip - final WindowState win = findMainWindow(); - final Rect frame = new Rect(0, 0, width, height); - final Rect displayFrame = new Rect(0, 0, - displayInfo.logicalWidth, displayInfo.logicalHeight); - final Rect insets = new Rect(); - final Rect stableInsets = new Rect(); - Rect surfaceInsets = null; - final boolean freeform = win != null && win.inFreeformWindowingMode(); - if (win != null) { - // Containing frame will usually cover the whole screen, including dialog windows. - // For freeform workspace windows it will not cover the whole screen and it also - // won't exactly match the final freeform window frame (e.g. when overlapping with - // the status bar). In that case we need to use the final frame. - if (freeform) { - frame.set(win.getFrameLw()); - } else if (win.isLetterboxedAppWindow()) { - frame.set(getTask().getBounds()); - } else if (win.isDockedResizing()) { - // If we are animating while docked resizing, then use the stack bounds as the - // animation target (which will be different than the task bounds) - frame.set(getTask().getParent().getBounds()); - } else { - frame.set(win.getContainingFrame()); - } - surfaceInsets = win.getAttrs().surfaceInsets; - // XXX(b/72757033): These are insets relative to the window frame, but we're really - // interested in the insets relative to the frame we chose in the if-blocks above. - win.getContentInsets(insets); - win.getStableInsets(stableInsets); - } - - if (mLaunchTaskBehind) { - // Differentiate the two animations. This one which is briefly on the screen - // gets the !enter animation, and the other activity which remains on the - // screen gets the enter animation. Both appear in the mOpeningApps set. - enter = false; - } - ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, - "Loading animation for app transition. transit=%s enter=%b frame=%s insets=%s " - + "surfaceInsets=%s", - AppTransition.appTransitionToString(transit), enter, frame, insets, surfaceInsets); - final Configuration displayConfig = displayContent.getConfiguration(); - final Animation a = getDisplayContent().mAppTransition.loadAnimation(lp, transit, enter, - displayConfig.uiMode, displayConfig.orientation, frame, displayFrame, insets, - surfaceInsets, stableInsets, isVoiceInteraction, freeform, getTask().mTaskId); - if (a != null) { - if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this); - final int containingWidth = frame.width(); - final int containingHeight = frame.height(); - a.initialize(containingWidth, containingHeight, width, height); - a.scaleCurrentDuration(mWmService.getTransitionAnimationScaleLocked()); - } - return a; + /** + * Creates a layer to apply crop to an animation. + */ + private SurfaceControl createAnimationBoundsLayer(Transaction t) { + ProtoLog.i(WM_DEBUG_APP_TRANSITIONS_ANIM, "Creating animation bounds layer"); + final SurfaceControl.Builder builder = makeAnimationLeash() + .setParent(getAnimationLeashParent()) + .setName(getSurfaceControl() + " - animation-bounds"); + final SurfaceControl boundsLayer = builder.build(); + t.show(boundsLayer); + return boundsLayer; } @Override @@ -5935,6 +5755,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM); } + @Override boolean isWaitingForTransitionStart() { final DisplayContent dc = getDisplayContent(); // TODO: Test for null can be removed once unification is done. @@ -6063,10 +5884,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Override void prepareSurfaces() { - // isSelfAnimating also returns true when we are about to start a transition, so we need - // to check super here. - final boolean reallyAnimating = super.isSelfAnimating(); - final boolean show = !isHidden() || reallyAnimating; + final boolean show = !isHidden() || isAnimating(); if (mSurfaceControl != null) { if (show && !mLastSurfaceShowing) { @@ -6094,14 +5912,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void attachThumbnailAnimation() { - if (!isReallyAnimating()) { + if (!isAnimating()) { return; } - final int taskId = getTask().mTaskId; final GraphicBuffer thumbnailHeader = - getDisplayContent().mAppTransition.getAppTransitionThumbnailHeader(taskId); + getDisplayContent().mAppTransition.getAppTransitionThumbnailHeader(getTask()); if (thumbnailHeader == null) { - ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "No thumbnail header bitmap for: %d", taskId); + ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "No thumbnail header bitmap for: %s", getTask()); return; } clearThumbnail(); @@ -6115,7 +5932,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * {@link android.app.ActivityOptions#ANIM_OPEN_CROSS_PROFILE_APPS} animation. */ void attachCrossProfileAppsThumbnailAnimation() { - if (!isReallyAnimating()) { + if (!isAnimating()) { return; } clearThumbnail(); @@ -6155,22 +5972,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final Rect insets = win != null ? win.getContentInsets() : null; final Configuration displayConfig = mDisplayContent.getConfiguration(); return getDisplayContent().mAppTransition.createThumbnailAspectScaleAnimationLocked( - appRect, insets, thumbnailHeader, getTask().mTaskId, displayConfig.uiMode, + appRect, insets, thumbnailHeader, getTask(), displayConfig.uiMode, displayConfig.orientation); } @Override - boolean isAppAnimating() { - return isSelfAnimating(); - } - - @Override - boolean isSelfAnimating() { - // If we are about to start a transition, we also need to be considered animating. - return isWaitingForTransitionStart() || isReallyAnimating(); - } - - @Override public void onAnimationLeashLost(Transaction t) { super.onAnimationLeashLost(t); if (mAnimationBoundsLayer != null) { @@ -6275,15 +6081,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } - /** - * @return True if and only if we are actually running an animation. Note that - * {@link #isSelfAnimating} also returns true if we are waiting for an animation to - * start. - */ - private boolean isReallyAnimating() { - return super.isSelfAnimating(); - } - @Override void cancelAnimation() { cancelAnimationOnly(); @@ -6685,6 +6482,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } @VisibleForTesting + @Override Rect getAnimationBounds(int appStackClipMode) { if (appStackClipMode == STACK_CLIP_BEFORE_ANIM && getStack() != null) { // Using the stack bounds here effectively applies the clipping before animation. @@ -7637,7 +7435,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A super.writeToProto(proto, WINDOW_TOKEN, logLevel); proto.write(LAST_SURFACE_SHOWING, mLastSurfaceShowing); proto.write(IS_WAITING_FOR_TRANSITION_START, isWaitingForTransitionStart()); - proto.write(IS_REALLY_ANIMATING, isReallyAnimating()); + proto.write(IS_ANIMATING, isAnimating()); if (mThumbnail != null){ mThumbnail.writeToProto(proto, THUMBNAIL); } @@ -7759,4 +7557,35 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A System.arraycopy(translation, 0, mTranslation, 0, mTranslation.length); } } + + @Override + RemoteAnimationTarget createRemoteAnimationTarget( + RemoteAnimationController.RemoteAnimationRecord record) { + final Task task = getTask(); + final WindowState mainWindow = findMainWindow(); + if (task == null || mainWindow == null) { + return null; + } + final Rect insets = new Rect(); + mainWindow.getContentInsets(insets); + InsetUtils.addInsets(insets, getLetterboxInsets()); + return new RemoteAnimationTarget(task.mTaskId, record.getMode(), + record.mAdapter.mCapturedLeash, !task.fillsParent(), + mainWindow.mWinAnimator.mLastClipRect, insets, + getPrefixOrderIndex(), record.mAdapter.mPosition, + record.mAdapter.mStackBounds, task.getWindowConfiguration(), + false /*isNotInRecents*/, + record.mThumbnailAdapter != null ? record.mThumbnailAdapter.mCapturedLeash : null, + record.mStartBounds); + } + + @Override + void getAnimationFrames(Rect outFrame, Rect outInsets, Rect outStableInsets, + Rect outSurfaceInsets) { + final WindowState win = findMainWindow(); + if (win == null) { + return; + } + win.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets); + } } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 64351fb07aed..87b41b4263f4 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -82,6 +82,7 @@ import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_WHITELISTED; import static com.android.server.wm.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE; import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT; +import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import android.Manifest; import android.app.Activity; @@ -2127,7 +2128,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) { ActivityRecord s = mStoppingActivities.get(activityNdx); - final boolean animating = s.isSelfAnimating(); + final boolean animating = s.isAnimating(TRANSITION); if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible + " animating=" + animating + " finishing=" + s.finishing); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index ff0dc54f86b1..f87175de1d06 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2354,7 +2354,7 @@ class ActivityStarter { mMovedToFront = true; } - if (launchStack.topTask() == null) { + if (launchStack != null && launchStack.topTask() == null) { // The task does not need to be reparented to the launch stack. Remove the // launch stack if there is no activity in it. Slog.w(TAG, "Removing an empty stack: " + launchStack); diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index c1143c856bb4..cb9a200e6010 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -219,7 +219,7 @@ public class AppTransition implements Dump { private int mNextAppTransitionExit; private int mNextAppTransitionInPlace; - // Keyed by task id. + // Keyed by WindowContainer hashCode. private final SparseArray<AppTransitionAnimationSpec> mNextAppTransitionAnimationsSpecs = new SparseArray<>(); private IAppTransitionAnimationSpecsFuture mNextAppTransitionAnimationsSpecsFuture; @@ -372,8 +372,9 @@ public class AppTransition implements Dump { setAppTransitionState(APP_STATE_TIMEOUT); } - GraphicBuffer getAppTransitionThumbnailHeader(int taskId) { - AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId); + GraphicBuffer getAppTransitionThumbnailHeader(WindowContainer container) { + AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get( + container.hashCode()); if (spec == null) { spec = mDefaultNextAppTransitionAnimationSpec; } @@ -789,14 +790,15 @@ public class AppTransition implements Dump { } } - void getNextAppTransitionStartRect(int taskId, Rect rect) { - AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId); + void getNextAppTransitionStartRect(WindowContainer container, Rect rect) { + AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get( + container.hashCode()); if (spec == null) { spec = mDefaultNextAppTransitionAnimationSpec; } if (spec == null || spec.rect == null) { - Slog.e(TAG, "Starting rect for task: " + taskId + " requested, but not available", - new Throwable()); + Slog.e(TAG, "Starting rect for container: " + container + + " requested, but not available", new Throwable()); rect.setEmpty(); } else { rect.set(spec.rect); @@ -1065,7 +1067,7 @@ public class AppTransition implements Dump { * when a thumbnail is specified with the pending animation override. */ Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, @Nullable Rect contentInsets, - GraphicBuffer thumbnailHeader, final int taskId, int uiMode, int orientation) { + GraphicBuffer thumbnailHeader, WindowContainer container, int uiMode, int orientation) { Animation a; final int thumbWidthI = thumbnailHeader.getWidth(); final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; @@ -1073,7 +1075,7 @@ public class AppTransition implements Dump { final int appWidth = appRect.width(); float scaleW = appWidth / thumbWidth; - getNextAppTransitionStartRect(taskId, mTmpRect); + getNextAppTransitionStartRect(container, mTmpRect); final float fromX; float fromY; final float toX; @@ -1226,7 +1228,7 @@ public class AppTransition implements Dump { Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState, int uiMode, int orientation, int transit, Rect containingFrame, Rect contentInsets, @Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean freeform, - int taskId) { + WindowContainer container) { Animation a; final int appWidth = containingFrame.width(); final int appHeight = containingFrame.height(); @@ -1244,10 +1246,10 @@ public class AppTransition implements Dump { final boolean scaleUp = thumbTransitState == THUMBNAIL_TRANSITION_ENTER_SCALE_UP; if (freeform && scaleUp) { a = createAspectScaledThumbnailEnterFreeformAnimationLocked( - containingFrame, surfaceInsets, taskId); + containingFrame, surfaceInsets, container); } else if (freeform) { a = createAspectScaledThumbnailExitFreeformAnimationLocked( - containingFrame, surfaceInsets, taskId); + containingFrame, surfaceInsets, container); } else { AnimationSet set = new AnimationSet(true); @@ -1359,15 +1361,15 @@ public class AppTransition implements Dump { } private Animation createAspectScaledThumbnailEnterFreeformAnimationLocked(Rect frame, - @Nullable Rect surfaceInsets, int taskId) { - getNextAppTransitionStartRect(taskId, mTmpRect); + @Nullable Rect surfaceInsets, WindowContainer container) { + getNextAppTransitionStartRect(container, mTmpRect); return createAspectScaledThumbnailFreeformAnimationLocked(mTmpRect, frame, surfaceInsets, true); } private Animation createAspectScaledThumbnailExitFreeformAnimationLocked(Rect frame, - @Nullable Rect surfaceInsets, int taskId) { - getNextAppTransitionStartRect(taskId, mTmpRect); + @Nullable Rect surfaceInsets, WindowContainer container) { + getNextAppTransitionStartRect(container, mTmpRect); return createAspectScaledThumbnailFreeformAnimationLocked(frame, mTmpRect, surfaceInsets, false); } @@ -1469,10 +1471,10 @@ public class AppTransition implements Dump { * leaving, and the activity that is entering. */ Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, Rect containingFrame, - int transit, int taskId) { + int transit, WindowContainer container) { final int appWidth = containingFrame.width(); final int appHeight = containingFrame.height(); - final GraphicBuffer thumbnailHeader = getAppTransitionThumbnailHeader(taskId); + final GraphicBuffer thumbnailHeader = getAppTransitionThumbnailHeader(container); Animation a; getDefaultNextAppTransitionStartRect(mTmpRect); final int thumbWidthI = thumbnailHeader != null ? thumbnailHeader.getWidth() : appWidth; @@ -1615,7 +1617,7 @@ public class AppTransition implements Dump { Animation loadAnimation(LayoutParams lp, int transit, boolean enter, int uiMode, int orientation, Rect frame, Rect displayFrame, Rect insets, @Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean isVoiceInteraction, - boolean freeform, int taskId) { + boolean freeform, WindowContainer container) { Animation a; if (isKeyguardGoingAwayTransit(transit) && enter) { a = loadKeyguardExitAnimation(transit); @@ -1679,7 +1681,7 @@ public class AppTransition implements Dump { mNextAppTransitionScaleUp = (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP); a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter), - frame, transit, taskId); + frame, transit, container); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: anim=%s nextAppTransition=%s transit=%s isEntrance=%b " + "Callers=%s", @@ -1692,7 +1694,7 @@ public class AppTransition implements Dump { (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP); a = createAspectScaledThumbnailEnterExitAnimationLocked( getThumbnailTransitionState(enter), uiMode, orientation, transit, frame, - insets, surfaceInsets, stableInsets, freeform, taskId); + insets, surfaceInsets, stableInsets, freeform, container); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: anim=%s nextAppTransition=%s transit=%s isEntrance=%b " + "Callers=%s", @@ -1895,7 +1897,11 @@ public class AppTransition implements Dump { for (int i = 0; i < specs.length; i++) { AppTransitionAnimationSpec spec = specs[i]; if (spec != null) { - mNextAppTransitionAnimationsSpecs.put(spec.taskId, spec); + final WindowContainer container = findTask(spec.taskId); + if (container == null) { + continue; + } + mNextAppTransitionAnimationsSpecs.put(container.hashCode(), spec); if (i == 0) { // In full screen mode, the transition code depends on the default spec // to be set. @@ -1912,6 +1918,19 @@ public class AppTransition implements Dump { } } + private Task findTask(int taskId) { + if (taskId < 0) { + return null; + } + ArrayList<Task> tasks = new ArrayList<>(); + mDisplayContent.forAllTasks(task -> { + if (task.mTaskId == taskId) { + tasks.add(task); + } + }); + return tasks.size() == 1 ? tasks.get(0) : null; + } + void overridePendingAppTransitionMultiThumbFuture( IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback, boolean scaleUp) { diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 3bda0c25a6b0..bef6af350269 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -410,7 +410,7 @@ public class AppTransitionController { ActivityRecord activity = apps.valueAt(i); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now changing app %s", activity); activity.cancelAnimationOnly(); - activity.applyAnimationLocked(null, transit, true, false); + activity.applyAnimation(null, transit, true, false); activity.updateReportedVisibilityLocked(); mService.openSurfaceTransaction(); try { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 2d1d29710c95..89568ebf4277 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -106,6 +106,8 @@ import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_SCREEN_ON; import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS; +import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; +import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT; @@ -2363,7 +2365,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override void removeIfPossible() { - if (isAnimating()) { + if (isAnimating(TRANSITION | PARENTS)) { mDeferredRemoval = true; return; } @@ -3165,13 +3167,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // to look at all windows below the current target that are in this app, finding the // highest visible one in layering. WindowState highestTarget = null; - if (activity.isSelfAnimating()) { + if (activity.isAnimating(TRANSITION)) { highestTarget = activity.getHighestAnimLayerWindow(curTarget); } if (highestTarget != null) { - if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, mAppTransition + " " + highestTarget - + " animating=" + highestTarget.isAnimating()); + if (DEBUG_INPUT_METHOD) { + Slog.v(TAG_WM, mAppTransition + " " + highestTarget + " animating=" + + highestTarget.isAnimating(TRANSITION | PARENTS)); + } if (mAppTransition.isTransitionSet()) { // If we are currently setting up for an animation, hold everything until we @@ -4322,7 +4326,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // The split screen divider anchor is located above the split screen window. layerForSplitScreenDividerAnchor = layer++; } - if (s.isTaskAnimating() || s.isAppAnimating()) { + if (s.isTaskAnimating() || s.isAppTransitioning()) { // The animation layer is located above the highest animating stack and no // higher. layerForAnimationLayer = layer++; @@ -4651,7 +4655,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // so it get's layered above the starting window. if (imeTarget != null && !(imeTarget.mActivityRecord != null && imeTarget.mActivityRecord.hasStartingWindow()) - && (!(imeTarget.inSplitScreenWindowingMode() || imeTarget.mToken.isAppAnimating()) + && (!(imeTarget.inSplitScreenWindowingMode() + || imeTarget.mToken.isAppTransitioning()) && (imeTarget.getSurfaceControl() != null))) { mImeWindowsContainers.assignRelativeLayer(t, imeTarget.getSurfaceControl(), // TODO: We need to use an extra level on the app surface to ensure diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index a783ee9d4e44..6e238b33d8dd 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -1920,10 +1920,18 @@ public class DisplayPolicy { vf.set(displayFrames.mStable); if (adjust == SOFT_INPUT_ADJUST_RESIZE) { - cf.bottom = displayFrames.mContent.bottom; + // cf.bottom should not be below the stable bottom, or the content might be obscured + // by the navigation bar. + if (cf.bottom > displayFrames.mContent.bottom) { + cf.bottom = displayFrames.mContent.bottom; + } } else { - cf.bottom = displayFrames.mDock.bottom; - vf.bottom = displayFrames.mContent.bottom; + if (cf.bottom > displayFrames.mDock.bottom) { + cf.bottom = displayFrames.mDock.bottom; + } + if (vf.bottom > displayFrames.mContent.bottom) { + vf.bottom = displayFrames.mContent.bottom; + } } } else { dcf.set(displayFrames.mSystem); diff --git a/services/core/java/com/android/server/wm/PolicyControl.java b/services/core/java/com/android/server/wm/PolicyControl.java index 4c8ce9ebb72c..0f92bc83a666 100644 --- a/services/core/java/com/android/server/wm/PolicyControl.java +++ b/services/core/java/com/android/server/wm/PolicyControl.java @@ -26,6 +26,8 @@ import android.view.View; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; +import com.android.internal.annotations.VisibleForTesting; + import java.io.PrintWriter; import java.io.StringWriter; @@ -51,7 +53,8 @@ class PolicyControl { private static final String TAG = "PolicyControl"; private static final boolean DEBUG = false; - private static final String NAME_IMMERSIVE_FULL = "immersive.full"; + @VisibleForTesting + static final String NAME_IMMERSIVE_FULL = "immersive.full"; private static final String NAME_IMMERSIVE_STATUS = "immersive.status"; private static final String NAME_IMMERSIVE_NAVIGATION = "immersive.navigation"; private static final String NAME_IMMERSIVE_PRECONFIRMATIONS = "immersive.preconfirms"; @@ -67,15 +70,19 @@ class PolicyControl { : (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility); if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) { vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - | View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + | View.SYSTEM_UI_FLAG_FULLSCREEN; + if (attrs.isFullscreen()) { + vis |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + } vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.STATUS_BAR_TRANSLUCENT); } if (sImmersiveNavigationFilter != null && sImmersiveNavigationFilter.matches(attrs)) { vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + if (attrs.isFullscreen()) { + vis |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + } vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.NAVIGATION_BAR_TRANSLUCENT); } @@ -144,7 +151,8 @@ class PolicyControl { } } - private static void setFilters(String value) { + @VisibleForTesting + static void setFilters(String value) { if (DEBUG) Slog.d(TAG, "setFilters: " + value); sImmersiveStatusFilter = null; sImmersiveNavigationFilter = null; diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java index 0c0cf9262e44..e0a7b18f40c0 100644 --- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java +++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java @@ -16,6 +16,9 @@ package com.android.server.wm; +import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; +import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; + import android.util.ArraySet; import android.view.Display.Mode; import android.view.DisplayInfo; @@ -67,7 +70,7 @@ class RefreshRatePolicy { // If app is animating, it's not able to control refresh rate because we want the animation // to run in default refresh rate. - if (w.isAnimating()) { + if (w.isAnimating(TRANSITION | PARENTS)) { return 0; } diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index c23ffd964e06..efd124164779 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -41,7 +41,6 @@ import com.android.internal.util.FastPrintWriter; import com.android.server.protolog.ProtoLogImpl; import com.android.server.protolog.common.ProtoLog; import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; -import com.android.server.wm.utils.InsetUtils; import java.io.PrintWriter; import java.io.StringWriter; @@ -76,20 +75,20 @@ class RemoteAnimationController implements DeathRecipient { } /** - * Creates an animation record for each individual {@link ActivityRecord}. + * Creates an animation record for each individual {@link WindowContainer}. * - * @param activity The app to animate. + * @param windowContainer The windows to animate. * @param position The position app bounds, in screen coordinates. * @param stackBounds The stack bounds of the app relative to position. * @param startBounds The stack bounds before the transition, in screen coordinates * @return The record representing animation(s) to run on the app. */ - RemoteAnimationRecord createRemoteAnimationRecord(ActivityRecord activity, Point position, - Rect stackBounds, Rect startBounds) { - ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): token=%s", - activity); + RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer, + Point position, Rect stackBounds, Rect startBounds) { + ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): container=%s", + windowContainer); final RemoteAnimationRecord adapters = - new RemoteAnimationRecord(activity, position, stackBounds, startBounds); + new RemoteAnimationRecord(windowContainer, position, stackBounds, startBounds); mPendingAnimations.add(adapters); return adapters; } @@ -169,11 +168,12 @@ class RemoteAnimationController implements DeathRecipient { final RemoteAnimationRecord wrappers = mPendingAnimations.get(i); final RemoteAnimationTarget target = wrappers.createRemoteAnimationTarget(); if (target != null) { - ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tAdd token=%s", wrappers.mActivityRecord); + ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tAdd container=%s", + wrappers.mWindowContainer); targets.add(target); } else { - ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tRemove token=%s", - wrappers.mActivityRecord); + ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tRemove container=%s", + wrappers.mWindowContainer); // We can't really start an animation but we still need to make sure to finish the // pending animation that was started by SurfaceAnimator @@ -228,7 +228,8 @@ class RemoteAnimationController implements DeathRecipient { .onAnimationFinished(adapters.mThumbnailAdapter); } mPendingAnimations.remove(i); - ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tapp=%s", adapters.mActivityRecord); + ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tcontainer=%s", + adapters.mWindowContainer); } for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) { @@ -332,7 +333,7 @@ class RemoteAnimationController implements DeathRecipient { }; /** - * Contains information about a remote-animation for one AppWindowToken. This keeps track of, + * Contains information about a remote-animation for one WindowContainer. This keeps track of, * potentially, multiple animating surfaces (AdapterWrappers) associated with one * Window/Transition. For example, a change transition has an adapter controller for the * main window and an adapter controlling the start-state snapshot. @@ -345,12 +346,12 @@ class RemoteAnimationController implements DeathRecipient { RemoteAnimationAdapterWrapper mAdapter; RemoteAnimationAdapterWrapper mThumbnailAdapter = null; RemoteAnimationTarget mTarget; - final ActivityRecord mActivityRecord; + final WindowContainer mWindowContainer; final Rect mStartBounds; - RemoteAnimationRecord(ActivityRecord activityRecord, Point endPos, Rect endBounds, + RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect endBounds, Rect startBounds) { - mActivityRecord = activityRecord; + mWindowContainer = windowContainer; mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, endBounds); if (startBounds != null) { mStartBounds = new Rect(startBounds); @@ -366,31 +367,20 @@ class RemoteAnimationController implements DeathRecipient { } RemoteAnimationTarget createRemoteAnimationTarget() { - final Task task = mActivityRecord.getTask(); - final WindowState mainWindow = mActivityRecord.findMainWindow(); - if (task == null || mainWindow == null || mAdapter == null + if (mAdapter == null || mAdapter.mCapturedFinishCallback == null || mAdapter.mCapturedLeash == null) { return null; } - final Rect insets = new Rect(); - mainWindow.getContentInsets(insets); - InsetUtils.addInsets(insets, mActivityRecord.getLetterboxInsets()); - mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(), - mAdapter.mCapturedLeash, !mActivityRecord.fillsParent(), - mainWindow.mWinAnimator.mLastClipRect, insets, - mActivityRecord.getPrefixOrderIndex(), mAdapter.mPosition, - mAdapter.mStackBounds, task.getWindowConfiguration(), false /*isNotInRecents*/, - mThumbnailAdapter != null ? mThumbnailAdapter.mCapturedLeash : null, - mStartBounds); + mTarget = mWindowContainer.createRemoteAnimationTarget(this); return mTarget; } - private int getMode() { - final DisplayContent dc = mActivityRecord.getDisplayContent(); - if (dc.mOpeningApps.contains(mActivityRecord)) { + int getMode() { + final DisplayContent dc = mWindowContainer.getDisplayContent(); + if (dc.mOpeningApps.contains(mWindowContainer)) { return RemoteAnimationTarget.MODE_OPENING; - } else if (dc.mChangingApps.contains(mActivityRecord)) { + } else if (dc.mChangingApps.contains(mWindowContainer)) { return RemoteAnimationTarget.MODE_CHANGING; } else { return RemoteAnimationTarget.MODE_CLOSING; @@ -398,12 +388,12 @@ class RemoteAnimationController implements DeathRecipient { } } - private class RemoteAnimationAdapterWrapper implements AnimationAdapter { + class RemoteAnimationAdapterWrapper implements AnimationAdapter { private final RemoteAnimationRecord mRecord; SurfaceControl mCapturedLeash; private OnAnimationFinishedCallback mCapturedFinishCallback; - private final Point mPosition = new Point(); - private final Rect mStackBounds = new Rect(); + final Point mPosition = new Point(); + final Rect mStackBounds = new Rect(); RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position, Rect stackBounds) { @@ -423,7 +413,7 @@ class RemoteAnimationController implements DeathRecipient { ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation"); // Restore z-layering, position and stack crop until client has a chance to modify it. - t.setLayer(animationLeash, mRecord.mActivityRecord.getPrefixOrderIndex()); + t.setLayer(animationLeash, mRecord.mWindowContainer.getPrefixOrderIndex()); if (mRecord.mStartBounds != null) { t.setPosition(animationLeash, mRecord.mStartBounds.left, mRecord.mStartBounds.top); t.setWindowCrop(animationLeash, mRecord.mStartBounds.width(), @@ -464,7 +454,7 @@ class RemoteAnimationController implements DeathRecipient { @Override public void dump(PrintWriter pw, String prefix) { - pw.print(prefix); pw.print("token="); pw.println(mRecord.mActivityRecord); + pw.print(prefix); pw.print("container="); pw.println(mRecord.mWindowContainer); if (mRecord.mTarget != null) { pw.print(prefix); pw.println("Target:"); mRecord.mTarget.dump(pw, prefix + " "); diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java index 9db6dc263944..51a3e7205489 100644 --- a/services/core/java/com/android/server/wm/RootActivityContainer.java +++ b/services/core/java/com/android/server/wm/RootActivityContainer.java @@ -1154,7 +1154,7 @@ class RootActivityContainer extends ConfigurationContainer // activity is started and resumed, and no recursion occurs. final ActivityStack focusedStack = display.getFocusedStack(); if (focusedStack != null) { - focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions); + result |= focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions); } } } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 17f5abdda2c7..f5d3affa89ef 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -787,7 +787,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } - if (curDisplay.mAppTransition.isRunning() && !curDisplay.isAppAnimating()) { + if (curDisplay.mAppTransition.isRunning() && !curDisplay.isAppTransitioning()) { // We have finished the animation of an app transition. To do this, we have // delayed a lot of operations like showing and hiding apps, moving apps in // Z-order, etc. diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index bf7dd57e82ed..92ff2dc96ab3 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -28,13 +28,14 @@ import static com.android.server.EventLogTags.WM_TASK_REMOVED; import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; import static com.android.server.wm.TaskProto.APP_WINDOW_TOKENS; import static com.android.server.wm.TaskProto.BOUNDS; -import static com.android.server.wm.TaskProto.DEFER_REMOVAL; import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS; import static com.android.server.wm.TaskProto.FILLS_PARENT; import static com.android.server.wm.TaskProto.ID; import static com.android.server.wm.TaskProto.SURFACE_HEIGHT; import static com.android.server.wm.TaskProto.SURFACE_WIDTH; import static com.android.server.wm.TaskProto.WINDOW_CONTAINER; +import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; +import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -50,6 +51,7 @@ import android.util.EventLog; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.Display; +import android.view.RemoteAnimationTarget; import android.view.Surface; import android.view.SurfaceControl; @@ -204,7 +206,7 @@ class Task extends WindowContainer<ActivityRecord> implements ConfigurationConta // No reason to defer removal of a Task that doesn't have any child. return false; } - return hasWindowsAlive() && mStack.isSelfOrChildAnimating(); + return hasWindowsAlive() && mStack.isAnimating(TRANSITION | CHILDREN); } @Override @@ -645,6 +647,18 @@ class Task extends WindowContainer<ActivityRecord> implements ConfigurationConta return getAppAnimationLayer(ANIMATION_LAYER_HOME); } + boolean shouldAnimate() { + // Don't animate while the task runs recents animation but only if we are in the mode + // where we cancel with deferred screenshot, which means that the controller has + // transformed the task. + final RecentsAnimationController controller = mWmService.getRecentsAnimationController(); + if (controller != null && controller.isAnimatingTask(this) + && controller.shouldDeferCancelUntilNextTransition()) { + return false; + } + return true; + } + @Override SurfaceControl.Builder makeSurface() { return super.makeSurface().setMetadata(METADATA_TASK_ID, mTaskId); @@ -660,6 +674,22 @@ class Task extends WindowContainer<ActivityRecord> implements ConfigurationConta return false; } + /** + * @return {@code true} if changing app transition is running. + */ + @Override + boolean isChangingAppTransition() { + final ActivityRecord activity = getTopVisibleActivity(); + return activity != null && getDisplayContent().mChangingApps.contains(activity); + } + + @Override + RemoteAnimationTarget createRemoteAnimationTarget( + RemoteAnimationController.RemoteAnimationRecord record) { + final ActivityRecord activity = getTopVisibleActivity(); + return activity != null ? activity.createRemoteAnimationTarget(record) : null; + } + WindowState getTopVisibleAppMainWindow() { final ActivityRecord activity = getTopVisibleActivity(); return activity != null ? activity.findMainWindow() : null; diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 355224536066..8f8c7e7caac6 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -52,6 +52,8 @@ import static com.android.server.wm.StackProto.ID; import static com.android.server.wm.StackProto.MINIMIZE_AMOUNT; import static com.android.server.wm.StackProto.TASKS; import static com.android.server.wm.StackProto.WINDOW_CONTAINER; +import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; +import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -69,6 +71,7 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.DisplayCutout; import android.view.DisplayInfo; +import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import com.android.internal.annotations.VisibleForTesting; @@ -934,7 +937,7 @@ public class TaskStack extends WindowContainer<Task> implements @Override void removeIfPossible() { - if (isSelfOrChildAnimating()) { + if (isAnimating(TRANSITION | CHILDREN)) { mDeferRemoval = true; return; } @@ -1792,7 +1795,7 @@ public class TaskStack extends WindowContainer<Task> implements /** Returns true if a removal action is still being deferred. */ boolean checkCompleteDeferredRemoval() { - if (isSelfOrChildAnimating()) { + if (isAnimating(TRANSITION | CHILDREN)) { return true; } if (mDeferRemoval) { @@ -1866,4 +1869,11 @@ public class TaskStack extends WindowContainer<Task> implements AnimatingActivityRegistry getAnimatingActivityRegistry() { return mAnimatingActivityRegistry; } + + @Override + RemoteAnimationTarget createRemoteAnimationTarget( + RemoteAnimationController.RemoteAnimationRecord record) { + final Task task = getTopChild(); + return task != null ? task.createRemoteAnimationTarget(record) : null; + } } diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 1e13aef2f8eb..3632284fdeb6 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -24,6 +24,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; +import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; +import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT; @@ -118,7 +120,8 @@ class WallpaperController { } mFindResults.resetTopWallpaper = true; - if (w.mActivityRecord != null && w.mActivityRecord.isHidden() && !w.mActivityRecord.isSelfAnimating()) { + if (w.mActivityRecord != null && w.mActivityRecord.isHidden() + && !w.mActivityRecord.isAnimating(TRANSITION)) { // If this window's app token is hidden and not animating, it is of no interest to us. if (DEBUG_WALLPAPER) Slog.v(TAG, "Skipping hidden and not animating token: " + w); @@ -136,7 +139,7 @@ class WallpaperController { } final boolean keyguardGoingAwayWithWallpaper = (w.mActivityRecord != null - && w.mActivityRecord.isSelfAnimating() + && w.mActivityRecord.isAnimating(TRANSITION) && AppTransition.isKeyguardGoingAwayTransit(w.mActivityRecord.getTransit()) && (w.mActivityRecord.getTransitFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0); @@ -159,7 +162,8 @@ class WallpaperController { final RecentsAnimationController recentsAnimationController = mService.getRecentsAnimationController(); - final boolean animationWallpaper = w.mActivityRecord != null && w.mActivityRecord.getAnimation() != null + final boolean animationWallpaper = w.mActivityRecord != null + && w.mActivityRecord.getAnimation() != null && w.mActivityRecord.getAnimation().getShowWallpaper(); final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0 || animationWallpaper; @@ -173,7 +177,7 @@ class WallpaperController { && (mWallpaperTarget == w || w.isDrawFinishedLw())) { if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: " + w); mFindResults.setWallpaperTarget(w); - if (w == mWallpaperTarget && w.isAnimating()) { + if (w == mWallpaperTarget && w.isAnimating(TRANSITION | PARENTS)) { // The current wallpaper target is animating, so we'll look behind it for // another possible target and figure out what is going on later. if (DEBUG_WALLPAPER) Slog.v(TAG, @@ -224,19 +228,19 @@ class WallpaperController { if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured=" + (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??") + " animating=" + ((wallpaperTarget != null && wallpaperTarget.mActivityRecord != null) - ? wallpaperTarget.mActivityRecord.isSelfAnimating() : null) + ? wallpaperTarget.mActivityRecord.isAnimating(TRANSITION) : null) + " prev=" + mPrevWallpaperTarget + " recentsAnimationWallpaperVisible=" + isAnimatingWithRecentsComponent); return (wallpaperTarget != null && (!wallpaperTarget.mObscured || isAnimatingWithRecentsComponent || (wallpaperTarget.mActivityRecord != null - && wallpaperTarget.mActivityRecord.isSelfAnimating()))) + && wallpaperTarget.mActivityRecord.isAnimating(TRANSITION)))) || mPrevWallpaperTarget != null; } boolean isWallpaperTargetAnimating() { - return mWallpaperTarget != null && mWallpaperTarget.isAnimating() + return mWallpaperTarget != null && mWallpaperTarget.isAnimating(TRANSITION | PARENTS) && (mWallpaperTarget.mActivityRecord == null || !mWallpaperTarget.mActivityRecord.isWaitingForTransitionStart()); } diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 3a1d6e047fa8..f7525a913e33 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -17,6 +17,8 @@ package com.android.server.wm; import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS; +import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; +import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -192,7 +194,7 @@ public class WindowAnimator { mService.mWindowPlacerLocked.requestTraversal(); } - final boolean rootAnimating = mService.mRoot.isSelfOrChildAnimating(); + final boolean rootAnimating = mService.mRoot.isAnimating(TRANSITION | CHILDREN); if (rootAnimating && !mLastRootAnimating) { // Usually app transitions but quite a load onto the system already (with all the diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index a620a7cfe42b..7ce2b5eb727b 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -24,8 +24,15 @@ import static android.content.pm.ActivityInfo.isFixedOrientationPortrait; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.SurfaceControl.Transaction; +import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; +import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; +import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM; +import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; +import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; +import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowContainerProto.CONFIGURATION_CONTAINER; import static com.android.server.wm.WindowContainerProto.ORIENTATION; import static com.android.server.wm.WindowContainerProto.SURFACE_ANIMATOR; @@ -33,7 +40,9 @@ import static com.android.server.wm.WindowContainerProto.VISIBLE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import static com.android.server.wm.WindowManagerService.logWithStack; import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING; +import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; import android.annotation.CallSuper; import android.annotation.IntDef; @@ -45,17 +54,24 @@ import android.graphics.Point; import android.graphics.Rect; import android.os.Debug; import android.os.IBinder; +import android.os.Trace; +import android.util.Pair; import android.util.Pools; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import android.view.DisplayInfo; import android.view.MagnificationSpec; +import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.SurfaceControl.Builder; import android.view.SurfaceSession; +import android.view.WindowManager; +import android.view.animation.Animation; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ToBooleanFunction; import com.android.server.policy.WindowManagerPolicy; +import com.android.server.protolog.common.ProtoLog; import com.android.server.wm.SurfaceAnimator.Animatable; import java.io.PrintWriter; @@ -155,6 +171,29 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< private final Configuration mTmpConfig = new Configuration(); + /** Interface for {@link #isAnimating} to check which cases for the container is animating. */ + public interface AnimationFlags { + /** + * A bit flag indicates that {@link #isAnimating} should also return {@code true} + * even though the container is not yet animating, but the window container or its + * relatives as specified by PARENTS or CHILDREN are part of an {@link AppTransition} + * that is pending so an animation starts soon. + */ + int TRANSITION = 1; + + /** + * A bit flag indicates that {@link #isAnimating} should also check if one of the + * ancestors of the container are animating in addition to the container itself. + */ + int PARENTS = 2; + + /** + * A bit flag indicates that {@link #isAnimating} should also check if one of the + * descendants of the container are animating in addition to the container itself. + */ + int CHILDREN = 4; + } + /** * Callback which is triggered while changing the parent, after setting up the surface but * before asking the parent to assign child layers. @@ -163,6 +202,47 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< void onPreAssignChildLayers(); } + /** + * True if this an AppWindowToken and the activity which created this was launched with + * ActivityOptions.setLaunchTaskBehind. + * + * TODO(b/142617871): We run a special animation when the activity was launched with that + * flag, but it's not necessary anymore. Keep the window invisible until the task is explicitly + * selected to suppress an animation, and remove this flag. + */ + boolean mLaunchTaskBehind; + + /** + * If we are running an animation, this determines the transition type. Must be one of + * {@link AppTransition#TransitionFlags}. + */ + int mTransit; + + /** + * If we are running an animation, this determines the flags during this animation. Must be a + * bitwise combination of AppTransition.TRANSIT_FLAG_* constants. + */ + int mTransitFlags; + + /** Whether this container should be boosted at the top of all its siblings. */ + @VisibleForTesting boolean mNeedsZBoost; + + /** Layer used to constrain the animation to a container's stack bounds. */ + SurfaceControl mAnimationBoundsLayer; + + /** Whether this container needs to create mAnimationBoundsLayer for cropping animations. */ + boolean mNeedsAnimationBoundsLayer; + + /** + * This gets used during some open/close transitions as well as during a change transition + * where it represents the starting-state snapshot. + */ + AppWindowThumbnail mThumbnail; + final Rect mTransitStartRect = new Rect(); + final Point mTmpPoint = new Point(); + protected final Rect mTmpRect = new Rect(); + final Rect mTmpPrevBounds = new Rect(); + WindowContainer(WindowManagerService wms) { mWmService = wms; mPendingTransaction = wms.mTransactionFactory.get(); @@ -642,51 +722,78 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } /** - * @return Whether our own container is running an animation or any child, no matter how deep in - * the hierarchy, is animating. + * @return {@code true} when this container or its related containers are running an + * animation, {@code false} otherwise. + * + * By default this predicate only checks if this container itself is actually running an + * animation, but you can extend the check target over its relatives, or relax the condition + * so that this can return {@code true} if an animation starts soon by giving a combination + * of {@link #AnimationFlags}. + * + * Note that you can give a combination of bitmask flags to specify targets and condition for + * checking animating status. + * e.g. {@code isAnimating(TRANSITION | PARENT)} returns {@code true} if either this + * container itself or one of its parents is running an animation or waiting for an app + * transition. + * + * Note that TRANSITION propagates to parents and children as well. + * + * {@see AnimationFlags#TRANSITION} + * {@see AnimationFlags#PARENTS} + * {@see AnimationFlags#CHILDREN} */ - boolean isSelfOrChildAnimating() { - if (isSelfAnimating()) { + final boolean isAnimating(int flags) { + if (mSurfaceAnimator.isAnimating()) { return true; } - for (int j = mChildren.size() - 1; j >= 0; j--) { - final WindowContainer wc = mChildren.get(j); - if (wc.isSelfOrChildAnimating()) { + if ((flags & TRANSITION) != 0 && isWaitingForTransitionStart()) { + return true; + } + if ((flags & PARENTS) != 0) { + final WindowContainer parent = getParent(); + if (parent != null && parent.isAnimating(flags & ~CHILDREN)) { return true; } } + if ((flags & CHILDREN) != 0) { + for (int i = 0; i < mChildren.size(); ++i) { + final WindowContainer wc = mChildren.get(i); + if (wc.isAnimating(flags & ~PARENTS)) { + return true; + } + } + } return false; } /** - * @return Whether our own container is running an animation or our parent is animating. This - * doesn't consider whether children are animating. + * @return {@code true} when the container is waiting the app transition start, {@code false} + * otherwise. */ - boolean isAnimating() { - - // We are animating if we ourselves are animating or if our parent is animating. - return isSelfAnimating() || mParent != null && mParent.isAnimating(); + boolean isWaitingForTransitionStart() { + return false; } /** - * @return {@code true} if in this subtree of the hierarchy we have an {@link AppWindowToken} - * that is {@link #isSelfAnimating}; {@code false} otherwise. + * @return {@code true} if in this subtree of the hierarchy we have an + * {@ode ActivityRecord#isAnimating(TRANSITION)}, {@code false} otherwise. */ - boolean isAppAnimating() { - for (int j = mChildren.size() - 1; j >= 0; j--) { - final WindowContainer wc = mChildren.get(j); - if (wc.isAppAnimating()) { - return true; - } - } - return false; + boolean isAppTransitioning() { + return forAllActivities(app -> app.isAnimating(TRANSITION)); } /** * @return Whether our own container running an animation at the moment. */ - boolean isSelfAnimating() { - return mSurfaceAnimator.isAnimating(); + final boolean isAnimating() { + return isAnimating(0 /* self only */); + } + + /** + * @return {@code true} if the container is in changing app transition. + */ + boolean isChangingAppTransition() { + return false; } void sendAppVisibilityToClients() { @@ -988,10 +1095,13 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< wrapper.release(); } - void forAllActivities(Consumer<ActivityRecord> callback) { + boolean forAllActivities(ToBooleanFunction<ActivityRecord> callback) { for (int i = mChildren.size() - 1; i >= 0; --i) { - mChildren.get(i).forAllActivities(callback); + if (mChildren.get(i).forAllActivities(callback)) { + return true; + } } + return false; } void forAllWallpaperWindows(Consumer<WallpaperWindowToken> callback) { @@ -1374,6 +1484,203 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return null; } + // TODO: Remove this and use #getBounds() instead once we set an app transition animation + // on TaskStack. + Rect getAnimationBounds(int appStackClipMode) { + return getBounds(); + } + + /** + * Applies the app transition animation according the given the layout properties in the + * window hierarchy. + * + * @param lp The layout parameters of the window. + * @param transit The app transition type indicates what kind of transition to be applied. + * @param enter Whether the app transition is entering transition or not. + * @param isVoiceInteraction Whether the container is participating in voice interaction or not. + * + * @return {@code true} when the container applied the app transition, {@code false} if the + * app transition is disabled or skipped. + * + * @see #getAnimationAdapter + */ + boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, + boolean isVoiceInteraction) { + if (mWmService.mDisableTransitionAnimation) { + ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, + "applyAnimation: transition animation is disabled or skipped. " + + "container=%s", this); + cancelAnimation(); + return false; + } + + // Only apply an animation if the display isn't frozen. If it is frozen, there is no reason + // to animate and it can cause strange artifacts when we unfreeze the display if some + // different animation is running. + try { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WC#applyAnimation"); + if (okToAnimate()) { + Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp, transit, + enter, isVoiceInteraction); + AnimationAdapter adapter = adapters.first; + AnimationAdapter thumbnailAdapter = adapters.second; + if (adapter != null) { + startAnimation(getPendingTransaction(), adapter, !isVisible()); + if (adapter.getShowWallpaper()) { + mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + } + if (thumbnailAdapter != null) { + mThumbnail.startAnimation( + getPendingTransaction(), thumbnailAdapter, !isVisible()); + } + } + } else { + cancelAnimation(); + } + } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + + return isAnimating(); + } + + /** + * Gets the {@link AnimationAdapter} according the given window layout properties in the window + * hierarchy. + * + * @return The return value will always contain two elements, one for normal animations and the + * other for thumbnail animation, both can be {@code null}. + * + * @See com.android.server.wm.RemoteAnimationController.RemoteAnimationRecord + * @See LocalAnimationAdapter + */ + Pair<AnimationAdapter, AnimationAdapter> getAnimationAdapter(WindowManager.LayoutParams lp, + int transit, boolean enter, boolean isVoiceInteraction) { + final Pair<AnimationAdapter, AnimationAdapter> resultAdapters; + final int appStackClipMode = getDisplayContent().mAppTransition.getAppStackClipMode(); + + // Separate position and size for use in animators. + mTmpRect.set(getAnimationBounds(appStackClipMode)); + mTmpPoint.set(mTmpRect.left, mTmpRect.top); + mTmpRect.offsetTo(0, 0); + + final RemoteAnimationController controller = + getDisplayContent().mAppTransition.getRemoteAnimationController(); + final boolean isChanging = AppTransition.isChangeTransit(transit) && enter + && isChangingAppTransition(); + + // Delaying animation start isn't compatible with remote animations at all. + if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) { + final RemoteAnimationController.RemoteAnimationRecord adapters = + controller.createRemoteAnimationRecord(this, mTmpPoint, mTmpRect, + (isChanging ? mTransitStartRect : null)); + resultAdapters = new Pair<>(adapters.mAdapter, adapters.mThumbnailAdapter); + } else if (isChanging) { + final float durationScale = mWmService.getTransitionAnimationScaleLocked(); + final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo(); + mTmpRect.offsetTo(mTmpPoint.x, mTmpPoint.y); + + AnimationAdapter adapter = new LocalAnimationAdapter( + new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect, displayInfo, + durationScale, true /* isAppAnimation */, false /* isThumbnail */), + getSurfaceAnimationRunner()); + + AnimationAdapter thumbnailAdapter = null; + if (mThumbnail != null) { + thumbnailAdapter = new LocalAnimationAdapter( + new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect, displayInfo, + durationScale, true /* isAppAnimation */, true /* isThumbnail */), + getSurfaceAnimationRunner()); + } + resultAdapters = new Pair<>(adapter, thumbnailAdapter); + mTransit = transit; + mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags(); + } else { + mNeedsAnimationBoundsLayer = (appStackClipMode == STACK_CLIP_AFTER_ANIM); + final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction); + + if (a != null) { + // Only apply corner radius to animation if we're not in multi window mode. + // We don't want rounded corners when in pip or split screen. + final float windowCornerRadius = !inMultiWindowMode() + ? getDisplayContent().getWindowCornerRadius() + : 0; + AnimationAdapter adapter = new LocalAnimationAdapter( + new WindowAnimationSpec(a, mTmpPoint, mTmpRect, + getDisplayContent().mAppTransition.canSkipFirstFrame(), + appStackClipMode, true /* isAppAnimation */, windowCornerRadius), + getSurfaceAnimationRunner()); + + resultAdapters = new Pair<>(adapter, null); + mNeedsZBoost = a.getZAdjustment() == Animation.ZORDER_TOP; + mTransit = transit; + mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags(); + } else { + resultAdapters = new Pair<>(null, null); + } + } + return resultAdapters; + } + + final SurfaceAnimationRunner getSurfaceAnimationRunner() { + return mWmService.mSurfaceAnimationRunner; + } + + private Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, + boolean isVoiceInteraction) { + final DisplayContent displayContent = getDisplayContent(); + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); + final int width = displayInfo.appWidth; + final int height = displayInfo.appHeight; + ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: container=%s", this); + + // Determine the visible rect to calculate the thumbnail clip with + // getAnimationFrames. + final Rect frame = new Rect(0, 0, width, height); + final Rect displayFrame = new Rect(0, 0, + displayInfo.logicalWidth, displayInfo.logicalHeight); + final Rect insets = new Rect(); + final Rect stableInsets = new Rect(); + final Rect surfaceInsets = new Rect(); + getAnimationFrames(frame, insets, stableInsets, surfaceInsets); + + if (mLaunchTaskBehind) { + // Differentiate the two animations. This one which is briefly on the screen + // gets the !enter animation, and the other one which remains on the + // screen gets the enter animation. Both appear in the mOpeningApps set. + enter = false; + } + ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, + "Loading animation for app transition. transit=%s enter=%b frame=%s insets=%s " + + "surfaceInsets=%s", + AppTransition.appTransitionToString(transit), enter, frame, insets, surfaceInsets); + final Configuration displayConfig = displayContent.getConfiguration(); + final Animation a = getDisplayContent().mAppTransition.loadAnimation(lp, transit, enter, + displayConfig.uiMode, displayConfig.orientation, frame, displayFrame, insets, + surfaceInsets, stableInsets, isVoiceInteraction, inFreeformWindowingMode(), this); + if (a != null) { + if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this); + final int containingWidth = frame.width(); + final int containingHeight = frame.height(); + a.initialize(containingWidth, containingHeight, width, height); + a.scaleCurrentDuration(mWmService.getTransitionAnimationScaleLocked()); + } + return a; + } + + RemoteAnimationTarget createRemoteAnimationTarget( + RemoteAnimationController.RemoteAnimationRecord record) { + return null; + } + + boolean okToDisplay() { + return mDisplayContent != null && mDisplayContent.okToDisplay(); + } + + boolean okToAnimate() { + return mDisplayContent != null && mDisplayContent.okToAnimate(); + } + @Override public void commitPendingTransaction() { scheduleAnimation(); @@ -1473,6 +1780,26 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return getBounds(); } + /** + * The {@code outFrame} retrieved by this method specifies where the animation will finish + * the entrance animation, as the next frame will display the window at these coordinates. In + * case of exit animation, this is where the animation will start, as the frame before the + * animation is displaying the window at these bounds. + * + * @param outFrame The bounds where entrance animation finishes or exit animation starts. + * @param outInsets Insets that are covered by system windows. + * @param outStableInsets Insets that determine the area covered by the stable system windows. + * @param outSurfaceInsets Positive insets between the drawing surface and window content. + */ + void getAnimationFrames(Rect outFrame, Rect outInsets, Rect outStableInsets, + Rect outSurfaceInsets) { + final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo(); + outFrame.set(0, 0, displayInfo.appWidth, displayInfo.appHeight); + outInsets.setEmpty(); + outStableInsets.setEmpty(); + outSurfaceInsets.setEmpty(); + } + void getRelativeDisplayedPosition(Point outPos) { final Rect dispBounds = getDisplayedBounds(); outPos.set(dispBounds.left, dispBounds.top); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 085b0f66702a..ab937e0a4e23 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -94,6 +94,9 @@ import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT; import static com.android.server.wm.ProtoLogGroup.WM_ERROR; import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS; +import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; +import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; +import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD; @@ -2418,7 +2421,7 @@ public class WindowManagerService extends IWindowManager.Stub if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) { focusMayChange = true; win.mAnimatingExit = true; - } else if (win.isAnimating()) { + } else if (win.isAnimating(TRANSITION | PARENTS)) { // Currently in a hide animation... turn this into // an exit. win.mAnimatingExit = true; @@ -7618,7 +7621,7 @@ public class WindowManagerService extends IWindowManager.Stub private void waitForAnimationsToComplete() { synchronized (mGlobalLock) { long timeoutRemaining = ANIMATION_COMPLETED_TIMEOUT_MS; - while (mRoot.isSelfOrChildAnimating() && timeoutRemaining > 0) { + while (mRoot.isAnimating(TRANSITION | CHILDREN) && timeoutRemaining > 0) { long startTime = System.currentTimeMillis(); try { mGlobalLock.wait(timeoutRemaining); @@ -7627,7 +7630,7 @@ public class WindowManagerService extends IWindowManager.Stub timeoutRemaining -= (System.currentTimeMillis() - startTime); } - if (mRoot.isSelfOrChildAnimating()) { + if (mRoot.isAnimating(TRANSITION | CHILDREN)) { Log.w(TAG, "Timed out waiting for animations to complete."); } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index f0ea13d2c68a..f7402e1dfea4 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -98,6 +98,8 @@ import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RESIZE; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW; +import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; +import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION; @@ -1492,7 +1494,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override boolean hasContentToDisplay() { if (!mAppFreezing && isDrawnLw() && (mViewVisibility == View.VISIBLE - || (isAnimating() && !getDisplayContent().mAppTransition.isTransitionSet()))) { + || (isAnimating(TRANSITION | PARENTS) + && !getDisplayContent().mAppTransition.isTransitionSet()))) { return true; } @@ -1550,8 +1553,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ // TODO: Can we consolidate this with #isVisible() or have a more appropriate name for this? boolean isWinVisibleLw() { - return (mActivityRecord == null || !mActivityRecord.hiddenRequested || mActivityRecord.isSelfAnimating()) - && isVisible(); + return (mActivityRecord == null || !mActivityRecord.hiddenRequested + || mActivityRecord.isAnimating(TRANSITION)) && isVisible(); } /** @@ -1597,9 +1600,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final ActivityRecord atoken = mActivityRecord; if (atoken != null) { return ((!isParentWindowHidden() && !atoken.hiddenRequested) - || isAnimating()); + || isAnimating(TRANSITION | PARENTS)); } - return !isParentWindowHidden() || isAnimating(); + return !isParentWindowHidden() || isAnimating(TRANSITION | PARENTS); } /** @@ -1634,7 +1637,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final boolean parentAndClientVisible = !isParentWindowHidden() && mViewVisibility == View.VISIBLE && !mToken.isHidden(); return mHasSurface && isVisibleByPolicy() && !mDestroying - && (parentAndClientVisible || isAnimating()); + && (parentAndClientVisible || isAnimating(TRANSITION | PARENTS)); } // TODO: Another visibility method that was added late in the release to minimize risk. @@ -1664,7 +1667,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final ActivityRecord atoken = mActivityRecord; return isDrawnLw() && isVisibleByPolicy() && ((!isParentWindowHidden() && (atoken == null || !atoken.hiddenRequested)) - || isAnimating()); + || isAnimating(TRANSITION | PARENTS)); } /** @@ -1672,7 +1675,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ @Override public boolean isAnimatingLw() { - return isAnimating(); + return isAnimating(TRANSITION | PARENTS); } @Override @@ -1718,7 +1721,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // to determine if it's occluding apps. return ((!mIsWallpaper && mAttrs.format == PixelFormat.OPAQUE) || (mIsWallpaper && mWallpaperVisible)) - && isDrawnLw() && !isAnimating(); + && isDrawnLw() && !isAnimating(TRANSITION | PARENTS); } @Override @@ -1740,7 +1743,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // Starting window that's exiting will be removed when the animation finishes. // Mark all relevant flags for that onExitAnimationDone will proceed all the way // to actually remove it. - if (!visible && isVisibleNow() && mActivityRecord.isSelfAnimating()) { + if (!visible && isVisibleNow() && mActivityRecord.isAnimating(TRANSITION)) { mAnimatingExit = true; mRemoveOnExit = true; mWindowRemovalAllowed = true; @@ -2016,8 +2019,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + "mWillReplaceWindow=%b inPendingTransaction=%b mDisplayFrozen=%b " + "callers=%s", this, mWinAnimator.mSurfaceController, mAnimatingExit, mRemoveOnExit, - mHasSurface, mWinAnimator.getShown(), isAnimating(), - mActivityRecord != null && mActivityRecord.isSelfAnimating(), mWillReplaceWindow, + mHasSurface, mWinAnimator.getShown(), + isAnimating(TRANSITION | PARENTS), + mActivityRecord != null && mActivityRecord.isAnimating(TRANSITION), + mWillReplaceWindow, mActivityRecord != null && mActivityRecord.inPendingTransaction, mWmService.mDisplayFrozen, Debug.getCallers(6)); @@ -2076,7 +2081,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWmService.mAccessibilityController.onWindowTransitionLocked(this, transit); } } - final boolean isAnimating = isAnimating() + final boolean isAnimating = isAnimating(TRANSITION | PARENTS) && (mActivityRecord == null || !mActivityRecord.isWaitingForTransitionStart()); final boolean lastWindowIsStartingWindow = startingWindow && mActivityRecord != null && mActivityRecord.isLastWindow(this); @@ -2683,10 +2688,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this); if (doAnimation) { if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility=" - + isLegacyPolicyVisibility() + " animating=" + isAnimating()); + + isLegacyPolicyVisibility() + + " animating=" + isAnimating(TRANSITION | PARENTS)); if (!mToken.okToAnimate()) { doAnimation = false; - } else if (isLegacyPolicyVisibility() && !isAnimating()) { + } else if (isLegacyPolicyVisibility() && !isAnimating(TRANSITION | PARENTS)) { // Check for the case where we are currently visible and // not animating; we do not want to do animation at such a // point to become visible when we already are. @@ -2726,7 +2732,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } if (doAnimation) { mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false); - if (!isAnimating()) { + if (!isAnimating(TRANSITION | PARENTS)) { doAnimation = false; } } @@ -4163,9 +4169,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + " tok.hiddenRequested=" + (mActivityRecord != null && mActivityRecord.hiddenRequested) + " tok.hidden=" + (mActivityRecord != null && mActivityRecord.isHidden()) - + " animating=" + isAnimating() + + " animating=" + isAnimating(TRANSITION | PARENTS) + " tok animating=" - + (mActivityRecord != null && mActivityRecord.isSelfAnimating()) + + (mActivityRecord != null && mActivityRecord.isAnimating(TRANSITION)) + " Callers=" + Debug.getCallers(4)); } } @@ -4392,7 +4398,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP void onExitAnimationDone() { if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this + ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit - + " selfAnimating=" + isSelfAnimating()); + + " selfAnimating=" + isAnimating()); if (!mChildren.isEmpty()) { // Copying to a different list as multiple children can be removed. @@ -4415,7 +4421,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - if (isSelfAnimating()) { + if (isAnimating()) { return; } if (mWmService.mAccessibilityController != null) { @@ -4563,25 +4569,25 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } if (DEBUG_VISIBILITY) { Slog.v(TAG, "Win " + this + ": isDrawn=" + isDrawnLw() - + ", animating=" + isAnimating()); + + ", animating=" + isAnimating(TRANSITION | PARENTS)); if (!isDrawnLw()) { Slog.v(TAG, "Not displayed: s=" + mWinAnimator.mSurfaceController + " pv=" + isVisibleByPolicy() + " mDrawState=" + mWinAnimator.mDrawState + " ph=" + isParentWindowHidden() + " th=" + (mActivityRecord != null ? mActivityRecord.hiddenRequested : false) - + " a=" + isAnimating()); + + " a=" + isAnimating(TRANSITION | PARENTS)); } } results.numInteresting++; if (isDrawnLw()) { results.numDrawn++; - if (!isAnimating()) { + if (!isAnimating(TRANSITION | PARENTS)) { results.numVisible++; } results.nowGone = false; - } else if (isAnimating()) { + } else if (isAnimating(TRANSITION | PARENTS)) { results.nowGone = false; } } @@ -4718,7 +4724,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + mRemoveOnExit + ", mDestroying=" + mDestroying); // Cancel the existing exit animation for the next enter animation. - if (isSelfAnimating()) { + if (isAnimating()) { cancelAnimation(); destroySurfaceUnchecked(); } @@ -5398,4 +5404,29 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } return mKeyInterceptionInfo; } + + @Override + void getAnimationFrames(Rect outFrame, Rect outInsets, Rect outStableInsets, + Rect outSurfaceInsets) { + // Containing frame will usually cover the whole screen, including dialog windows. + // For freeform workspace windows it will not cover the whole screen and it also + // won't exactly match the final freeform window frame (e.g. when overlapping with + // the status bar). In that case we need to use the final frame. + if (inFreeformWindowingMode()) { + outFrame.set(getFrameLw()); + } else if (isLetterboxedAppWindow()) { + outFrame.set(getTask().getBounds()); + } else if (isDockedResizing()) { + // If we are animating while docked resizing, then use the stack bounds as the + // animation target (which will be different than the task bounds) + outFrame.set(getTask().getParent().getBounds()); + } else { + outFrame.set(getContainingFrame()); + } + outSurfaceInsets.set(getAttrs().surfaceInsets); + // TODO(b/72757033): These are insets relative to the window frame, but we're really + // interested in the insets relative to the frame we chose in the if-blocks above. + getContentInsets(outInsets); + getStableInsets(outStableInsets); + } } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 3f25f89c1a3f..fef3a9d362eb 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -32,6 +32,8 @@ import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW; import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC; import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS; +import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; +import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS; @@ -435,7 +437,7 @@ class WindowStateAnimator { return; } - if (!mWin.mActivityRecord.isSelfAnimating()) { + if (!mWin.mActivityRecord.isAnimating(TRANSITION)) { mWin.mActivityRecord.clearAllDrawn(); } else { // Currently animating, persist current state of allDrawn until animation @@ -1169,7 +1171,7 @@ class WindowStateAnimator { w.mToken.hasVisible = true; } } else { - if (DEBUG_ANIM && mWin.isAnimating()) { + if (DEBUG_ANIM && mWin.isAnimating(TRANSITION | PARENTS)) { Slog.v(TAG, "prepareSurface: No changes in animation for " + this); } displayed = true; @@ -1332,7 +1334,7 @@ class WindowStateAnimator { * @return true if an animation has been loaded. */ boolean applyAnimationLocked(int transit, boolean isEntrance) { - if (mWin.isSelfAnimating() && mAnimationIsEntrance == isEntrance) { + if (mWin.isAnimating() && mAnimationIsEntrance == isEntrance) { // If we are trying to apply an animation, but already running // an animation of the same type, then just leave that one alone. return true; @@ -1400,7 +1402,7 @@ class WindowStateAnimator { mWin.getDisplayContent().adjustForImeIfNeeded(); } - return mWin.isAnimating(); + return mWin.isAnimating(TRANSITION | PARENTS); } void writeToProto(ProtoOutputStream proto, long fieldId) { diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index ad71237bdf4e..88a1458a783f 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -22,6 +22,9 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT; +import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; +import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; +import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; @@ -159,13 +162,10 @@ class WindowToken extends WindowContainer<WindowState> { final int count = mChildren.size(); boolean changed = false; - boolean delayed = false; + final boolean delayed = isAnimating(TRANSITION | PARENTS | CHILDREN); for (int i = 0; i < count; i++) { final WindowState win = mChildren.get(i); - if (win.isAnimating()) { - delayed = true; - } changed |= win.onSetAppExiting(); } @@ -321,14 +321,6 @@ class WindowToken extends WindowContainer<WindowState> { return toString(); } - boolean okToDisplay() { - return mDisplayContent != null && mDisplayContent.okToDisplay(); - } - - boolean okToAnimate() { - return mDisplayContent != null && mDisplayContent.okToAnimate(); - } - /** * Return whether windows from this token can layer above the * system bars, or in other words extend outside of the "Decor Frame" diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp index a8c76827c43c..64c7935efff9 100644 --- a/services/core/jni/com_android_server_VibratorService.cpp +++ b/services/core/jni/com_android_server_VibratorService.cpp @@ -190,7 +190,7 @@ static void vibratorSetExternalControl(JNIEnv*, jclass, jboolean enabled) { } } -static jlong vibratorPerformEffect(JNIEnv* env, jclass, jlong effect, jint strength, +static jlong vibratorPerformEffect(JNIEnv* env, jclass, jlong effect, jlong strength, jobject vibration) { Status status; uint32_t lengthMs; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java index 4a456f720db3..aa38880d6d7d 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java @@ -17,7 +17,6 @@ package com.android.server.devicepolicy; import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; @@ -26,12 +25,9 @@ import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.Resources; -import android.graphics.Color; -import android.os.Build; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; -import android.os.UserManager; import android.os.storage.StorageManager; import android.provider.Settings; import android.security.Credentials; @@ -39,9 +35,9 @@ import android.security.KeyChain; import android.security.KeyChain.KeyChainConnection; import android.util.Log; +import com.android.internal.R; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; -import com.android.internal.R; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -49,7 +45,6 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.List; -import java.util.Set; public class CertificateMonitor { protected static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG; @@ -111,13 +106,13 @@ public class CertificateMonitor { } } - public List<String> getInstalledCaCertificates(UserHandle userHandle) + private List<String> getInstalledCaCertificates(UserHandle userHandle) throws RemoteException, RuntimeException { try (KeyChainConnection conn = mInjector.keyChainBindAsUser(userHandle)) { return conn.getService().getUserCaAliases().getList(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - return null; + throw new RuntimeException(e); } catch (AssertionError e) { throw new RuntimeException(e); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 58830487a0fe..2009dbdca448 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -27,6 +27,7 @@ import static com.android.server.utils.TimingsTraceAndSlog.SYSTEM_SERVER_TIMING_ import android.annotation.NonNull; import android.annotation.StringRes; import android.app.ActivityThread; +import android.app.AppCompatCallbacks; import android.app.INotificationManager; import android.app.usage.UsageStatsManagerInternal; import android.content.ComponentName; @@ -646,6 +647,7 @@ public final class SystemServer { ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE, platformCompat); ServiceManager.addService(Context.PLATFORM_COMPAT_NATIVE_SERVICE, new PlatformCompatNative(platformCompat)); + AppCompatCallbacks.install(new long[0]); t.traceEnd(); // Wait for installd to finish starting up so that it has a chance to @@ -1133,7 +1135,6 @@ public final class SystemServer { StatusBarManagerService statusBar = null; INotificationManager notification = null; - LocationManagerService location = null; CountryDetectorService countryDetector = null; ILockSettings lockSettings = null; MediaRouterService mediaRouter = null; @@ -1430,12 +1431,7 @@ public final class SystemServer { t.traceEnd(); t.traceBegin("StartLocationManagerService"); - try { - location = new LocationManagerService(context); - ServiceManager.addService(Context.LOCATION_SERVICE, location); - } catch (Throwable e) { - reportWtf("starting Location Manager", e); - } + mSystemServiceManager.startService(LocationManagerService.Lifecycle.class); t.traceEnd(); t.traceBegin("StartCountryDetectorService"); @@ -2019,7 +2015,6 @@ public final class SystemServer { final NetworkStatsService networkStatsF = networkStats; final NetworkPolicyManagerService networkPolicyF = networkPolicy; final ConnectivityService connectivityF = connectivity; - final LocationManagerService locationF = location; final CountryDetectorService countryDetectorF = countryDetector; final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater; final InputManagerService inputManagerF = inputManager; @@ -2175,16 +2170,6 @@ public final class SystemServer { } t.traceEnd(); - - t.traceBegin("MakeLocationServiceReady"); - try { - if (locationF != null) { - locationF.systemRunning(); - } - } catch (Throwable e) { - reportWtf("Notifying Location Service running", e); - } - t.traceEnd(); t.traceBegin("MakeCountryDetectionServiceReady"); try { if (countryDetectorF != null) { diff --git a/services/net/Android.bp b/services/net/Android.bp index 08cdbfc55cfe..e24dec562a46 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -1,12 +1,12 @@ java_library_static { name: "services.net", srcs: [ - ":tethering-services-srcs", + ":tethering-servicesnet-srcs", "java/**/*.java", ], static_libs: [ "dnsresolver_aidl_interface-V2-java", - "netd_aidl_interface-java", + "netd_aidl_interface-unstable-java", "networkstack-client", "tethering-client", ], diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java index 9e7b80567263..485f436f7f65 100644 --- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java @@ -38,7 +38,6 @@ import static com.android.server.AlarmManagerService.AlarmHandler.CHARGING_STATU import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_LONG_TIME; import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_SHORT_TIME; import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION; -import static com.android.server.AlarmManagerService.Constants.KEY_APP_STANDBY_QUOTAS_ENABLED; import static com.android.server.AlarmManagerService.Constants.KEY_LISTENER_TIMEOUT; import static com.android.server.AlarmManagerService.Constants.KEY_MAX_INTERVAL; import static com.android.server.AlarmManagerService.Constants.KEY_MIN_FUTURITY; @@ -305,6 +304,8 @@ public class AlarmManagerServiceTest { argThat((filter) -> filter.hasAction(BatteryManager.ACTION_CHARGING) && filter.hasAction(BatteryManager.ACTION_DISCHARGING))); mChargingReceiver = chargingReceiverCaptor.getValue(); + + setTestableQuotas(); } private void setTestAlarm(int type, long triggerTime, PendingIntent operation) { @@ -342,9 +343,10 @@ public class AlarmManagerServiceTest { } /** + * Lowers quotas to make testing feasible. * Careful while calling as this will replace any existing settings for the calling test. */ - private void setQuotasEnabled(boolean enabled) { + private void setTestableQuotas() { final StringBuilder constantsBuilder = new StringBuilder(); constantsBuilder.append(KEY_MIN_FUTURITY); constantsBuilder.append("=0,"); @@ -353,14 +355,9 @@ public class AlarmManagerServiceTest { constantsBuilder.append("=8,"); constantsBuilder.append(mService.mConstants.KEYS_APP_STANDBY_QUOTAS[WORKING_INDEX]); constantsBuilder.append("=5,"); - if (!enabled) { - constantsBuilder.append(KEY_APP_STANDBY_QUOTAS_ENABLED); - constantsBuilder.append("=false,"); - } doReturn(constantsBuilder.toString()).when(() -> Settings.Global.getString(mMockResolver, Settings.Global.ALARM_MANAGER_CONSTANTS)); mService.mConstants.onChange(false, null); - assertEquals(mService.mConstants.APP_STANDBY_QUOTAS_ENABLED, enabled); } @Test @@ -481,67 +478,6 @@ public class AlarmManagerServiceTest { assertEquals(mNowElapsedTest + 9, mTestTimer.getElapsed()); } - @Test - public void testStandbyBucketDelay_workingSet() throws Exception { - setQuotasEnabled(false); - setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent()); - setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent()); - assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed()); - - when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), - anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET); - - mNowElapsedTest = mTestTimer.getElapsed(); - mTestTimer.expire(); - - verify(mUsageStatsManagerInternal, atLeastOnce()) - .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), - eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong()); - final long expectedNextTrigger = mNowElapsedTest - + mService.getMinDelayForBucketLocked(STANDBY_BUCKET_WORKING_SET); - assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed()); - } - - @Test - public void testStandbyBucketDelay_frequent() throws Exception { - setQuotasEnabled(false); - setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent()); - setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent()); - assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed()); - - when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), - anyLong())).thenReturn(STANDBY_BUCKET_FREQUENT); - mNowElapsedTest = mTestTimer.getElapsed(); - mTestTimer.expire(); - - verify(mUsageStatsManagerInternal, atLeastOnce()) - .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), - eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong()); - final long expectedNextTrigger = mNowElapsedTest - + mService.getMinDelayForBucketLocked(STANDBY_BUCKET_FREQUENT); - assertEquals("Incorrect next alarm trigger.", expectedNextTrigger, mTestTimer.getElapsed()); - } - - @Test - public void testStandbyBucketDelay_rare() throws Exception { - setQuotasEnabled(false); - setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent()); - setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent()); - assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed()); - - when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), - anyLong())).thenReturn(STANDBY_BUCKET_RARE); - mNowElapsedTest = mTestTimer.getElapsed(); - mTestTimer.expire(); - - verify(mUsageStatsManagerInternal, atLeastOnce()) - .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), - eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong()); - final long expectedNextTrigger = mNowElapsedTest - + mService.getMinDelayForBucketLocked(STANDBY_BUCKET_RARE); - assertEquals("Incorrect next alarm trigger.", expectedNextTrigger, mTestTimer.getElapsed()); - } - private void testQuotasDeferralOnSet(int standbyBucket) throws Exception { final int quota = mService.getQuotaForBucketLocked(standbyBucket); when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), @@ -601,73 +537,61 @@ public class AlarmManagerServiceTest { @Test public void testActiveQuota_deferredOnSet() throws Exception { - setQuotasEnabled(true); testQuotasDeferralOnSet(STANDBY_BUCKET_ACTIVE); } @Test public void testActiveQuota_deferredOnExpiration() throws Exception { - setQuotasEnabled(true); testQuotasDeferralOnExpiration(STANDBY_BUCKET_ACTIVE); } @Test public void testActiveQuota_notDeferred() throws Exception { - setQuotasEnabled(true); testQuotasNoDeferral(STANDBY_BUCKET_ACTIVE); } @Test public void testWorkingQuota_deferredOnSet() throws Exception { - setQuotasEnabled(true); testQuotasDeferralOnSet(STANDBY_BUCKET_WORKING_SET); } @Test public void testWorkingQuota_deferredOnExpiration() throws Exception { - setQuotasEnabled(true); testQuotasDeferralOnExpiration(STANDBY_BUCKET_WORKING_SET); } @Test public void testWorkingQuota_notDeferred() throws Exception { - setQuotasEnabled(true); testQuotasNoDeferral(STANDBY_BUCKET_WORKING_SET); } @Test public void testFrequentQuota_deferredOnSet() throws Exception { - setQuotasEnabled(true); testQuotasDeferralOnSet(STANDBY_BUCKET_FREQUENT); } @Test public void testFrequentQuota_deferredOnExpiration() throws Exception { - setQuotasEnabled(true); testQuotasDeferralOnExpiration(STANDBY_BUCKET_FREQUENT); } @Test public void testFrequentQuota_notDeferred() throws Exception { - setQuotasEnabled(true); testQuotasNoDeferral(STANDBY_BUCKET_FREQUENT); } @Test public void testRareQuota_deferredOnSet() throws Exception { - setQuotasEnabled(true); testQuotasDeferralOnSet(STANDBY_BUCKET_RARE); } @Test public void testRareQuota_deferredOnExpiration() throws Exception { - setQuotasEnabled(true); testQuotasDeferralOnExpiration(STANDBY_BUCKET_RARE); } @Test public void testRareQuota_notDeferred() throws Exception { - setQuotasEnabled(true); testQuotasNoDeferral(STANDBY_BUCKET_RARE); } @@ -686,7 +610,6 @@ public class AlarmManagerServiceTest { @Test public void testQuotaDowngrade() throws Exception { - setQuotasEnabled(true); final int workingQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_WORKING_SET); when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET); @@ -714,7 +637,6 @@ public class AlarmManagerServiceTest { @Test public void testQuotaUpgrade() throws Exception { - setQuotasEnabled(true); final int frequentQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_FREQUENT); when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), anyLong())).thenReturn(STANDBY_BUCKET_FREQUENT); @@ -752,7 +674,6 @@ public class AlarmManagerServiceTest { @Test public void testCharging() throws Exception { - setQuotasEnabled(true); final int workingQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_WORKING_SET); when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET); diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 83e20fb011b0..30ccb717e8a2 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -75,7 +75,7 @@ android_test { "libui", "libunwindstack", "libutils", - "netd_aidl_interface-V2-cpp", + "netd_aidl_interface-cpp", ], dxflags: ["--multi-dex"], diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml index 4d653b99439c..d34f78353e71 100644 --- a/services/tests/servicestests/AndroidTest.xml +++ b/services/tests/servicestests/AndroidTest.xml @@ -23,6 +23,7 @@ <option name="test-file-name" value="JobTestApp.apk" /> <option name="test-file-name" value="ConnTestApp.apk" /> <option name="test-file-name" value="SuspendTestApp.apk" /> + <option name="test-file-name" value="SimpleServiceTestApp.apk" /> </target_preparer> <option name="test-tag" value="FrameworksServicesTests" /> diff --git a/services/tests/servicestests/src/com/android/server/GnssManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/GnssManagerServiceTest.java new file mode 100644 index 000000000000..9692c257079d --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/GnssManagerServiceTest.java @@ -0,0 +1,743 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import static com.google.common.truth.Truth.assertThat; + +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.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertThrows; + +import android.Manifest; +import android.app.ActivityManager; +import android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.PackageManager; +import android.location.GnssClock; +import android.location.GnssMeasurementCorrections; +import android.location.GnssMeasurementsEvent; +import android.location.GnssNavigationMessage; +import android.location.GnssSingleSatCorrection; +import android.location.IBatchedLocationCallback; +import android.location.IGnssMeasurementsListener; +import android.location.IGnssNavigationMessageListener; +import android.location.IGnssStatusListener; +import android.location.INetInitiatedListener; +import android.location.Location; +import android.os.Handler; +import android.os.IBinder; +import android.os.IInterface; +import android.os.Message; +import android.os.RemoteException; + +import com.android.server.location.GnssBatchingProvider; +import com.android.server.location.GnssCapabilitiesProvider; +import com.android.server.location.GnssLocationProvider; +import com.android.server.location.GnssMeasurementCorrectionsProvider; +import com.android.server.location.GnssMeasurementsProvider; +import com.android.server.location.GnssMeasurementsProvider.GnssMeasurementProviderNative; +import com.android.server.location.GnssNavigationMessageProvider; +import com.android.server.location.GnssNavigationMessageProvider.GnssNavigationMessageProviderNative; +import com.android.server.location.GnssStatusListenerHelper; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.AdditionalMatchers; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; + +import java.util.Arrays; +import java.util.List; + +/** + * Unit tests for {@link com.android.server.GnssManagerService}. + */ +public class GnssManagerServiceTest { + + // Gnss Providers + @Mock + private GnssLocationProvider mMockGnssLocationProvider; + @Mock + private GnssBatchingProvider mMockGnssBatchingProvider; + @Mock + private GnssLocationProvider.GnssSystemInfoProvider mMockGnssSystemInfoProvider; + @Mock + private GnssCapabilitiesProvider mMockGnssCapabilitiesProvider; + @Mock + private GnssMeasurementCorrectionsProvider mMockGnssMeasurementCorrectionsProvider; + @Mock + private INetInitiatedListener mMockNetInitiatedListener; + private GnssMeasurementsProvider mTestGnssMeasurementsProvider; + private GnssStatusListenerHelper mTestGnssStatusProvider; + private GnssNavigationMessageProvider mTestGnssNavigationMessageProvider; + + // Managers and services + @Mock + private AppOpsManager mMockAppOpsManager; + @Mock + private ActivityManager mMockActivityManager; + @Mock + private LocationManagerService mMockLocationManagerService; + + // Context and handler + @Mock + private Handler mMockHandler; + @Mock + private Context mMockContext; + + // Class under test + private GnssManagerService mGnssManagerService; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + GnssLocationProvider.setIsSupportedForTest(true); + + // Set up mock context + when(mMockContext.getSystemServiceName(AppOpsManager.class)).thenReturn( + Context.APP_OPS_SERVICE); + when(mMockContext.getSystemServiceName(ActivityManager.class)).thenReturn( + Context.ACTIVITY_SERVICE); + when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn( + mMockAppOpsManager); + when(mMockContext.getSystemService( + eq(Context.ACTIVITY_SERVICE))).thenReturn( + mMockActivityManager); + enableLocationPermissions(); + + // Mock Handler will execute posted runnables immediately + when(mMockHandler.sendMessageAtTime(any(Message.class), anyLong())).thenAnswer( + (InvocationOnMock invocation) -> { + Message msg = (Message) (invocation.getArguments()[0]); + msg.getCallback().run(); + return null; + }); + + // Setup providers + mTestGnssMeasurementsProvider = createGnssMeasurementsProvider( + mMockContext, mMockHandler); + mTestGnssStatusProvider = createGnssStatusListenerHelper( + mMockContext, mMockHandler); + mTestGnssNavigationMessageProvider = createGnssNavigationMessageProvider( + mMockContext, mMockHandler); + + // Setup GnssLocationProvider to return providers + when(mMockGnssLocationProvider.getGnssStatusProvider()).thenReturn( + mTestGnssStatusProvider); + when(mMockGnssLocationProvider.getGnssBatchingProvider()).thenReturn( + mMockGnssBatchingProvider); + when(mMockGnssLocationProvider.getGnssCapabilitiesProvider()).thenReturn( + mMockGnssCapabilitiesProvider); + when(mMockGnssLocationProvider.getGnssSystemInfoProvider()).thenReturn( + mMockGnssSystemInfoProvider); + when(mMockGnssLocationProvider.getGnssMeasurementCorrectionsProvider()).thenReturn( + mMockGnssMeasurementCorrectionsProvider); + when(mMockGnssLocationProvider.getGnssMeasurementsProvider()).thenReturn( + mTestGnssMeasurementsProvider); + when(mMockGnssLocationProvider.getGnssNavigationMessageProvider()).thenReturn( + mTestGnssNavigationMessageProvider); + when(mMockGnssLocationProvider.getNetInitiatedListener()).thenReturn( + mMockNetInitiatedListener); + + // Setup GnssBatching provider + when(mMockGnssBatchingProvider.start(anyLong(), anyBoolean())).thenReturn(true); + when(mMockGnssBatchingProvider.stop()).thenReturn(true); + + // Create GnssManagerService + mGnssManagerService = new GnssManagerService(mMockLocationManagerService, mMockContext, + mMockGnssLocationProvider, new LocationUsageLogger()); + } + + private void overrideAsBinder(IInterface mockListener) { + IBinder mockBinder = mock(IBinder.class); + when(mockListener.asBinder()).thenReturn(mockBinder); + } + + private IGnssStatusListener createMockGnssStatusListener() { + IGnssStatusListener mockListener = mock(IGnssStatusListener.class); + overrideAsBinder(mockListener); + return mockListener; + } + + private IGnssMeasurementsListener createMockGnssMeasurementsListener() { + IGnssMeasurementsListener mockListener = mock( + IGnssMeasurementsListener.class); + overrideAsBinder(mockListener); + return mockListener; + } + + private IBatchedLocationCallback createMockBatchedLocationCallback() { + IBatchedLocationCallback mockedCallback = mock(IBatchedLocationCallback.class); + overrideAsBinder(mockedCallback); + return mockedCallback; + } + + private IGnssNavigationMessageListener createMockGnssNavigationMessageListener() { + IGnssNavigationMessageListener mockListener = mock(IGnssNavigationMessageListener.class); + overrideAsBinder(mockListener); + return mockListener; + } + + private GnssMeasurementCorrections createDummyGnssMeasurementCorrections() { + GnssSingleSatCorrection gnssSingleSatCorrection = + new GnssSingleSatCorrection.Builder().build(); + return + new GnssMeasurementCorrections.Builder().setSingleSatelliteCorrectionList( + Arrays.asList(gnssSingleSatCorrection)).build(); + } + + private void enableLocationPermissions() { + Mockito.doThrow(new SecurityException()).when( + mMockContext).enforceCallingPermission( + AdditionalMatchers.and( + AdditionalMatchers.not(eq(Manifest.permission.LOCATION_HARDWARE)), + AdditionalMatchers.not(eq(Manifest.permission.ACCESS_FINE_LOCATION))), + anyString()); + when(mMockContext.checkPermission( + eq(android.Manifest.permission.LOCATION_HARDWARE), anyInt(), anyInt())).thenReturn( + PackageManager.PERMISSION_GRANTED); + + // AppOpsManager will return true if OP_FINE_LOCATION is checked + when(mMockAppOpsManager.checkOp(anyInt(), anyInt(), anyString())).thenAnswer( + (InvocationOnMock invocation) -> { + int code = (int) (invocation.getArguments()[0]); + if (code == AppOpsManager.OP_FINE_LOCATION) { + return AppOpsManager.MODE_ALLOWED; + } + return AppOpsManager.MODE_ERRORED; + }); + } + + private void disableLocationPermissions() { + Mockito.doThrow(new SecurityException()).when( + mMockContext).enforceCallingPermission(anyString(), anyString()); + Mockito.doThrow(new SecurityException()).when( + mMockContext).checkPermission(anyString(), anyInt(), anyInt()); + + when(mMockAppOpsManager.checkOp(anyInt(), anyInt(), + anyString())).thenReturn(AppOpsManager.MODE_ERRORED); + } + + private GnssStatusListenerHelper createGnssStatusListenerHelper(Context context, + Handler handler) { + return new GnssStatusListenerHelper( + context, handler) { + @Override + protected boolean isAvailableInPlatform() { + return true; + } + + @Override + protected boolean isGpsEnabled() { + return true; + } + }; + } + + private GnssMeasurementsProvider createGnssMeasurementsProvider(Context context, + Handler handler) { + GnssMeasurementProviderNative + mockGnssMeasurementProviderNative = mock(GnssMeasurementProviderNative.class); + return new GnssMeasurementsProvider( + context, handler, mockGnssMeasurementProviderNative) { + @Override + protected boolean isGpsEnabled() { + return true; + } + }; + } + + private GnssNavigationMessageProvider createGnssNavigationMessageProvider(Context context, + Handler handler) { + GnssNavigationMessageProviderNative mockGnssNavigationMessageProviderNative = mock( + GnssNavigationMessageProviderNative.class); + return new GnssNavigationMessageProvider(context, handler, + mockGnssNavigationMessageProviderNative) { + @Override + protected boolean isGpsEnabled() { + return true; + } + }; + } + + @Test + public void getGnssYearOfHardwareTest() { + final int gnssYearOfHardware = 2012; + when(mMockGnssSystemInfoProvider.getGnssYearOfHardware()).thenReturn(gnssYearOfHardware); + enableLocationPermissions(); + + assertThat(mGnssManagerService.getGnssYearOfHardware()).isEqualTo(gnssYearOfHardware); + } + + @Test + public void getGnssHardwareModelNameTest() { + final String gnssHardwareModelName = "hardwarename"; + when(mMockGnssSystemInfoProvider.getGnssHardwareModelName()).thenReturn( + gnssHardwareModelName); + enableLocationPermissions(); + + assertThat(mGnssManagerService.getGnssHardwareModelName()).isEqualTo( + gnssHardwareModelName); + } + + @Test + public void getGnssCapabilitiesWithoutPermissionsTest() { + disableLocationPermissions(); + + assertThrows(SecurityException.class, + () -> mGnssManagerService.getGnssCapabilities("com.android.server")); + } + + @Test + public void getGnssCapabilitiesWithPermissionsTest() { + final long mGnssCapabilities = 23132L; + when(mMockGnssCapabilitiesProvider.getGnssCapabilities()).thenReturn(mGnssCapabilities); + enableLocationPermissions(); + + assertThat(mGnssManagerService.getGnssCapabilities("com.android.server")).isEqualTo( + mGnssCapabilities); + } + + @Test + public void getGnssBatchSizeWithoutPermissionsTest() { + disableLocationPermissions(); + + assertThrows(SecurityException.class, + () -> mGnssManagerService.getGnssBatchSize("com.android.server")); + } + + @Test + public void getGnssBatchSizeWithPermissionsTest() { + final int gnssBatchSize = 10; + when(mMockGnssBatchingProvider.getBatchSize()).thenReturn(gnssBatchSize); + enableLocationPermissions(); + + assertThat(mGnssManagerService.getGnssBatchSize("com.android.server")).isEqualTo( + gnssBatchSize); + } + + @Test + public void startGnssBatchWithoutPermissionsTest() { + final long periodNanos = 100L; + final boolean wakeOnFifoFull = true; + + disableLocationPermissions(); + + assertThrows(SecurityException.class, + () -> mGnssManagerService.startGnssBatch(periodNanos, wakeOnFifoFull, + "com.android.server")); + verify(mMockGnssBatchingProvider, times(0)).start(periodNanos, wakeOnFifoFull); + } + + @Test + public void startGnssBatchWithPermissionsTest() { + final long periodNanos = 100L; + final boolean wakeOnFifoFull = true; + + enableLocationPermissions(); + + assertThat(mGnssManagerService.startGnssBatch(periodNanos, wakeOnFifoFull, + "com.android.server")) + .isEqualTo( + true); + verify(mMockGnssBatchingProvider, times(1)).start(100L, true); + } + + @Test + public void addGnssBatchCallbackWithoutPermissionsTest() throws RemoteException { + IBatchedLocationCallback mockBatchedLocationCallback = createMockBatchedLocationCallback(); + List<Location> mockLocationList = (List<Location>) mock(List.class); + + disableLocationPermissions(); + + assertThrows(SecurityException.class, () -> mGnssManagerService.addGnssBatchingCallback( + mockBatchedLocationCallback, "com.android.server", "abcd123", + "TestBatchedLocationCallback")); + + mGnssManagerService.onReportLocation(mockLocationList); + + verify(mockBatchedLocationCallback, times(0)).onLocationBatch(mockLocationList); + } + + @Test + public void addGnssBatchCallbackWithPermissionsTest() throws RemoteException { + IBatchedLocationCallback mockBatchedLocationCallback = createMockBatchedLocationCallback(); + List<Location> mockLocationList = (List<Location>) mock(List.class); + + enableLocationPermissions(); + + assertThat(mGnssManagerService.addGnssBatchingCallback( + mockBatchedLocationCallback, "com.android.server", + "abcd123", "TestBatchedLocationCallback")).isEqualTo(true); + + mGnssManagerService.onReportLocation(mockLocationList); + + verify(mockBatchedLocationCallback, times(1)).onLocationBatch(mockLocationList); + } + + @Test + public void replaceGnssBatchCallbackTest() throws RemoteException { + IBatchedLocationCallback mockBatchedLocationCallback1 = createMockBatchedLocationCallback(); + IBatchedLocationCallback mockBatchedLocationCallback2 = createMockBatchedLocationCallback(); + List<Location> mockLocationList = (List<Location>) mock(List.class); + + enableLocationPermissions(); + + assertThat(mGnssManagerService.addGnssBatchingCallback( + mockBatchedLocationCallback1, "com.android.server", + "abcd123", "TestBatchedLocationCallback")).isEqualTo(true); + assertThat(mGnssManagerService.addGnssBatchingCallback( + mockBatchedLocationCallback2, "com.android.server", + "abcd123", "TestBatchedLocationCallback")).isEqualTo(true); + + mGnssManagerService.onReportLocation(mockLocationList); + + verify(mockBatchedLocationCallback1, times(0)).onLocationBatch(mockLocationList); + verify(mockBatchedLocationCallback2, times(1)).onLocationBatch(mockLocationList); + } + + @Test + public void flushGnssBatchWithoutPermissionsTest() { + disableLocationPermissions(); + + assertThrows(SecurityException.class, + () -> mGnssManagerService.flushGnssBatch("com.android.server")); + verify(mMockGnssBatchingProvider, times(0)).flush(); + } + + @Test + public void flushGnssBatchWithPermissionsTest() { + enableLocationPermissions(); + mGnssManagerService.flushGnssBatch("com.android.server"); + + verify(mMockGnssBatchingProvider, times(1)).flush(); + } + + @Test + public void removeGnssBatchingCallbackWithoutPermissionsTest() throws RemoteException { + IBatchedLocationCallback mockBatchedLocationCallback = createMockBatchedLocationCallback(); + List<Location> mockLocationList = (List<Location>) mock(List.class); + + enableLocationPermissions(); + + mGnssManagerService.addGnssBatchingCallback(mockBatchedLocationCallback, + "com.android.server", "abcd123", "TestBatchedLocationCallback"); + + disableLocationPermissions(); + + assertThrows(SecurityException.class, + () -> mGnssManagerService.removeGnssBatchingCallback()); + + mGnssManagerService.onReportLocation(mockLocationList); + + verify(mockBatchedLocationCallback, times(1)).onLocationBatch(mockLocationList); + } + + @Test + public void removeGnssBatchingCallbackWithPermissionsTest() throws RemoteException { + IBatchedLocationCallback mockBatchedLocationCallback = createMockBatchedLocationCallback(); + List<Location> mockLocationList = (List<Location>) mock(List.class); + + enableLocationPermissions(); + + mGnssManagerService.addGnssBatchingCallback(mockBatchedLocationCallback, + "com.android.server", "abcd123", "TestBatchedLocationCallback"); + + mGnssManagerService.removeGnssBatchingCallback(); + + mGnssManagerService.onReportLocation(mockLocationList); + + verify(mockBatchedLocationCallback, times(0)).onLocationBatch(mockLocationList); + } + + @Test + public void stopGnssBatchWithoutPermissionsTest() { + disableLocationPermissions(); + + assertThrows(SecurityException.class, () -> mGnssManagerService.stopGnssBatch()); + verify(mMockGnssBatchingProvider, times(0)).stop(); + } + + @Test + public void stopGnssBatchWithPermissionsTest() { + enableLocationPermissions(); + + assertThat(mGnssManagerService.stopGnssBatch()).isEqualTo(true); + verify(mMockGnssBatchingProvider, times(1)).stop(); + } + + @Test + public void registerGnssStatusCallbackWithoutPermissionsTest() throws RemoteException { + final int timeToFirstFix = 20000; + IGnssStatusListener mockGnssStatusListener = createMockGnssStatusListener(); + + disableLocationPermissions(); + + assertThrows(SecurityException.class, () -> mGnssManagerService + .registerGnssStatusCallback( + mockGnssStatusListener, "com.android.server", "abcd123")); + + mTestGnssStatusProvider.onFirstFix(timeToFirstFix); + + verify(mockGnssStatusListener, times(0)).onFirstFix(timeToFirstFix); + } + + @Test + public void registerGnssStatusCallbackWithPermissionsTest() throws RemoteException { + final int timeToFirstFix = 20000; + IGnssStatusListener mockGnssStatusListener = createMockGnssStatusListener(); + + enableLocationPermissions(); + + assertThat(mGnssManagerService.registerGnssStatusCallback( + mockGnssStatusListener, "com.android.server", "abcd123")).isEqualTo(true); + + mTestGnssStatusProvider.onFirstFix(timeToFirstFix); + + verify(mockGnssStatusListener, times(1)).onFirstFix(timeToFirstFix); + } + + @Test + public void unregisterGnssStatusCallbackWithPermissionsTest() throws RemoteException { + final int timeToFirstFix = 20000; + IGnssStatusListener mockGnssStatusListener = createMockGnssStatusListener(); + + enableLocationPermissions(); + + mGnssManagerService.registerGnssStatusCallback( + mockGnssStatusListener, "com.android.server", "abcd123"); + + mGnssManagerService.unregisterGnssStatusCallback(mockGnssStatusListener); + + mTestGnssStatusProvider.onFirstFix(timeToFirstFix); + + verify(mockGnssStatusListener, times(0)).onFirstFix(timeToFirstFix); + } + + @Test + public void addGnssMeasurementsListenerWithoutPermissionsTest() throws RemoteException { + IGnssMeasurementsListener mockGnssMeasurementsListener = + createMockGnssMeasurementsListener(); + GnssMeasurementsEvent gnssMeasurementsEvent = new GnssMeasurementsEvent(new GnssClock(), + null); + + disableLocationPermissions(); + + assertThrows(SecurityException.class, + () -> mGnssManagerService.addGnssMeasurementsListener( + mockGnssMeasurementsListener, + "com.android.server", "abcd123", "TestGnssMeasurementsListener")); + + mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent); + verify(mockGnssMeasurementsListener, times(0)).onGnssMeasurementsReceived( + gnssMeasurementsEvent); + } + + @Test + public void addGnssMeasurementsListenerWithPermissionsTest() throws RemoteException { + IGnssMeasurementsListener mockGnssMeasurementsListener = + createMockGnssMeasurementsListener(); + GnssMeasurementsEvent gnssMeasurementsEvent = new GnssMeasurementsEvent(new GnssClock(), + null); + + enableLocationPermissions(); + + assertThat(mGnssManagerService.addGnssMeasurementsListener(mockGnssMeasurementsListener, + "com.android.server", "abcd123", "TestGnssMeasurementsListener")).isEqualTo(true); + + mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent); + verify(mockGnssMeasurementsListener, times(1)).onGnssMeasurementsReceived( + gnssMeasurementsEvent); + } + + @Test + public void injectGnssMeasurementCorrectionsWithoutPermissionsTest() { + GnssMeasurementCorrections gnssMeasurementCorrections = + createDummyGnssMeasurementCorrections(); + + disableLocationPermissions(); + + assertThrows(SecurityException.class, + () -> mGnssManagerService.injectGnssMeasurementCorrections( + gnssMeasurementCorrections, "com.android.server")); + verify(mMockGnssMeasurementCorrectionsProvider, times(0)) + .injectGnssMeasurementCorrections( + gnssMeasurementCorrections); + } + + @Test + public void injectGnssMeasurementCorrectionsWithPermissionsTest() { + GnssMeasurementCorrections gnssMeasurementCorrections = + createDummyGnssMeasurementCorrections(); + + enableLocationPermissions(); + + mGnssManagerService.injectGnssMeasurementCorrections( + gnssMeasurementCorrections, "com.android.server"); + verify(mMockGnssMeasurementCorrectionsProvider, times(1)) + .injectGnssMeasurementCorrections( + gnssMeasurementCorrections); + } + + @Test + public void removeGnssMeasurementsListenerWithoutPermissionsTest() throws RemoteException { + IGnssMeasurementsListener mockGnssMeasurementsListener = + createMockGnssMeasurementsListener(); + GnssMeasurementsEvent gnssMeasurementsEvent = new GnssMeasurementsEvent(new GnssClock(), + null); + + enableLocationPermissions(); + + mGnssManagerService.addGnssMeasurementsListener(mockGnssMeasurementsListener, + "com.android.server", "abcd123", "TestGnssMeasurementsListener"); + + disableLocationPermissions(); + + mGnssManagerService.removeGnssMeasurementsListener( + mockGnssMeasurementsListener); + + mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent); + verify(mockGnssMeasurementsListener, times(0)).onGnssMeasurementsReceived( + gnssMeasurementsEvent); + } + + @Test + public void removeGnssMeasurementsListenerWithPermissionsTest() throws RemoteException { + IGnssMeasurementsListener mockGnssMeasurementsListener = + createMockGnssMeasurementsListener(); + GnssMeasurementsEvent gnssMeasurementsEvent = new GnssMeasurementsEvent(new GnssClock(), + null); + + enableLocationPermissions(); + + mGnssManagerService.addGnssMeasurementsListener(mockGnssMeasurementsListener, + "com.android.server", "abcd123", "TestGnssMeasurementsListener"); + + disableLocationPermissions(); + + mGnssManagerService.removeGnssMeasurementsListener( + mockGnssMeasurementsListener); + + mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent); + verify(mockGnssMeasurementsListener, times(0)).onGnssMeasurementsReceived( + gnssMeasurementsEvent); + } + + @Test + public void addGnssNavigationMessageListenerWithoutPermissionsTest() throws RemoteException { + IGnssNavigationMessageListener mockGnssNavigationMessageListener = + createMockGnssNavigationMessageListener(); + GnssNavigationMessage gnssNavigationMessage = new GnssNavigationMessage(); + + disableLocationPermissions(); + + assertThrows(SecurityException.class, + () -> mGnssManagerService.addGnssNavigationMessageListener( + mockGnssNavigationMessageListener, "com.android.server", + "abcd123", "TestGnssNavigationMessageListener")); + + mTestGnssNavigationMessageProvider.onNavigationMessageAvailable(gnssNavigationMessage); + + verify(mockGnssNavigationMessageListener, times(0)).onGnssNavigationMessageReceived( + gnssNavigationMessage); + } + + @Test + public void addGnssNavigationMessageListenerWithPermissionsTest() throws RemoteException { + IGnssNavigationMessageListener mockGnssNavigationMessageListener = + createMockGnssNavigationMessageListener(); + GnssNavigationMessage gnssNavigationMessage = new GnssNavigationMessage(); + + enableLocationPermissions(); + + assertThat(mGnssManagerService.addGnssNavigationMessageListener( + mockGnssNavigationMessageListener, "com.android.server", + "abcd123", "TestGnssNavigationMessageListener")).isEqualTo(true); + + mTestGnssNavigationMessageProvider.onNavigationMessageAvailable(gnssNavigationMessage); + + verify(mockGnssNavigationMessageListener, times(1)).onGnssNavigationMessageReceived( + gnssNavigationMessage); + } + + @Test + public void removeGnssNavigationMessageListenerWithoutPermissionsTest() throws RemoteException { + IGnssNavigationMessageListener mockGnssNavigationMessageListener = + createMockGnssNavigationMessageListener(); + GnssNavigationMessage gnssNavigationMessage = new GnssNavigationMessage(); + + enableLocationPermissions(); + + mGnssManagerService.addGnssNavigationMessageListener( + mockGnssNavigationMessageListener, "com.android.server", + "abcd123", "TestGnssNavigationMessageListener"); + + disableLocationPermissions(); + + mGnssManagerService.removeGnssNavigationMessageListener( + mockGnssNavigationMessageListener); + + mTestGnssNavigationMessageProvider.onNavigationMessageAvailable(gnssNavigationMessage); + + verify(mockGnssNavigationMessageListener, times(0)).onGnssNavigationMessageReceived( + gnssNavigationMessage); + } + + @Test + public void removeGnssNavigationMessageListenerWithPermissionsTest() throws RemoteException { + IGnssNavigationMessageListener mockGnssNavigationMessageListener = + createMockGnssNavigationMessageListener(); + GnssNavigationMessage gnssNavigationMessage = new GnssNavigationMessage(); + + enableLocationPermissions(); + + mGnssManagerService.addGnssNavigationMessageListener( + mockGnssNavigationMessageListener, "com.android.server", + "abcd123", "TestGnssNavigationMessageListener"); + + mGnssManagerService.removeGnssNavigationMessageListener( + mockGnssNavigationMessageListener); + + mTestGnssNavigationMessageProvider.onNavigationMessageAvailable(gnssNavigationMessage); + + verify(mockGnssNavigationMessageListener, times(0)).onGnssNavigationMessageReceived( + gnssNavigationMessage); + } + + @Test + public void sendNiResponseWithPermissionsTest() throws RemoteException { + when(mMockNetInitiatedListener.sendNiResponse(anyInt(), anyInt())).thenReturn(true); + + int notifId = 0; + int userResponse = 0; + enableLocationPermissions(); + + assertThat(mGnssManagerService.sendNiResponse(notifId, userResponse)).isEqualTo(true); + + verify(mMockNetInitiatedListener, times(1)).sendNiResponse(notifId, userResponse); + } +} diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java index 1ad7b6e8d155..79af34d80686 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java @@ -191,7 +191,7 @@ public class NetworkScoreServiceTest { @After public void tearDown() throws Exception { mHandlerThread.quitSafely(); - LocalServices.removeServiceForTest(PackageManagerInternal.class); + LocalServices.removeServiceForTest(PermissionManagerServiceInternal.class); } @Test diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java index 8965152257c6..129d2633ae92 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java @@ -19,20 +19,34 @@ package com.android.server.am; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import android.app.ActivityManager; import android.app.ActivityManager.RecentTaskInfo; import android.app.IActivityManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.os.IBinder; +import android.os.IRemoteCallback; import android.os.RemoteException; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; +import android.support.test.uiautomator.UiDevice; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.FlakyTest; import org.junit.Before; import org.junit.Test; +import java.io.IOException; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * Tests for {@link ActivityManager}. @@ -43,12 +57,22 @@ import java.util.List; @FlakyTest(detail = "Promote to presubmit if stable") @Presubmit public class ActivityManagerTest { + private static final String TAG = "ActivityManagerTest"; + + private static final String TEST_APP = "com.android.servicestests.apps.simpleservicetestapp"; + private static final String TEST_CLASS = TEST_APP + ".SimpleService"; + private static final int TEST_LOOPS = 100; + private static final long AWAIT_TIMEOUT = 2000; + private static final long CHECK_INTERVAL = 100; private IActivityManager mService; + private IRemoteCallback mCallback; + private Context mContext; @Before public void setUp() throws Exception { mService = ActivityManager.getService(); + mContext = InstrumentationRegistry.getTargetContext(); } @Test @@ -72,4 +96,112 @@ public class ActivityManagerTest { } } } + + @Test + public void testServiceUnbindAndKilling() { + for (int i = TEST_LOOPS; i > 0; i--) { + runOnce(i); + } + } + + private void runOnce(long yieldDuration) { + final PackageManager pm = mContext.getPackageManager(); + int uid = 0; + try { + uid = pm.getPackageUid(TEST_APP, 0); + } catch (PackageManager.NameNotFoundException e) { + throw new RuntimeException(e); + } + + Intent intent = new Intent(); + intent.setClassName(TEST_APP, TEST_CLASS); + + // Create a service connection with auto creation. + CountDownLatch latch = new CountDownLatch(1); + final MyServiceConnection autoConnection = new MyServiceConnection(latch); + mContext.bindService(intent, autoConnection, Context.BIND_AUTO_CREATE); + try { + assertTrue("Timeout to bind to service " + intent.getComponent(), + latch.await(AWAIT_TIMEOUT, TimeUnit.MILLISECONDS)); + } catch (InterruptedException e) { + fail("Unable to bind to service " + intent.getComponent()); + } + + // Create a service connection without any flags. + intent = new Intent(); + intent.setClassName(TEST_APP, TEST_CLASS); + latch = new CountDownLatch(1); + MyServiceConnection otherConnection = new MyServiceConnection(latch); + mContext.bindService(intent, otherConnection, 0); + try { + assertTrue("Timeout to bind to service " + intent.getComponent(), + latch.await(AWAIT_TIMEOUT, TimeUnit.MILLISECONDS)); + } catch (InterruptedException e) { + fail("Unable to bind to service " + intent.getComponent()); + } + + // Inform the remote process to kill itself + try { + mCallback.sendResult(null); + // It's basically a test for race condition, we expect the bringDownServiceLocked() + // would find out the hosting process is dead - to do this, technically we should + // do killing and unbinding simultaneously; but in reality, the killing would take + // a little while, before the signal really kills it; so we do it in the same thread, + // and even wait a while after sending killing signal. + Thread.sleep(yieldDuration); + } catch (RemoteException | InterruptedException e) { + fail("Unable to kill the process"); + } + // Now unbind that auto connection, this should be equivalent to stopService + mContext.unbindService(autoConnection); + + // Now we don't expect the system_server crashes. + + // Wait for the target process dies + long total = 0; + for (; total < AWAIT_TIMEOUT; total += CHECK_INTERVAL) { + try { + if (!targetPackageIsRunning(mContext, uid)) { + break; + } + Thread.sleep(CHECK_INTERVAL); + } catch (InterruptedException e) { + } + } + assertTrue("Timeout to wait for the target package dies", total < AWAIT_TIMEOUT); + mCallback = null; + } + + private boolean targetPackageIsRunning(Context context, int uid) { + final String result = runShellCommand( + String.format("cmd activity get-uid-state %d", uid)); + return !result.contains("(NONEXISTENT)"); + } + + private static String runShellCommand(String cmd) { + try { + return UiDevice.getInstance( + InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private class MyServiceConnection implements ServiceConnection { + private CountDownLatch mLatch; + + MyServiceConnection(CountDownLatch latch) { + this.mLatch = latch; + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + mCallback = IRemoteCallback.Stub.asInterface(service); + mLatch.countDown(); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + } + } } diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java index 29a8dada7d89..5c2ad94720f0 100644 --- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java @@ -126,22 +126,6 @@ public class AudioDeviceBrokerTest { doTestConnectionDisconnectionReconnection(AudioService.BECOMING_NOISY_DELAY_MS / 2); } - /** - * Verify connecting an A2DP sink will call into AudioService to unmute media - */ - @Test - public void testA2dpConnectionUnmutesMedia() throws Exception { - Log.i(TAG, "testA2dpConnectionUnmutesMedia"); - Assert.assertNotNull("invalid null BT device", mFakeBtDevice); - - mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice, - BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1); - Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); - verify(mMockAudioService, times(1)).postAccessoryPlugMediaUnmute( - ArgumentMatchers.eq(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)); - - } - private void doTestConnectionDisconnectionReconnection(int delayAfterDisconnection) throws Exception { when(mMockAudioService.getDeviceForStream(AudioManager.STREAM_MUSIC)) diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index 46db97834dae..64bd2c7a4095 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -143,6 +143,7 @@ import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; +import com.android.server.usage.AppStandbyInternal; import com.google.common.util.concurrent.AbstractFuture; @@ -297,6 +298,7 @@ public class NetworkPolicyManagerServiceTest { private void registerLocalServices() { addLocalServiceMock(DeviceIdleInternal.class); + addLocalServiceMock(AppStandbyInternal.class); final UsageStatsManagerInternal usageStats = addLocalServiceMock(UsageStatsManagerInternal.class); @@ -446,6 +448,7 @@ public class NetworkPolicyManagerServiceTest { LocalServices.removeServiceForTest(ActivityManagerInternal.class); LocalServices.removeServiceForTest(PowerManagerInternal.class); LocalServices.removeServiceForTest(DeviceIdleInternal.class); + LocalServices.removeServiceForTest(AppStandbyInternal.class); LocalServices.removeServiceForTest(UsageStatsManagerInternal.class); LocalServices.removeServiceForTest(NetworkStatsManagerInternal.class); } diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java index df0c37a9856c..e32103fe6bff 100644 --- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java +++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java @@ -21,6 +21,7 @@ import static android.app.usage.UsageEvents.Event.MAX_EVENT_TYPE; import static junit.framework.TestCase.fail; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; import android.app.usage.TimeSparseArray; import android.app.usage.UsageEvents.Event; @@ -35,6 +36,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -423,6 +425,11 @@ public class UsageStatsDatabaseTest { prevDB.putUsageStats(UsageStatsManager.INTERVAL_DAILY, mIntervalStats); // Create a backup with a specific version byte[] blob = prevDB.getBackupPayload(KEY_USAGE_STATS, version); + if (version >= 1 && version <= 3) { + assertFalse(blob != null && blob.length != 0, + "UsageStatsDatabase shouldn't be able to write backups as XML"); + return; + } clearUsageStatsFiles(); @@ -434,11 +441,9 @@ public class UsageStatsDatabaseTest { List<IntervalStats> stats = newDB.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, 0, mEndTime, mIntervalStatsVerifier); - - if (version > newDB.BACKUP_VERSION || version < 1) { - if (stats != null && stats.size() != 0) { - fail("UsageStatsDatabase should ne be able to restore from unknown data versions"); - } + if (version > UsageStatsDatabase.BACKUP_VERSION || version < 1) { + assertFalse(stats != null && !stats.isEmpty(), + "UsageStatsDatabase shouldn't be able to restore from unknown data versions"); return; } @@ -455,9 +460,12 @@ public class UsageStatsDatabaseTest { /** * Test the version upgrade from 3 to 4 + * + * Ignored - version 3 is now deprecated. */ + @Ignore @Test - public void testVersionUpgradeFrom3to4() throws IOException { + public void ignore_testVersionUpgradeFrom3to4() throws IOException { runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_DAILY); runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_WEEKLY); runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_MONTHLY); @@ -477,9 +485,12 @@ public class UsageStatsDatabaseTest { /** * Test the version upgrade from 3 to 5 + * + * Ignored - version 3 is now deprecated. */ + @Ignore @Test - public void testVersionUpgradeFrom3to5() throws IOException { + public void ignore_testVersionUpgradeFrom3to5() throws IOException { runVersionChangeTest(3, 5, UsageStatsManager.INTERVAL_DAILY); runVersionChangeTest(3, 5, UsageStatsManager.INTERVAL_WEEKLY); runVersionChangeTest(3, 5, UsageStatsManager.INTERVAL_MONTHLY); @@ -488,13 +499,15 @@ public class UsageStatsDatabaseTest { /** - * Test the version upgrade from 3 to 4 + * Test backup/restore */ @Test public void testBackupRestore() throws IOException { - runBackupRestoreTest(1); runBackupRestoreTest(4); + // test deprecated versions + runBackupRestoreTest(1); + // test invalid backup versions as well runBackupRestoreTest(0); runBackupRestoreTest(99999); diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp b/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp new file mode 100644 index 000000000000..5cbd39c39434 --- /dev/null +++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp @@ -0,0 +1,30 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +android_test_helper_app { + name: "SimpleServiceTestApp", + + test_suites: ["device-tests"], + + srcs: ["**/*.java"], + + platform_apis: true, + certificate: "platform", + dex_preopt: { + enabled: false, + }, + optimize: { + enabled: false, + }, +} diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml new file mode 100644 index 000000000000..8789992280d0 --- /dev/null +++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.servicestests.apps.simpleservicetestapp"> + + <application> + <service android:name=".SimpleService" + android:exported="true" /> + </application> + +</manifest> diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java new file mode 100644 index 000000000000..75f71d609a13 --- /dev/null +++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java @@ -0,0 +1,37 @@ +/* + * 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.servicestests.apps.simpleservicetestapp; + +import android.app.Service; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; +import android.os.IRemoteCallback; +import android.os.Process; + +public class SimpleService extends Service { + private final IRemoteCallback.Stub mBinder = new IRemoteCallback.Stub() { + @Override + public void sendResult(Bundle bundle) { + Process.killProcess(Process.myPid()); + } + }; + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } +} diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp index 4a9cef1f1cbd..92198fa8cb0c 100644 --- a/services/tests/uiservicestests/Android.bp +++ b/services/tests/uiservicestests/Android.bp @@ -55,6 +55,6 @@ android_test { "libui", "libunwindstack", "libutils", - "netd_aidl_interface-V2-cpp", + "netd_aidl_interface-cpp", ], } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java new file mode 100644 index 000000000000..bcff2f81f805 --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java @@ -0,0 +1,184 @@ +/* + * 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.notification; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.NotificationHistory.HistoricalNotification; +import android.graphics.drawable.Icon; +import android.os.Handler; +import android.util.AtomicFile; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.UiServiceTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.File; +import java.util.Calendar; +import java.util.GregorianCalendar; + +@RunWith(AndroidJUnit4.class) +public class NotificationHistoryDatabaseTest extends UiServiceTestCase { + + File mRootDir; + @Mock + Handler mFileWriteHandler; + + NotificationHistoryDatabase mDataBase; + + private HistoricalNotification getHistoricalNotification(int index) { + return getHistoricalNotification("package" + index, index); + } + + private HistoricalNotification getHistoricalNotification(String packageName, int index) { + String expectedChannelName = "channelName" + index; + String expectedChannelId = "channelId" + index; + int expectedUid = 1123456 + index; + int expectedUserId = 11 + index; + long expectedPostTime = 987654321 + index; + String expectedTitle = "title" + index; + String expectedText = "text" + index; + Icon expectedIcon = Icon.createWithResource(InstrumentationRegistry.getContext(), + index); + + return new HistoricalNotification.Builder() + .setPackage(packageName) + .setChannelName(expectedChannelName) + .setChannelId(expectedChannelId) + .setUid(expectedUid) + .setUserId(expectedUserId) + .setPostedTimeMs(expectedPostTime) + .setTitle(expectedTitle) + .setText(expectedText) + .setIcon(expectedIcon) + .build(); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mRootDir = new File(mContext.getFilesDir(), "NotificationHistoryDatabaseTest"); + + mDataBase = new NotificationHistoryDatabase(mRootDir); + mDataBase.init(mFileWriteHandler); + } + + @Test + public void testPrune() { + int retainDays = 1; + for (long i = 10; i >= 5; i--) { + File file = mock(File.class); + when(file.lastModified()).thenReturn(i); + AtomicFile af = new AtomicFile(file); + mDataBase.mHistoryFiles.addLast(af); + } + GregorianCalendar cal = new GregorianCalendar(); + cal.setTimeInMillis(5); + cal.add(Calendar.DATE, -1 * retainDays); + for (int i = 5; i >= 0; i--) { + File file = mock(File.class); + when(file.lastModified()).thenReturn(cal.getTimeInMillis() - i); + AtomicFile af = new AtomicFile(file); + mDataBase.mHistoryFiles.addLast(af); + } + mDataBase.prune(retainDays, 10); + + for (AtomicFile file : mDataBase.mHistoryFiles) { + assertThat(file.getBaseFile().lastModified() > 0); + } + } + + @Test + public void testOnPackageRemove_posts() { + mDataBase.onPackageRemoved("test"); + verify(mFileWriteHandler, times(1)).post(any()); + } + + @Test + public void testForceWriteToDisk() { + mDataBase.forceWriteToDisk(); + verify(mFileWriteHandler, times(1)).post(any()); + } + + @Test + public void testOnlyOneWriteRunnableInQueue() { + when(mFileWriteHandler.hasCallbacks(any())).thenReturn(true); + mDataBase.forceWriteToDisk(); + verify(mFileWriteHandler, never()).post(any()); + } + + @Test + public void testAddNotification() { + HistoricalNotification n = getHistoricalNotification(1); + HistoricalNotification n2 = getHistoricalNotification(2); + + mDataBase.addNotification(n); + assertThat(mDataBase.mBuffer.getNotificationsToWrite()).contains(n); + verify(mFileWriteHandler, times(1)).postDelayed(any(), anyLong()); + + // second add should not trigger another write + mDataBase.addNotification(n2); + assertThat(mDataBase.mBuffer.getNotificationsToWrite()).contains(n2); + verify(mFileWriteHandler, times(1)).postDelayed(any(), anyLong()); + } + + @Test + public void testReadNotificationHistory_readsAllFiles() throws Exception { + for (long i = 10; i >= 5; i--) { + AtomicFile af = mock(AtomicFile.class); + mDataBase.mHistoryFiles.addLast(af); + } + + mDataBase.readNotificationHistory(); + + for (AtomicFile file : mDataBase.mHistoryFiles) { + verify(file, times(1)).openRead(); + } + } + + @Test + public void testReadNotificationHistory_withNumFilterDoesNotReadExtraFiles() throws Exception { + AtomicFile af = mock(AtomicFile.class); + when(af.getBaseFile()).thenReturn(new File(mRootDir, "af")); + mDataBase.mHistoryFiles.addLast(af); + + AtomicFile af2 = mock(AtomicFile.class); + when(af2.getBaseFile()).thenReturn(new File(mRootDir, "af2")); + mDataBase.mHistoryFiles.addLast(af2); + + mDataBase.readNotificationHistory(null, null, 0); + + verify(af, times(1)).openRead(); + verify(af2, never()).openRead(); + } + +} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryFilterTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryFilterTest.java new file mode 100644 index 000000000000..10bfcf12c89f --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryFilterTest.java @@ -0,0 +1,159 @@ +/* + * 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.notification; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.NotificationHistory; +import android.app.NotificationHistory.HistoricalNotification; +import android.graphics.drawable.Icon; +import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.UiServiceTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class NotificationHistoryFilterTest extends UiServiceTestCase { + + private HistoricalNotification getHistoricalNotification(int index) { + return getHistoricalNotification("package" + index, "channelId" + index, index); + } + private HistoricalNotification getHistoricalNotification(String pkg, int index) { + return getHistoricalNotification(pkg, "channelId" + index, index); + } + + private HistoricalNotification getHistoricalNotification(String packageName, String channelId, + int index) { + String expectedChannelName = "channelName" + index; + int expectedUid = 1123456 + index; + int expectedUserId = 11 + index; + long expectedPostTime = 987654321 + index; + String expectedTitle = "title" + index; + String expectedText = "text" + index; + Icon expectedIcon = Icon.createWithResource(InstrumentationRegistry.getContext(), + index); + + return new HistoricalNotification.Builder() + .setPackage(packageName) + .setChannelName(expectedChannelName) + .setChannelId(channelId) + .setUid(expectedUid) + .setUserId(expectedUserId) + .setPostedTimeMs(expectedPostTime) + .setTitle(expectedTitle) + .setText(expectedText) + .setIcon(expectedIcon) + .build(); + } + + @Test + public void testBuilder() { + NotificationHistoryFilter filter = new NotificationHistoryFilter.Builder() + .setChannel("pkg", "channel") + .setMaxNotifications(3) + .build(); + + assertThat(filter.getPackage()).isEqualTo("pkg"); + assertThat(filter.getChannel()).isEqualTo("channel"); + assertThat(filter.getMaxNotifications()).isEqualTo(3); + } + + @Test + public void testMatchesCountFilter() { + NotificationHistoryFilter filter = new NotificationHistoryFilter.Builder() + .setMaxNotifications(3) + .build(); + + NotificationHistory history = new NotificationHistory(); + assertThat(filter.matchesCountFilter(history)).isTrue(); + history.addNotificationToWrite(getHistoricalNotification(1)); + assertThat(filter.matchesCountFilter(history)).isTrue(); + history.addNotificationToWrite(getHistoricalNotification(2)); + assertThat(filter.matchesCountFilter(history)).isTrue(); + history.addNotificationToWrite(getHistoricalNotification(3)); + assertThat(filter.matchesCountFilter(history)).isFalse(); + } + + @Test + public void testMatchesCountFilter_noCountFilter() { + NotificationHistoryFilter filter = new NotificationHistoryFilter.Builder() + .build(); + + NotificationHistory history = new NotificationHistory(); + assertThat(filter.matchesCountFilter(history)).isTrue(); + history.addNotificationToWrite(getHistoricalNotification(1)); + assertThat(filter.matchesCountFilter(history)).isTrue(); + } + + @Test + public void testMatchesPackageAndChannelFilter_pkgOnly() { + NotificationHistoryFilter filter = new NotificationHistoryFilter.Builder() + .setPackage("pkg") + .build(); + + HistoricalNotification hnMatches = getHistoricalNotification("pkg", 1); + assertThat(filter.matchesPackageAndChannelFilter(hnMatches)).isTrue(); + HistoricalNotification hnMatches2 = getHistoricalNotification("pkg", 2); + assertThat(filter.matchesPackageAndChannelFilter(hnMatches2)).isTrue(); + + HistoricalNotification hnNoMatch = getHistoricalNotification("pkg2", 2); + assertThat(filter.matchesPackageAndChannelFilter(hnNoMatch)).isFalse(); + } + + @Test + public void testMatchesPackageAndChannelFilter_channelAlso() { + NotificationHistoryFilter filter = new NotificationHistoryFilter.Builder() + .setChannel("pkg", "channel") + .build(); + + HistoricalNotification hn1 = getHistoricalNotification("pkg", 1); + assertThat(filter.matchesPackageAndChannelFilter(hn1)).isFalse(); + + HistoricalNotification hn2 = getHistoricalNotification("pkg", "channel", 1); + assertThat(filter.matchesPackageAndChannelFilter(hn2)).isTrue(); + + HistoricalNotification hn3 = getHistoricalNotification("pkg2", "channel", 1); + assertThat(filter.matchesPackageAndChannelFilter(hn3)).isFalse(); + } + + @Test + public void testIsFiltering() { + NotificationHistoryFilter filter = new NotificationHistoryFilter.Builder() + .build(); + assertThat(filter.isFiltering()).isFalse(); + + filter = new NotificationHistoryFilter.Builder() + .setPackage("pkg") + .build(); + assertThat(filter.isFiltering()).isTrue(); + + filter = new NotificationHistoryFilter.Builder() + .setChannel("pkg", "channel") + .build(); + assertThat(filter.isFiltering()).isTrue(); + + filter = new NotificationHistoryFilter.Builder() + .setMaxNotifications(5) + .build(); + assertThat(filter.isFiltering()).isTrue(); + } +} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryProtoHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryProtoHelperTest.java new file mode 100644 index 000000000000..458117d50784 --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryProtoHelperTest.java @@ -0,0 +1,297 @@ +/* + * 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.notification; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.NotificationHistory; +import android.app.NotificationHistory.HistoricalNotification; +import android.graphics.drawable.Icon; +import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.UiServiceTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class NotificationHistoryProtoHelperTest extends UiServiceTestCase { + + private HistoricalNotification getHistoricalNotification(int index) { + return getHistoricalNotification("package" + index, index); + } + + private HistoricalNotification getHistoricalNotification(String packageName, int index) { + String expectedChannelName = "channelName" + index; + String expectedChannelId = "channelId" + index; + int expectedUid = 1123456 + index; + int expectedUserId = 11 + index; + long expectedPostTime = 987654321 + index; + String expectedTitle = "title" + index; + String expectedText = "text" + index; + Icon expectedIcon = Icon.createWithResource(InstrumentationRegistry.getContext(), + index); + + return new HistoricalNotification.Builder() + .setPackage(packageName) + .setChannelName(expectedChannelName) + .setChannelId(expectedChannelId) + .setUid(expectedUid) + .setUserId(expectedUserId) + .setPostedTimeMs(expectedPostTime) + .setTitle(expectedTitle) + .setText(expectedText) + .setIcon(expectedIcon) + .build(); + } + + @Test + public void testReadWriteNotifications() throws Exception { + NotificationHistory history = new NotificationHistory(); + + List<HistoricalNotification> expectedEntries = new ArrayList<>(); + // loops backwards just to maintain the post time newest -> oldest expectation + for (int i = 10; i >= 1; i--) { + HistoricalNotification n = getHistoricalNotification(i); + expectedEntries.add(n); + history.addNotificationToWrite(n); + } + history.poolStringsFromNotifications(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + NotificationHistoryProtoHelper.write(baos, history, 1); + + NotificationHistory actualHistory = new NotificationHistory(); + NotificationHistoryProtoHelper.read( + new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), + actualHistory, + new NotificationHistoryFilter.Builder().build()); + + assertThat(actualHistory.getHistoryCount()).isEqualTo(history.getHistoryCount()); + assertThat(actualHistory.getNotificationsToWrite()) + .containsExactlyElementsIn(expectedEntries); + } + + @Test + public void testReadWriteNotifications_stringFieldsPersistedEvenIfNoPool() throws Exception { + NotificationHistory history = new NotificationHistory(); + + List<HistoricalNotification> expectedEntries = new ArrayList<>(); + // loops backwards just to maintain the post time newest -> oldest expectation + for (int i = 10; i >= 1; i--) { + HistoricalNotification n = getHistoricalNotification(i); + expectedEntries.add(n); + history.addNotificationToWrite(n); + } + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + NotificationHistoryProtoHelper.write(baos, history, 1); + + NotificationHistory actualHistory = new NotificationHistory(); + NotificationHistoryProtoHelper.read( + new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), + actualHistory, + new NotificationHistoryFilter.Builder().build()); + + assertThat(actualHistory.getHistoryCount()).isEqualTo(history.getHistoryCount()); + assertThat(actualHistory.getNotificationsToWrite()) + .containsExactlyElementsIn(expectedEntries); + } + + @Test + public void testReadNotificationsWithPkgFilter() throws Exception { + NotificationHistory history = new NotificationHistory(); + + List<HistoricalNotification> expectedEntries = new ArrayList<>(); + Set<String> expectedStrings = new HashSet<>(); + // loops backwards just to maintain the post time newest -> oldest expectation + for (int i = 10; i >= 1; i--) { + HistoricalNotification n = + getHistoricalNotification((i % 2 == 0) ? "pkgEven" : "pkgOdd", i); + + if (i % 2 == 0) { + expectedStrings.add(n.getPackage()); + expectedStrings.add(n.getChannelName()); + expectedStrings.add(n.getChannelId()); + expectedEntries.add(n); + } + history.addNotificationToWrite(n); + } + history.poolStringsFromNotifications(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + NotificationHistoryProtoHelper.write(baos, history, 1); + + NotificationHistory actualHistory = new NotificationHistory(); + + NotificationHistoryFilter filter = new NotificationHistoryFilter.Builder() + .setPackage("pkgEven") + .build(); + NotificationHistoryProtoHelper.read( + new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), + actualHistory, + filter); + + assertThat(actualHistory.getNotificationsToWrite()) + .containsExactlyElementsIn(expectedEntries); + assertThat(Arrays.asList(actualHistory.getPooledStringsToWrite())) + .containsExactlyElementsIn(expectedStrings); + } + + @Test + public void testReadNotificationsWithNumberFilter() throws Exception { + int maxCount = 3; + NotificationHistory history = new NotificationHistory(); + + List<HistoricalNotification> expectedEntries = new ArrayList<>(); + Set<String> expectedStrings = new HashSet<>(); + for (int i = 1; i < 10; i++) { + HistoricalNotification n = getHistoricalNotification(i); + + if (i <= maxCount) { + expectedStrings.add(n.getPackage()); + expectedStrings.add(n.getChannelName()); + expectedStrings.add(n.getChannelId()); + expectedEntries.add(n); + } + history.addNotificationToWrite(n); + } + history.poolStringsFromNotifications(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + NotificationHistoryProtoHelper.write(baos, history, 1); + + NotificationHistory actualHistory = new NotificationHistory(); + + NotificationHistoryFilter filter = new NotificationHistoryFilter.Builder() + .setMaxNotifications(maxCount) + .build(); + NotificationHistoryProtoHelper.read( + new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), + actualHistory, + filter); + + assertThat(actualHistory.getNotificationsToWrite()) + .containsExactlyElementsIn(expectedEntries); + assertThat(Arrays.asList(actualHistory.getPooledStringsToWrite())) + .containsExactlyElementsIn(expectedStrings); + } + + @Test + public void testReadNotificationsWithNumberFilter_preExistingNotifs() throws Exception { + List<HistoricalNotification> expectedEntries = new ArrayList<>(); + Set<String> expectedStrings = new HashSet<>(); + int maxCount = 3; + + NotificationHistory history = new NotificationHistory(); + HistoricalNotification old1 = getHistoricalNotification(40); + history.addNotificationToWrite(old1); + expectedEntries.add(old1); + + HistoricalNotification old2 = getHistoricalNotification(50); + history.addNotificationToWrite(old2); + expectedEntries.add(old2); + history.poolStringsFromNotifications(); + expectedStrings.addAll(Arrays.asList(history.getPooledStringsToWrite())); + + for (int i = 1; i < 10; i++) { + HistoricalNotification n = getHistoricalNotification(i); + + if (i <= (maxCount - 2)) { + expectedStrings.add(n.getPackage()); + expectedStrings.add(n.getChannelName()); + expectedStrings.add(n.getChannelId()); + expectedEntries.add(n); + } + history.addNotificationToWrite(n); + } + history.poolStringsFromNotifications(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + NotificationHistoryProtoHelper.write(baos, history, 1); + + NotificationHistory actualHistory = new NotificationHistory(); + + NotificationHistoryFilter filter = new NotificationHistoryFilter.Builder() + .setMaxNotifications(maxCount) + .build(); + NotificationHistoryProtoHelper.read( + new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), + actualHistory, + filter); + + assertThat(actualHistory.getNotificationsToWrite()) + .containsExactlyElementsIn(expectedEntries); + assertThat(Arrays.asList(actualHistory.getPooledStringsToWrite())) + .containsExactlyElementsIn(expectedStrings); + } + + @Test + public void testReadMergeIntoExistingHistory() throws Exception { + NotificationHistory history = new NotificationHistory(); + + List<HistoricalNotification> expectedEntries = new ArrayList<>(); + Set<String> expectedStrings = new HashSet<>(); + for (int i = 1; i < 10; i++) { + HistoricalNotification n = getHistoricalNotification(i); + expectedEntries.add(n); + expectedStrings.add(n.getPackage()); + expectedStrings.add(n.getChannelName()); + expectedStrings.add(n.getChannelId()); + history.addNotificationToWrite(n); + } + history.poolStringsFromNotifications(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + NotificationHistoryProtoHelper.write(baos, history, 1); + + // set up pre-existing notification history, as though read from a different file + NotificationHistory actualHistory = new NotificationHistory(); + for (int i = 10; i < 20; i++) { + HistoricalNotification n = getHistoricalNotification(i); + expectedEntries.add(n); + expectedStrings.add(n.getPackage()); + expectedStrings.add(n.getChannelName()); + expectedStrings.add(n.getChannelId()); + actualHistory.addNotificationToWrite(n); + } + actualHistory.poolStringsFromNotifications(); + + NotificationHistoryProtoHelper.read( + new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), + actualHistory, + new NotificationHistoryFilter.Builder().build()); + + // Make sure history contains the original and new entries + assertThat(actualHistory.getNotificationsToWrite()) + .containsExactlyElementsIn(expectedEntries); + assertThat(Arrays.asList(actualHistory.getPooledStringsToWrite())) + .containsExactlyElementsIn(expectedStrings); + } +} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 80439cf66387..a1322b9a9015 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -62,7 +62,6 @@ import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.Global; import android.provider.Settings.Secure; - import android.test.suitebuilder.annotation.SmallTest; import android.testing.TestableContentResolver; import android.util.ArrayMap; @@ -162,11 +161,11 @@ public class PreferencesHelperTest extends UiServiceTestCase { when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider); contentResolver.addProvider(TEST_AUTHORITY, testContentProvider); - when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI))) + when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI))) .thenReturn(CANONICAL_SOUND_URI); - when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI))) + when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) .thenReturn(CANONICAL_SOUND_URI); - when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI))) + when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) .thenReturn(SOUND_URI); mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, @@ -465,7 +464,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { // Testing that in restore we are given the canonical version loadStreamXml(baos, true, UserHandle.USER_SYSTEM); - verify(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI)); + verify(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)); } @Test @@ -475,11 +474,11 @@ public class PreferencesHelperTest extends UiServiceTestCase { .appendQueryParameter("title", "Test") .appendQueryParameter("canonical", "1") .build(); - when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI))) + when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) .thenReturn(canonicalBasedOnLocal); - when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI))) + when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) .thenReturn(localUri); - when(mTestIContentProvider.uncanonicalize(any(), eq(canonicalBasedOnLocal))) + when(mTestIContentProvider.uncanonicalize(any(), any(), eq(canonicalBasedOnLocal))) .thenReturn(localUri); NotificationChannel channel = @@ -499,9 +498,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception { Thread.sleep(3000); - when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI))) + when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) .thenReturn(null); - when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI))) + when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) .thenReturn(null); NotificationChannel channel = @@ -526,7 +525,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception { // Not a local uncanonicalized uri, simulating that it fails to exist locally - when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI))).thenReturn(null); + when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI))).thenReturn(null); String id = "id"; String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n" diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java index 2abd340e967a..8774b639b554 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java @@ -132,11 +132,11 @@ public class RankingHelperTest extends UiServiceTestCase { when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider); contentResolver.addProvider(TEST_AUTHORITY, testContentProvider); - when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI))) + when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI))) .thenReturn(CANONICAL_SOUND_URI); - when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI))) + when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) .thenReturn(CANONICAL_SOUND_URI); - when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI))) + when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) .thenReturn(SOUND_URI); mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java index 3b336eb7aec9..aceed86220c6 100644 --- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java +++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java @@ -12,6 +12,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -109,8 +110,8 @@ public class PinnedSliceStateTest extends UiServiceTestCase { mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken); TestableLooper.get(this).processAllMessages(); - verify(mIContentProvider).call(anyString(), anyString(), eq(SliceProvider.METHOD_PIN), - eq(null), argThat(b -> { + verify(mIContentProvider).call(anyString(), nullable(String.class), anyString(), + eq(SliceProvider.METHOD_PIN), eq(null), argThat(b -> { assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI)); return true; })); @@ -167,8 +168,8 @@ public class PinnedSliceStateTest extends UiServiceTestCase { // Throw exception when trying to pin doAnswer(invocation -> { throw new Exception("Pin failed"); - }).when(mIContentProvider).call( - anyString(), anyString(), anyString(), eq(null), any()); + }).when(mIContentProvider).call(anyString(), nullable(String.class), anyString(), + anyString(), eq(null), any()); TestableLooper.get(this).processAllMessages(); @@ -176,8 +177,8 @@ public class PinnedSliceStateTest extends UiServiceTestCase { mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken); TestableLooper.get(this).processAllMessages(); - verify(mIContentProvider).call(anyString(), anyString(), eq(SliceProvider.METHOD_PIN), - eq(null), argThat(b -> { + verify(mIContentProvider).call(anyString(), nullable(String.class), anyString(), + eq(SliceProvider.METHOD_PIN), eq(null), argThat(b -> { assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI)); return true; })); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index 03367db0c3e6..7c867b695203 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -149,9 +149,7 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { public void testOnActivityLaunchFinished() { onActivityLaunched(); - mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(), - SystemClock.elapsedRealtimeNanos()); - + notifyTransitionStarting(); notifyWindowsDrawn(mTopActivity); verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTopActivity), anyLong()); @@ -159,10 +157,10 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { } @Test - public void testOnActivityLaunchCancelled() { + public void testOnActivityLaunchCancelled_hasDrawn() { onActivityLaunched(); - mTopActivity.mDrawn = true; + mTopActivity.visible = mTopActivity.mDrawn = true; // Cannot time already-visible activities. mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity); @@ -172,6 +170,28 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { } @Test + public void testOnActivityLaunchCancelled_finishedBeforeDrawn() { + mTopActivity.visible = mTopActivity.mDrawn = true; + + // Suppress resume when creating the record because we want to notify logger manually. + mSupervisor.beginDeferResume(); + // Create an activity with different process that meets process switch. + final ActivityRecord noDrawnActivity = new ActivityBuilder(mService) + .setTask(mTopActivity.getTaskRecord()) + .setProcessName("other") + .build(); + mSupervisor.readyToResume(); + + mActivityMetricsLogger.notifyActivityLaunching(noDrawnActivity.intent); + mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, noDrawnActivity); + + noDrawnActivity.destroyIfPossible("test"); + mActivityMetricsLogger.notifyVisibilityChanged(noDrawnActivity); + + verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(noDrawnActivity)); + } + + @Test public void testOnReportFullyDrawn() { onActivityLaunched(); @@ -184,16 +204,21 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { private void onActivityLaunchedTrampoline() { onIntentStarted(); - mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTopActivity); + mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTrampolineActivity); - verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mTopActivity), anyInt()); + verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mTrampolineActivity), anyInt()); - // A second, distinct, activity launch is coalesced into the the current app launch sequence - mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTrampolineActivity); + // A second, distinct, activity launch is coalesced into the current app launch sequence. + mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTopActivity); verifyNoMoreInteractions(mLaunchObserver); } + private void notifyTransitionStarting() { + mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(), + SystemClock.elapsedRealtimeNanos()); + } + private void notifyWindowsDrawn(ActivityRecord r) { mActivityMetricsLogger.notifyWindowsDrawn(r.getWindowingMode(), SystemClock.elapsedRealtimeNanos()); @@ -203,15 +228,12 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { public void testOnActivityLaunchFinishedTrampoline() { onActivityLaunchedTrampoline(); - mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(), - SystemClock.elapsedRealtimeNanos()); - + notifyTransitionStarting(); notifyWindowsDrawn(mTrampolineActivity); notifyWindowsDrawn(mTopActivity); - verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTrampolineActivity), - anyLong()); + verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTopActivity), anyLong()); verifyNoMoreInteractions(mLaunchObserver); } @@ -219,12 +241,12 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { public void testOnActivityLaunchCancelledTrampoline() { onActivityLaunchedTrampoline(); - mTrampolineActivity.mDrawn = true; + mTopActivity.mDrawn = true; // Cannot time already-visible activities. - mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mTrampolineActivity); + mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity); - verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mTrampolineActivity)); + verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mTopActivity)); verifyNoMoreInteractions(mLaunchObserver); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index aceb6335c4d3..38d6c9c1e4db 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -836,6 +836,14 @@ public class ActivityRecordTests extends ActivityTestsBase { // Set process to 'null' to allow immediate removal, but don't call mActivity.setProcess() - // this will cause NPE when updating task's process. mActivity.app = null; + + // Put a visible activity on top, so the finishing activity doesn't have to wait until the + // next activity reports idle to destroy it. + final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build(); + topActivity.visible = true; + topActivity.nowVisible = true; + topActivity.setState(RESUMED, "test"); + assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_REMOVED, mActivity.finishIfPossible("test", false /* oomAdj */)); assertTrue(mActivity.finishing); @@ -995,6 +1003,35 @@ public class ActivityRecordTests extends ActivityTestsBase { } /** + * Verify that finish request won't change the state of next top activity if the current + * finishing activity doesn't need to be destroyed immediately. The case is usually like + * from {@link ActivityStack#completePauseLocked(boolean, ActivityRecord)} to + * {@link ActivityRecord#completeFinishing(String)}, so the complete-pause should take the + * responsibility to resume the next activity with updating the state. + */ + @Test + public void testCompleteFinishing_keepStateOfNextInvisible() { + final ActivityRecord currentTop = mActivity; + currentTop.visible = currentTop.nowVisible = true; + + // Simulates that {@code currentTop} starts an existing activity from background (so its + // state is stopped) and the starting flow just goes to place it at top. + final ActivityStack nextStack = new StackBuilder(mRootActivityContainer).build(); + final ActivityRecord nextTop = nextStack.getTopActivity(); + nextTop.setState(STOPPED, "test"); + + mStack.mPausingActivity = currentTop; + currentTop.finishing = true; + currentTop.setState(PAUSED, "test"); + currentTop.completeFinishing("completePauseLocked"); + + // Current top becomes stopping because it is visible and the next is invisible. + assertEquals(STOPPING, currentTop.getState()); + // The state of next activity shouldn't be changed. + assertEquals(STOPPED, nextTop.getState()); + } + + /** * Verify that complete finish request for visible activity must be delayed before the next one * becomes visible. */ @@ -1102,8 +1139,11 @@ public class ActivityRecordTests extends ActivityTestsBase { // Add another stack to become focused and make the activity there visible. This way it // simulates finishing in non-focused stack in split-screen. final ActivityStack stack = new StackBuilder(mRootActivityContainer).build(); - stack.getChildAt(0).getChildAt(0).nowVisible = true; - stack.getChildAt(0).getChildAt(0).visible = true; + final ActivityRecord focusedActivity = stack.getChildAt(0).getChildAt(0); + focusedActivity.nowVisible = true; + focusedActivity.visible = true; + focusedActivity.setState(RESUMED, "test"); + stack.mResumedActivity = focusedActivity; topActivity.completeFinishing("test"); @@ -1151,6 +1191,30 @@ public class ActivityRecordTests extends ActivityTestsBase { } /** + * Verify that complete finish request for visible activity must resume next home stack before + * destroying it immediately if it is the last running activity on a display with a home stack. + * We must wait for home activity to come up to avoid a black flash in this case. + */ + @Test + public void testCompleteFinishing_lastActivityAboveEmptyHomeStack() { + // Empty the home stack. + final ActivityStack homeStack = mActivity.getDisplay().getHomeStack(); + for (TaskRecord t : homeStack.getAllTasks()) { + homeStack.removeTask(t, "test", REMOVE_TASK_MODE_DESTROYING); + } + mActivity.finishing = true; + spyOn(mStack); + + // Try to finish the last activity above the home stack. + mActivity.completeFinishing("test"); + + // Verify that the activity is not destroyed immediately, but waits for next one to come up. + verify(mActivity, never()).destroyImmediately(eq(true) /* removeFromApp */, anyString()); + assertEquals(FINISHING, mActivity.getState()); + assertTrue(mActivity.mStackSupervisor.mFinishingActivities.contains(mActivity)); + } + + /** * Test that the activity will be moved to destroying state and the message to destroy will be * sent to the client. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index 80f0851c27a8..78db6c92772f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -118,7 +118,8 @@ class ActivityTestsBase extends SystemServiceTestsBase { private ComponentName mComponent; private String mTargetActivity; private TaskRecord mTaskRecord; - private int mUid; + private String mProcessName = "name"; + private int mUid = 12345; private boolean mCreateTask; private ActivityStack mStack; private int mActivityFlags; @@ -175,6 +176,11 @@ class ActivityTestsBase extends SystemServiceTestsBase { return this; } + ActivityBuilder setProcessName(String name) { + mProcessName = name; + return this; + } + ActivityBuilder setUid(int uid) { mUid = uid; return this; @@ -235,6 +241,7 @@ class ActivityTestsBase extends SystemServiceTestsBase { aInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; aInfo.applicationInfo.packageName = mComponent.getPackageName(); aInfo.applicationInfo.uid = mUid; + aInfo.processName = mProcessName; aInfo.packageName = mComponent.getPackageName(); if (mTargetActivity != null) { aInfo.targetActivity = mTargetActivity; @@ -268,7 +275,7 @@ class ActivityTestsBase extends SystemServiceTestsBase { } final WindowProcessController wpc = new WindowProcessController(mService, - mService.mContext.getApplicationInfo(), "name", 12345, + mService.mContext.getApplicationInfo(), mProcessName, mUid, UserHandle.getUserId(12345), mock(Object.class), mock(WindowProcessListener.class)); wpc.setThread(mock(IApplicationThread.class)); diff --git a/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java index 77f9f0428e70..b6eaab7efec4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java @@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions; +import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -69,8 +70,8 @@ public class AnimatingActivityRegistryTest extends WindowTestsBase { activity1.startAnimation(activity1.getPendingTransaction(), mAdapter, false /* hidden */); activity2.startAnimation(activity1.getPendingTransaction(), mAdapter, false /* hidden */); - assertTrue(activity1.isSelfAnimating()); - assertTrue(activity2.isSelfAnimating()); + assertTrue(activity1.isAnimating(TRANSITION)); + assertTrue(activity2.isAnimating(TRANSITION)); // Make sure that first animation finish is deferred, second one is not deferred, and first // one gets cancelled. @@ -92,8 +93,8 @@ public class AnimatingActivityRegistryTest extends WindowTestsBase { window1.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */); window2.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */); - assertTrue(window1.isSelfAnimating()); - assertTrue(window2.isSelfAnimating()); + assertTrue(window1.isAnimating(TRANSITION)); + assertTrue(window2.isAnimating(TRANSITION)); // Make sure that first animation finish is deferred, and removing the second window stops // finishes all pending deferred finishings. diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index 06afce2368b7..5f42d2398421 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -147,8 +147,8 @@ public class AppTransitionTests extends WindowTestsBase { // Make sure each display is in animating stage. assertTrue(dc1.mOpeningApps.size() > 0); assertTrue(dc2.mClosingApps.size() > 0); - assertTrue(dc1.isAppAnimating()); - assertTrue(dc2.isAppAnimating()); + assertTrue(dc1.isAppTransitioning()); + assertTrue(dc2.isAppTransitioning()); } @Test @@ -219,10 +219,10 @@ public class AppTransitionTests extends WindowTestsBase { assertTrue(dc.mClosingApps.size() > 0); // Make sure window is in animating stage before freeze, and cancel after freeze. - assertTrue(dc.isAppAnimating()); + assertTrue(dc.isAppTransitioning()); assertFalse(runner.mCancelled); dc.mAppTransition.freeze(); - assertFalse(dc.isAppAnimating()); + assertFalse(dc.isAppTransitioning()); assertTrue(runner.mCancelled); } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 2a3731a2e483..67b7a66b4794 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -24,6 +24,7 @@ import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; +import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; @@ -56,15 +57,23 @@ import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.WindowManager; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.utils.WmDisplayCutout; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +/** + * Tests for the {@link DisplayPolicy} class. + * + * Build/Install/Run: + * atest WmTests:DisplayPolicyLayoutTests + */ @SmallTest @Presubmit @RunWith(WindowTestRunner.class) @@ -93,6 +102,12 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { attrs.format = PixelFormat.TRANSLUCENT; } + @After + public void tearDown() { + PolicyControl.setFilters(""); + mWindow.getDisplayContent().mInputMethodTarget = null; + } + public void setRotation(int rotation) { mRotation = rotation; updateDisplayFrames(); @@ -393,6 +408,105 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0); } + @FlakyTest(bugId = 129711077) + @Test + public void layoutWindowLw_withImmersive_SoftInputAdjustResize() { + synchronized (mWm.mGlobalLock) { + mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_RESIZE; + mWindow.mAttrs.flags = 0; + mWindow.mAttrs.systemUiVisibility = + SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | SYSTEM_UI_FLAG_FULLSCREEN | SYSTEM_UI_FLAG_HIDE_NAVIGATION; + + addWindow(mWindow); + + mWindow.getDisplayContent().mInputMethodTarget = mWindow; + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mFrames.mContent.bottom = mFrames.mVoiceContent.bottom = INPUT_METHOD_WINDOW_TOP; + mFrames.mCurrent.bottom = INPUT_METHOD_WINDOW_TOP; + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + int bottomInset = mFrames.mDisplayHeight - INPUT_METHOD_WINDOW_TOP; + assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getContentFrameLw(), 0, 0); + assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, bottomInset); + } + } + + @FlakyTest(bugId = 129711077) + @Test + public void layoutWindowLw_withImmersive_SoftInputAdjustNothing() { + synchronized (mWm.mGlobalLock) { + mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_NOTHING; + mWindow.mAttrs.flags = 0; + mWindow.mAttrs.systemUiVisibility = + SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | SYSTEM_UI_FLAG_FULLSCREEN | SYSTEM_UI_FLAG_HIDE_NAVIGATION; + + addWindow(mWindow); + + mWindow.getDisplayContent().mInputMethodTarget = mWindow; + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mFrames.mContent.bottom = mFrames.mVoiceContent.bottom = INPUT_METHOD_WINDOW_TOP; + mFrames.mCurrent.bottom = INPUT_METHOD_WINDOW_TOP; + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getContentFrameLw(), 0, 0); + assertInsetByTopBottom(mWindow.getVisibleFrameLw(), 0, 0); + } + } + + @FlakyTest(bugId = 129711077) + @Test + public void layoutWindowLw_withForceImmersive_fullscreen() { + synchronized (mWm.mGlobalLock) { + mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_RESIZE; + mWindow.mAttrs.flags = 0; + mWindow.mAttrs.systemUiVisibility = 0; + PolicyControl.setFilters(PolicyControl.NAME_IMMERSIVE_FULL + "=*"); + + addWindow(mWindow); + + mWindow.getDisplayContent().mInputMethodTarget = mWindow; + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mFrames.mContent.bottom = mFrames.mVoiceContent.bottom = INPUT_METHOD_WINDOW_TOP; + mFrames.mCurrent.bottom = INPUT_METHOD_WINDOW_TOP; + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + int bottomInset = mFrames.mDisplayHeight - INPUT_METHOD_WINDOW_TOP; + assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getContentFrameLw(), 0, 0); + assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, bottomInset); + } + } + + @FlakyTest(bugId = 129711077) + @Test + public void layoutWindowLw_withForceImmersive_nonFullscreen() { + synchronized (mWm.mGlobalLock) { + mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_RESIZE; + mWindow.mAttrs.flags = 0; + mWindow.mAttrs.systemUiVisibility = 0; + mWindow.mAttrs.width = DISPLAY_WIDTH / 2; + mWindow.mAttrs.height = DISPLAY_HEIGHT / 2; + PolicyControl.setFilters(PolicyControl.NAME_IMMERSIVE_FULL + "=*"); + + addWindow(mWindow); + + mWindow.getDisplayContent().mInputMethodTarget = mWindow; + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mFrames.mContent.bottom = mFrames.mVoiceContent.bottom = INPUT_METHOD_WINDOW_TOP; + mFrames.mCurrent.bottom = INPUT_METHOD_WINDOW_TOP; + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + int bottomInset = mFrames.mDisplayHeight - INPUT_METHOD_WINDOW_TOP; + assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, bottomInset); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, bottomInset); + assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, bottomInset); + } + } + @Test public void layoutHint_appWindow() { // Initialize DisplayFrames diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java index 2933b4a460d8..d4558dc29fda 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java @@ -62,6 +62,7 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { static final int STATUS_BAR_HEIGHT = 10; static final int NAV_BAR_HEIGHT = 15; static final int DISPLAY_CUTOUT_HEIGHT = 8; + static final int INPUT_METHOD_WINDOW_TOP = 585; DisplayPolicy mDisplayPolicy; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java index 2fc03c7f13d3..164d28d57cb0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java @@ -23,12 +23,13 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; +import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import android.graphics.Rect; @@ -141,7 +142,7 @@ public class TaskStackTests extends WindowTestsBase { final Task task = createTaskInStack(stack, 0 /* userId */); // Stack removal is deferred if one of its child is animating. - doReturn(true).when(task).isSelfAnimating(); + doReturn(true).when(task).isAnimating(TRANSITION | CHILDREN); stack.removeIfPossible(); // For the case of deferred removal the task controller will still be connected to the its diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index 85aff7f9a9cd..853644873f01 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -24,13 +24,18 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; +import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; +import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -335,7 +340,53 @@ public class WindowContainerTests extends WindowTestsBase { } @Test - public void testIsAnimating() { + public void testIsAnimating_TransitionFlag() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).build(); + final TestWindowContainer child1 = root.addChildWindow( + builder.setWaitForTransitionStart(true)); + + assertFalse(root.isAnimating(TRANSITION)); + assertTrue(child1.isAnimating(TRANSITION)); + } + + @Test + public void testIsAnimating_ParentsFlag() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).build(); + final TestWindowContainer child1 = root.addChildWindow(builder); + final TestWindowContainer child2 = root.addChildWindow(builder.setIsAnimating(true)); + final TestWindowContainer child21 = child2.addChildWindow(builder.setIsAnimating(false)); + + assertFalse(root.isAnimating()); + assertFalse(child1.isAnimating()); + assertFalse(child1.isAnimating(PARENTS)); + assertTrue(child2.isAnimating()); + assertTrue(child2.isAnimating(PARENTS)); + assertFalse(child21.isAnimating()); + assertTrue(child21.isAnimating(PARENTS)); + } + + @Test + public void testIsAnimating_ChildrenFlag() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).build(); + final TestWindowContainer child1 = root.addChildWindow(builder); + final TestWindowContainer child2 = root.addChildWindow(builder.setIsAnimating(true)); + final TestWindowContainer child11 = child1.addChildWindow(builder.setIsAnimating(true)); + + assertFalse(root.isAnimating()); + assertTrue(root.isAnimating(CHILDREN)); + assertFalse(child1.isAnimating()); + assertTrue(child1.isAnimating(CHILDREN)); + assertTrue(child2.isAnimating()); + assertTrue(child2.isAnimating(CHILDREN)); + assertTrue(child11.isAnimating()); + assertTrue(child11.isAnimating(CHILDREN)); + } + + @Test + public void testIsAnimating_combineFlags() { final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); final TestWindowContainer root = builder.setLayer(0).build(); @@ -345,19 +396,19 @@ public class WindowContainerTests extends WindowTestsBase { final TestWindowContainer child12 = child1.addChildWindow(builder.setIsAnimating(true)); final TestWindowContainer child21 = child2.addChildWindow(); - assertFalse(root.isAnimating()); - assertTrue(child1.isAnimating()); - assertTrue(child11.isAnimating()); - assertTrue(child12.isAnimating()); - assertFalse(child2.isAnimating()); - assertFalse(child21.isAnimating()); - - assertTrue(root.isSelfOrChildAnimating()); - assertTrue(child1.isSelfOrChildAnimating()); - assertFalse(child11.isSelfOrChildAnimating()); - assertTrue(child12.isSelfOrChildAnimating()); - assertFalse(child2.isSelfOrChildAnimating()); - assertFalse(child21.isSelfOrChildAnimating()); + assertFalse(root.isAnimating(TRANSITION | PARENTS)); + assertTrue(child1.isAnimating(TRANSITION | PARENTS)); + assertTrue(child11.isAnimating(TRANSITION | PARENTS)); + assertTrue(child12.isAnimating(TRANSITION | PARENTS)); + assertFalse(child2.isAnimating(TRANSITION | PARENTS)); + assertFalse(child21.isAnimating(TRANSITION | PARENTS)); + + assertTrue(root.isAnimating(TRANSITION | CHILDREN)); + assertTrue(child1.isAnimating(TRANSITION | CHILDREN)); + assertFalse(child11.isAnimating(TRANSITION | CHILDREN)); + assertTrue(child12.isAnimating(TRANSITION | CHILDREN)); + assertFalse(child2.isAnimating(TRANSITION | CHILDREN)); + assertFalse(child21.isAnimating(TRANSITION | CHILDREN)); } @Test @@ -716,6 +767,7 @@ public class WindowContainerTests extends WindowTestsBase { private boolean mIsAnimating; private boolean mIsVisible; private boolean mFillsParent; + private boolean mWaitForTransitStart; private Integer mOrientation; private boolean mOnParentChangedCalled; @@ -738,7 +790,7 @@ public class WindowContainerTests extends WindowTestsBase { }; TestWindowContainer(WindowManagerService wm, int layer, boolean isAnimating, - boolean isVisible, Integer orientation) { + boolean isVisible, boolean waitTransitStart, Integer orientation) { super(wm); mLayer = layer; @@ -746,6 +798,9 @@ public class WindowContainerTests extends WindowTestsBase { mIsVisible = isVisible; mFillsParent = true; mOrientation = orientation; + mWaitForTransitStart = waitTransitStart; + spyOn(mSurfaceAnimator); + doReturn(mIsAnimating).when(mSurfaceAnimator).isAnimating(); } TestWindowContainer getParentWindow() { @@ -783,11 +838,6 @@ public class WindowContainerTests extends WindowTestsBase { } @Override - boolean isSelfAnimating() { - return mIsAnimating; - } - - @Override boolean isVisible() { return mIsVisible; } @@ -810,6 +860,11 @@ public class WindowContainerTests extends WindowTestsBase { void setFillsParent(boolean fillsParent) { mFillsParent = fillsParent; } + + @Override + boolean isWaitingForTransitionStart() { + return mWaitForTransitStart; + } } private static class TestWindowContainerBuilder { @@ -817,6 +872,7 @@ public class WindowContainerTests extends WindowTestsBase { private int mLayer; private boolean mIsAnimating; private boolean mIsVisible; + private boolean mIsWaitTransitStart; private Integer mOrientation; TestWindowContainerBuilder(WindowManagerService wm) { @@ -847,8 +903,14 @@ public class WindowContainerTests extends WindowTestsBase { return this; } + TestWindowContainerBuilder setWaitForTransitionStart(boolean waitTransitStart) { + mIsWaitTransitStart = waitTransitStart; + return this; + } + TestWindowContainer build() { - return new TestWindowContainer(mWm, mLayer, mIsAnimating, mIsVisible, mOrientation); + return new TestWindowContainer(mWm, mLayer, mIsAnimating, mIsVisible, + mIsWaitTransitStart, mOrientation); } } diff --git a/services/usage/java/com/android/server/usage/TEST_MAPPING b/services/usage/java/com/android/server/usage/TEST_MAPPING new file mode 100644 index 000000000000..7b53d09fdbef --- /dev/null +++ b/services/usage/java/com/android/server/usage/TEST_MAPPING @@ -0,0 +1,33 @@ +{ + "presubmit": [ + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.app.usage" + } + ] + }, + { + "name": "FrameworksServicesTests", + "options": [ + { + "include-filter": "com.android.server.usage" + }, + { + "exclude-filter": "com.android.server.usage.StorageStatsServiceTest" + } + ] + } + ], + "postsubmit": [ + { + "name": "CtsUsageStatsTestCases", + "options": [ + { + "include-filter": "android.app.usage.cts.UsageStatsTest" + } + ] + } + ] +} diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java index db7ed1f58c1a..27d7360313ad 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java +++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java @@ -373,7 +373,12 @@ public class UsageStatsDatabase { Slog.e(TAG, "Failed read version upgrade breadcrumb"); throw new RuntimeException(e); } - continueUpgradeLocked(previousVersion, token); + if (mCurrentVersion >= 4) { + continueUpgradeLocked(previousVersion, token); + } else { + Slog.wtf(TAG, "Attempting to upgrade to an unsupported version: " + + mCurrentVersion); + } } if (version != mCurrentVersion || mNewUpdate) { @@ -487,6 +492,9 @@ public class UsageStatsDatabase { } private void continueUpgradeLocked(int version, long token) { + if (version <= 3) { + Slog.w(TAG, "Reading UsageStats as XML; current database version: " + mCurrentVersion); + } final File backupDir = new File(mBackupsDir, Long.toString(token)); // Upgrade step logic for the entire usage stats directory, not individual interval dirs. @@ -876,6 +884,10 @@ public class UsageStatsDatabase { } private void writeLocked(AtomicFile file, IntervalStats stats) throws IOException { + if (mCurrentVersion <= 3) { + Slog.wtf(TAG, "Attempting to write UsageStats as XML with version " + mCurrentVersion); + return; + } writeLocked(file, stats, mCurrentVersion, mPackagesTokenData); } @@ -892,17 +904,13 @@ public class UsageStatsDatabase { } } - private void writeLocked(OutputStream out, IntervalStats stats) throws IOException { - writeLocked(out, stats, mCurrentVersion, mPackagesTokenData); - } - private static void writeLocked(OutputStream out, IntervalStats stats, int version, PackagesTokenData packagesTokenData) throws IOException { switch (version) { case 1: case 2: case 3: - UsageStatsXml.write(out, stats); + Slog.wtf(TAG, "Attempting to write UsageStats as XML with version " + version); break; case 4: try { @@ -927,6 +935,10 @@ public class UsageStatsDatabase { } private void readLocked(AtomicFile file, IntervalStats statsOut) throws IOException { + if (mCurrentVersion <= 3) { + Slog.wtf(TAG, "Reading UsageStats as XML; current database version: " + + mCurrentVersion); + } readLocked(file, statsOut, mCurrentVersion, mPackagesTokenData); } @@ -951,17 +963,18 @@ public class UsageStatsDatabase { } } - private void readLocked(InputStream in, IntervalStats statsOut) throws IOException { - readLocked(in, statsOut, mCurrentVersion, mPackagesTokenData); - } - private static void readLocked(InputStream in, IntervalStats statsOut, int version, PackagesTokenData packagesTokenData) throws IOException { switch (version) { case 1: case 2: case 3: - UsageStatsXml.read(in, statsOut); + Slog.w(TAG, "Reading UsageStats as XML; database version: " + version); + try { + UsageStatsXml.read(in, statsOut); + } catch (Exception e) { + Slog.e(TAG, "Unable to read interval stats from XML", e); + } break; case 4: try { @@ -1076,6 +1089,10 @@ public class UsageStatsDatabase { */ @VisibleForTesting public byte[] getBackupPayload(String key, int version) { + if (version >= 1 && version <= 3) { + Slog.wtf(TAG, "Attempting to backup UsageStats as XML with version " + version); + return null; + } synchronized (mLock) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); if (KEY_USAGE_STATS.equals(key)) { diff --git a/services/usage/java/com/android/server/usage/UsageStatsXml.java b/services/usage/java/com/android/server/usage/UsageStatsXml.java index f8d1113e8460..3100310368da 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsXml.java +++ b/services/usage/java/com/android/server/usage/UsageStatsXml.java @@ -16,14 +16,11 @@ package com.android.server.usage; -import android.util.AtomicFile; import android.util.Slog; import android.util.Xml; -import android.util.proto.ProtoInputStream; -import android.util.proto.ProtoOutputStream; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -31,7 +28,6 @@ import java.io.*; public class UsageStatsXml { private static final String TAG = "UsageStatsXml"; - private static final int CURRENT_VERSION = 1; private static final String USAGESTATS_TAG = "usagestats"; private static final String VERSION_ATTR = "version"; static final String CHECKED_IN_SUFFIX = "-c"; @@ -61,18 +57,4 @@ public class UsageStatsXml { throw new IOException(e); } } - - public static void write(OutputStream out, IntervalStats stats) throws IOException { - FastXmlSerializer xml = new FastXmlSerializer(); - xml.setOutput(out, "utf-8"); - xml.startDocument("utf-8", true); - xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - xml.startTag(null, USAGESTATS_TAG); - xml.attribute(null, VERSION_ATTR, Integer.toString(CURRENT_VERSION)); - - UsageStatsXmlV1.write(xml, stats); - - xml.endTag(null, USAGESTATS_TAG); - xml.endDocument(); - } }
\ No newline at end of file diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java index 565ca9ed1a00..259873987aef 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java +++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java @@ -26,7 +26,6 @@ import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.net.ProtocolException; @@ -243,135 +242,6 @@ final class UsageStatsXmlV1 { statsOut.addEvent(event); } - private static void writeUsageStats(XmlSerializer xml, final IntervalStats stats, - final UsageStats usageStats) throws IOException { - xml.startTag(null, PACKAGE_TAG); - - // Write the time offset. - XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, - usageStats.mLastTimeUsed - stats.beginTime); - XmlUtils.writeLongAttribute(xml, LAST_TIME_VISIBLE_ATTR, - usageStats.mLastTimeVisible - stats.beginTime); - XmlUtils.writeLongAttribute(xml, LAST_TIME_SERVICE_USED_ATTR, - usageStats.mLastTimeForegroundServiceUsed - stats.beginTime); - XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName); - XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground); - XmlUtils.writeLongAttribute(xml, TOTAL_TIME_VISIBLE_ATTR, usageStats.mTotalTimeVisible); - XmlUtils.writeLongAttribute(xml, TOTAL_TIME_SERVICE_USED_ATTR, - usageStats.mTotalTimeForegroundServiceUsed); - XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent); - if (usageStats.mAppLaunchCount > 0) { - XmlUtils.writeIntAttribute(xml, APP_LAUNCH_COUNT_ATTR, usageStats.mAppLaunchCount); - } - writeChooserCounts(xml, usageStats); - xml.endTag(null, PACKAGE_TAG); - } - - private static void writeCountAndTime(XmlSerializer xml, String tag, int count, long time) - throws IOException { - xml.startTag(null, tag); - XmlUtils.writeIntAttribute(xml, COUNT_ATTR, count); - XmlUtils.writeLongAttribute(xml, TIME_ATTR, time); - xml.endTag(null, tag); - } - - private static void writeChooserCounts(XmlSerializer xml, final UsageStats usageStats) - throws IOException { - if (usageStats == null || usageStats.mChooserCounts == null || - usageStats.mChooserCounts.keySet().isEmpty()) { - return; - } - final int chooserCountSize = usageStats.mChooserCounts.size(); - for (int i = 0; i < chooserCountSize; i++) { - final String action = usageStats.mChooserCounts.keyAt(i); - final ArrayMap<String, Integer> counts = usageStats.mChooserCounts.valueAt(i); - if (action == null || counts == null || counts.isEmpty()) { - continue; - } - xml.startTag(null, CHOOSER_COUNT_TAG); - XmlUtils.writeStringAttribute(xml, NAME, action); - writeCountsForAction(xml, counts); - xml.endTag(null, CHOOSER_COUNT_TAG); - } - } - - private static void writeCountsForAction(XmlSerializer xml, ArrayMap<String, Integer> counts) - throws IOException { - final int countsSize = counts.size(); - for (int i = 0; i < countsSize; i++) { - String key = counts.keyAt(i); - int count = counts.valueAt(i); - if (count > 0) { - xml.startTag(null, CATEGORY_TAG); - XmlUtils.writeStringAttribute(xml, NAME, key); - XmlUtils.writeIntAttribute(xml, COUNT, count); - xml.endTag(null, CATEGORY_TAG); - } - } - } - - private static void writeConfigStats(XmlSerializer xml, final IntervalStats stats, - final ConfigurationStats configStats, boolean isActive) throws IOException { - xml.startTag(null, CONFIG_TAG); - - // Write the time offset. - XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, - configStats.mLastTimeActive - stats.beginTime); - - XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, configStats.mTotalTimeActive); - XmlUtils.writeIntAttribute(xml, COUNT_ATTR, configStats.mActivationCount); - if (isActive) { - XmlUtils.writeBooleanAttribute(xml, ACTIVE_ATTR, true); - } - - // Now write the attributes representing the configuration object. - Configuration.writeXmlAttrs(xml, configStats.mConfiguration); - - xml.endTag(null, CONFIG_TAG); - } - - private static void writeEvent(XmlSerializer xml, final IntervalStats stats, - final UsageEvents.Event event) throws IOException { - xml.startTag(null, EVENT_TAG); - - // Store the time offset. - XmlUtils.writeLongAttribute(xml, TIME_ATTR, event.mTimeStamp - stats.beginTime); - - XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, event.mPackage); - if (event.mClass != null) { - XmlUtils.writeStringAttribute(xml, CLASS_ATTR, event.mClass); - } - XmlUtils.writeIntAttribute(xml, FLAGS_ATTR, event.mFlags); - XmlUtils.writeIntAttribute(xml, TYPE_ATTR, event.mEventType); - XmlUtils.writeIntAttribute(xml, INSTANCE_ID_ATTR, event.mInstanceId); - - switch (event.mEventType) { - case UsageEvents.Event.CONFIGURATION_CHANGE: - if (event.mConfiguration != null) { - Configuration.writeXmlAttrs(xml, event.mConfiguration); - } - break; - case UsageEvents.Event.SHORTCUT_INVOCATION: - if (event.mShortcutId != null) { - XmlUtils.writeStringAttribute(xml, SHORTCUT_ID_ATTR, event.mShortcutId); - } - break; - case UsageEvents.Event.STANDBY_BUCKET_CHANGED: - if (event.mBucketAndReason != 0) { - XmlUtils.writeIntAttribute(xml, STANDBY_BUCKET_ATTR, event.mBucketAndReason); - } - break; - case UsageEvents.Event.NOTIFICATION_INTERRUPTION: - if (event.mNotificationChannelId != null) { - XmlUtils.writeStringAttribute( - xml, NOTIFICATION_CHANNEL_ATTR, event.mNotificationChannelId); - } - break; - } - - xml.endTag(null, EVENT_TAG); - } - /** * Reads from the {@link XmlPullParser}, assuming that it is already on the * <code><usagestats></code> tag. @@ -440,51 +310,6 @@ final class UsageStatsXmlV1 { } } - /** - * Writes the stats object to an XML file. The {@link XmlSerializer} - * has already written the <code><usagestats></code> tag, but attributes may still - * be added. - * - * @param xml The serializer to which to write the packageStats data. - * @param stats The stats object to write to the XML file. - */ - public static void write(XmlSerializer xml, IntervalStats stats) throws IOException { - XmlUtils.writeLongAttribute(xml, END_TIME_ATTR, stats.endTime - stats.beginTime); - XmlUtils.writeIntAttribute(xml, MAJOR_VERSION_ATTR, stats.majorVersion); - XmlUtils.writeIntAttribute(xml, MINOR_VERSION_ATTR, stats.minorVersion); - - writeCountAndTime(xml, INTERACTIVE_TAG, stats.interactiveTracker.count, - stats.interactiveTracker.duration); - writeCountAndTime(xml, NON_INTERACTIVE_TAG, stats.nonInteractiveTracker.count, - stats.nonInteractiveTracker.duration); - writeCountAndTime(xml, KEYGUARD_SHOWN_TAG, stats.keyguardShownTracker.count, - stats.keyguardShownTracker.duration); - writeCountAndTime(xml, KEYGUARD_HIDDEN_TAG, stats.keyguardHiddenTracker.count, - stats.keyguardHiddenTracker.duration); - - xml.startTag(null, PACKAGES_TAG); - final int statsCount = stats.packageStats.size(); - for (int i = 0; i < statsCount; i++) { - writeUsageStats(xml, stats, stats.packageStats.valueAt(i)); - } - xml.endTag(null, PACKAGES_TAG); - - xml.startTag(null, CONFIGURATIONS_TAG); - final int configCount = stats.configurations.size(); - for (int i = 0; i < configCount; i++) { - boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i)); - writeConfigStats(xml, stats, stats.configurations.valueAt(i), active); - } - xml.endTag(null, CONFIGURATIONS_TAG); - - xml.startTag(null, EVENT_LOG_TAG); - final int eventCount = stats.events.size(); - for (int i = 0; i < eventCount; i++) { - writeEvent(xml, stats, stats.events.get(i)); - } - xml.endTag(null, EVENT_LOG_TAG); - } - private UsageStatsXmlV1() { } } diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index fa16b84b544c..20abe773e415 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -858,7 +858,7 @@ public abstract class Connection extends Conferenceable { } /** @hide */ - public abstract static class Listener { + abstract static class Listener { public void onStateChanged(Connection c, int state) {} public void onAddressChanged(Connection c, Uri newAddress, int presentation) {} public void onCallerDisplayNameChanged( @@ -2006,7 +2006,7 @@ public abstract class Connection extends Conferenceable { * * @hide */ - public final Connection addConnectionListener(Listener l) { + final Connection addConnectionListener(Listener l) { mListeners.add(l); return this; } @@ -2019,7 +2019,7 @@ public abstract class Connection extends Conferenceable { * * @hide */ - public final Connection removeConnectionListener(Listener l) { + final Connection removeConnectionListener(Listener l) { if (l != null) { mListeners.remove(l); } diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java index bc29b595b0bb..dc95f1678b0b 100644 --- a/telephony/java/android/provider/Telephony.java +++ b/telephony/java/android/provider/Telephony.java @@ -16,6 +16,7 @@ package android.provider; +import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; @@ -3943,10 +3944,11 @@ public final class Telephony { } /** - * Contains received SMS cell broadcast messages. More details are available in 3GPP TS 23.041. + * Contains received cell broadcast messages. More details are available in 3GPP TS 23.041. * @hide */ @SystemApi + @TestApi public static final class CellBroadcasts implements BaseColumns { /** @@ -3957,11 +3959,44 @@ public final class Telephony { /** * The {@code content://} URI for this table. + * Only privileged framework components running on phone or network stack uid can + * query or modify this table. */ @NonNull public static final Uri CONTENT_URI = Uri.parse("content://cellbroadcasts"); /** + * The {@code content://} URI for query cellbroadcast message history. + * query results include following entries + * <ul> + * <li>{@link #_ID}</li> + * <li>{@link #SLOT_INDEX}</li> + * <li>{@link #GEOGRAPHICAL_SCOPE}</li> + * <li>{@link #PLMN}</li> + * <li>{@link #LAC}</li> + * <li>{@link #CID}</li> + * <li>{@link #SERIAL_NUMBER}</li> + * <li>{@link #SERVICE_CATEGORY}</li> + * <li>{@link #LANGUAGE_CODE}</li> + * <li>{@link #MESSAGE_BODY}</li> + * <li>{@link #DELIVERY_TIME}</li> + * <li>{@link #MESSAGE_READ}</li> + * <li>{@link #MESSAGE_FORMAT}</li> + * <li>{@link #MESSAGE_PRIORITY}</li> + * <li>{@link #ETWS_WARNING_TYPE}</li> + * <li>{@link #CMAS_MESSAGE_CLASS}</li> + * <li>{@link #CMAS_CATEGORY}</li> + * <li>{@link #CMAS_RESPONSE_TYPE}</li> + * <li>{@link #CMAS_SEVERITY}</li> + * <li>{@link #CMAS_URGENCY}</li> + * <li>{@link #CMAS_CERTAINTY}</li> + * </ul> + */ + @RequiresPermission(Manifest.permission.READ_CELL_BROADCASTS) + @NonNull + public static final Uri MESSAGE_HISTORY_URI = Uri.parse("content://cellbroadcasts/history"); + + /** * The subscription which received this cell broadcast message. * @deprecated use {@link #SLOT_INDEX} instead. * <P>Type: INTEGER</P> @@ -3972,7 +4007,6 @@ public final class Telephony { /** * The slot which received this cell broadcast message. * <P>Type: INTEGER</P> - * @hide */ public static final String SLOT_INDEX = "slot_index"; @@ -4150,14 +4184,12 @@ public final class Telephony { /** * The timestamp in millisecond of when the device received the message. * <P>Type: BIGINT</P> - * @hide */ public static final String RECEIVED_TIME = "received_time"; /** * Indicates that whether the message has been broadcasted to the application. * <P>Type: BOOLEAN</P> - * @hide */ public static final String MESSAGE_BROADCASTED = "message_broadcasted"; @@ -4193,7 +4225,6 @@ public final class Telephony { * "circle|0,0|100;polygon|0,0|0,1.5|1,1|1,0;circle|100.123,100|200.123" * * <P>Type: TEXT</P> - * @hide */ public static final String GEOMETRIES = "geometries"; @@ -4205,7 +4236,6 @@ public final class Telephony { * for the alert. * * <P>Type: INTEGER</P> - * @hide */ public static final String MAXIMUM_WAIT_TIME = "maximum_wait_time"; diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 047b220a7a23..b564502630c5 100755..100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1448,6 +1448,15 @@ public class CarrierConfigManager { "stk_disable_launch_browser_bool"; /** + * Boolean indicating if the helper text for STK GET INKEY/INPUT commands with the digit only + * mode is displayed on the input screen. + * The helper text is dispayed regardless of the input mode, if {@code false}. + * @hide + */ + public static final String KEY_HIDE_DIGITS_HELPER_TEXT_ON_STK_INPUT_SCREEN_BOOL = + "hide_digits_helper_text_on_stk_input_screen_bool"; + + /** * Boolean indicating if show data RAT icon on status bar even when data is disabled * @hide */ @@ -1829,6 +1838,13 @@ public class CarrierConfigManager { "support_direct_fdn_dialing_bool"; /** + * Int indicating the max number length for FDN + * @hide + */ + public static final String KEY_FDN_NUMBER_LENGTH_LIMIT_INT = + "fdn_number_length_limit_int"; + + /** * Report IMEI as device id even if it's a CDMA/LTE phone. * * @hide @@ -3215,6 +3231,14 @@ public class CarrierConfigManager { public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY = "disconnect_cause_play_busytone_int_array"; + /** + * Flag specifying whether to prevent sending CLIR activation("*31#") and deactivation("#31#") + * code only without dialing number. + * When {@code true}, these are prevented, {@code false} otherwise. + */ + public static final String KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL = + "prevent_clir_activation_and_deactivation_code_bool"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -3417,6 +3441,7 @@ public class CarrierConfigManager { 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); + sDefaults.putInt(KEY_FDN_NUMBER_LENGTH_LIMIT_INT, 20); sDefaults.putBoolean(KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL, false); sDefaults.putBoolean(KEY_SKIP_CF_FAIL_TO_DISABLE_DIALOG_BOOL, false); sDefaults.putBoolean(KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL, true); @@ -3525,6 +3550,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL, false); sDefaults.putBoolean(KEY_STK_DISABLE_LAUNCH_BROWSER_BOOL, false); sDefaults.putBoolean(KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL, false); + sDefaults.putBoolean(KEY_HIDE_DIGITS_HELPER_TEXT_ON_STK_INPUT_SCREEN_BOOL, true); sDefaults.putStringArray(KEY_CARRIER_WIFI_STRING_ARRAY, null); sDefaults.putInt(KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT, -1); sDefaults.putInt(KEY_EMERGENCY_NOTIFICATION_DELAY_INT, -1); @@ -3647,6 +3673,7 @@ public class CarrierConfigManager { sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, null); sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY, new int[] {4 /* BUSY */}); + sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false); } /** diff --git a/telephony/java/android/telephony/CellBroadcastService.java b/telephony/java/android/telephony/CellBroadcastService.java index 46eb9df8bdad..60281ad11f60 100644 --- a/telephony/java/android/telephony/CellBroadcastService.java +++ b/telephony/java/android/telephony/CellBroadcastService.java @@ -17,12 +17,18 @@ package android.telephony; import android.annotation.CallSuper; +import android.annotation.NonNull; import android.annotation.SystemApi; import android.app.Service; import android.content.Intent; +import android.os.Bundle; import android.os.IBinder; +import android.os.RemoteCallback; import android.telephony.cdma.CdmaSmsCbProgramData; +import java.util.List; +import java.util.function.Consumer; + /** * A service which exposes the cell broadcast handling module to the system. * <p> @@ -46,6 +52,7 @@ import android.telephony.cdma.CdmaSmsCbProgramData; * </service> * </manifest> * }</pre> + * * @hide */ @SystemApi @@ -62,21 +69,38 @@ public abstract class CellBroadcastService extends Service { /** * Handle a GSM cell broadcast SMS message forwarded from the system. + * * @param slotIndex the index of the slot which received the message - * @param message the SMS PDU + * @param message the SMS PDU */ public abstract void onGsmCellBroadcastSms(int slotIndex, byte[] message); /** * Handle a CDMA cell broadcast SMS message forwarded from the system. - * @param slotIndex the index of the slot which received the message - * @param bearerData the CDMA SMS bearer data + * + * @param slotIndex the index of the slot which received the message + * @param bearerData the CDMA SMS bearer data * @param serviceCategory the CDMA SCPT service category */ public abstract void onCdmaCellBroadcastSms(int slotIndex, byte[] bearerData, @CdmaSmsCbProgramData.Category int serviceCategory); /** + * Handle a CDMA cell broadcast SMS message forwarded from the system. + * + * @param slotIndex the index of the slot which received the message + * @param smsCbProgramData the SMS CB program data of the message + * @param originatingAddress the originating address of the message, as a non-separated dial + * string + * @param callback a callback to run after each cell broadcast receiver has handled + * the SCP message. The bundle will contain a non-separated + * dial string as and an ArrayList of {@link CdmaSmsCbProgramResults}. + */ + public abstract void onCdmaScpMessage(int slotIndex, + @NonNull List<CdmaSmsCbProgramData> smsCbProgramData, + @NonNull String originatingAddress, @NonNull Consumer<Bundle> callback); + + /** * If overriding this method, call through to the super method for any unknown actions. * {@inheritDoc} */ @@ -89,13 +113,15 @@ public abstract class CellBroadcastService extends Service { /** * A wrapper around ICellBroadcastService that forwards calls to implementations of * {@link CellBroadcastService}. + * * @hide */ public class ICellBroadcastServiceWrapper extends ICellBroadcastService.Stub { /** * Handle a GSM cell broadcast SMS. + * * @param slotIndex the index of the slot which received the broadcast - * @param message the SMS message PDU + * @param message the SMS message PDU */ @Override public void handleGsmCellBroadcastSms(int slotIndex, byte[] message) { @@ -104,8 +130,9 @@ public abstract class CellBroadcastService extends Service { /** * Handle a CDMA cell broadcast SMS. - * @param slotIndex the index of the slot which received the broadcast - * @param bearerData the CDMA SMS bearer data + * + * @param slotIndex the index of the slot which received the broadcast + * @param bearerData the CDMA SMS bearer data * @param serviceCategory the CDMA SCPT service category */ @Override @@ -114,5 +141,25 @@ public abstract class CellBroadcastService extends Service { CellBroadcastService.this.onCdmaCellBroadcastSms(slotIndex, bearerData, serviceCategory); } + + /** + * Handle a CDMA Service Category Program message. + * + * @param slotIndex the index of the slot which received the message + * @param smsCbProgramData the SMS CB program data of the message + * @param originatingAddress the originating address of the message + * @param callback a callback to run after each cell broadcast receiver has + * handled the SCP message + */ + @Override + public void handleCdmaScpMessage(int slotIndex, + List<CdmaSmsCbProgramData> smsCbProgramData, String originatingAddress, + RemoteCallback callback) { + Consumer<Bundle> consumer = bundle -> { + callback.sendResult(bundle); + }; + CellBroadcastService.this.onCdmaScpMessage(slotIndex, smsCbProgramData, + originatingAddress, consumer); + } } } diff --git a/telephony/java/android/telephony/ICellBroadcastService.aidl b/telephony/java/android/telephony/ICellBroadcastService.aidl index bcd6cc546eed..11263d99cb8f 100644 --- a/telephony/java/android/telephony/ICellBroadcastService.aidl +++ b/telephony/java/android/telephony/ICellBroadcastService.aidl @@ -16,6 +16,9 @@ package android.telephony; +import android.os.RemoteCallback; +import android.telephony.cdma.CdmaSmsCbProgramData; + /** * Service bound to by the system to allow custom handling of cell broadcast messages. * <p> @@ -29,4 +32,8 @@ interface ICellBroadcastService { /** @see android.telephony.CellBroadcastService#onCdmaCellBroadcastSms */ oneway void handleCdmaCellBroadcastSms(int slotId, in byte[] bearerData, int serviceCategory); + + /** @see android.telephony.CellBroadcastService#onCdmaScpMessage */ + oneway void handleCdmaScpMessage(int slotId, in List<CdmaSmsCbProgramData> programData, + String originatingAddress, in RemoteCallback callback); } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 8425ec13b282..b5e91d0bad85 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -49,7 +49,6 @@ import android.os.Build; import android.os.Handler; import android.os.HandlerExecutor; import android.os.Looper; -import android.os.Message; import android.os.ParcelUuid; import android.os.Process; import android.os.RemoteException; @@ -2053,13 +2052,13 @@ public class SubscriptionManager { /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public static boolean isValidSlotIndex(int slotIndex) { - return slotIndex >= 0 && slotIndex < TelephonyManager.getDefault().getSupportedModemCount(); + return slotIndex >= 0 && slotIndex < TelephonyManager.getDefault().getActiveModemCount(); } /** @hide */ @UnsupportedAppUsage public static boolean isValidPhoneId(int phoneId) { - return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getSupportedModemCount(); + return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getActiveModemCount(); } /** @hide */ diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 2eb4809bcb27..8455e3d57e49 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1873,7 +1873,12 @@ public class TelephonyManager { if (telephony == null) return null; try { - return telephony.getMeidForSlot(slotIndex, getOpPackageName()); + String meid = telephony.getMeidForSlot(slotIndex, getOpPackageName()); + if (TextUtils.isEmpty(meid)) { + Log.d(TAG, "getMeid: return null because MEID is not available"); + return null; + } + return meid; } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -9507,10 +9512,12 @@ public class TelephonyManager { } /** - * Resets telephony manager settings back to factory defaults. + * Resets Telephony and IMS settings back to factory defaults. * * @hide */ + @SystemApi + @RequiresPermission(Manifest.permission.CONNECTIVITY_INTERNAL) public void factoryReset(int subId) { try { Log.d(TAG, "factoryReset: subId=" + subId); diff --git a/telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.aidl b/telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.aidl new file mode 100644 index 000000000000..a648a0e81073 --- /dev/null +++ b/telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.aidl @@ -0,0 +1,21 @@ +/* + * 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. + */ + +/** @hide */ +package android.telephony.cdma; + +parcelable CdmaSmsCbProgramData; + diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index 2161dcb9542f..a5d62669de05 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -1191,7 +1191,7 @@ public class ApnSetting implements Parcelable { && !other.canHandleType(TYPE_DUN) && Objects.equals(this.mApnName, other.mApnName) && !typeSameAny(this, other) - && xorEquals(this.mProxyAddress, other.mProxyAddress) + && xorEqualsString(this.mProxyAddress, other.mProxyAddress) && xorEqualsInt(this.mProxyPort, other.mProxyPort) && xorEquals(this.mProtocol, other.mProtocol) && xorEquals(this.mRoamingProtocol, other.mRoamingProtocol) @@ -1200,7 +1200,7 @@ public class ApnSetting implements Parcelable { && Objects.equals(this.mMvnoType, other.mMvnoType) && Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData) && xorEquals(this.mMmsc, other.mMmsc) - && xorEquals(this.mMmsProxyAddress, other.mMmsProxyAddress) + && xorEqualsString(this.mMmsProxyAddress, other.mMmsProxyAddress) && xorEqualsInt(this.mMmsProxyPort, other.mMmsProxyPort)) && Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask) && Objects.equals(mApnSetId, other.mApnSetId) @@ -1213,6 +1213,11 @@ public class ApnSetting implements Parcelable { return first == null || second == null || first.equals(second); } + // Equal or one is null. + private boolean xorEqualsString(String first, String second) { + return TextUtils.isEmpty(first) || TextUtils.isEmpty(second) || first.equals(second); + } + // Equal or one is not specified. private boolean xorEqualsInt(int first, int second) { return first == UNSPECIFIED_INT || second == UNSPECIFIED_INT diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index 2fad8479178e..7cafa1ea068c 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -25,30 +25,26 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; -import android.content.pm.IPackageManager; -import android.content.pm.PackageManager; -import android.net.Uri; import android.os.Binder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.telephony.AccessNetworkConstants; +import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.ims.aidl.IImsCapabilityCallback; -import android.telephony.ims.aidl.IImsRegistrationCallback; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.feature.MmTelFeature; import android.telephony.ims.stub.ImsRegistrationImplBase; -import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.IIntegerConsumer; import com.android.internal.telephony.ITelephony; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * A manager for the MmTel (Multimedia Telephony) feature of an IMS network, given an associated @@ -62,9 +58,7 @@ import java.util.concurrent.Executor; * @hide */ @SystemApi -public class ImsMmTelManager { - - private static final String TAG = "ImsMmTelManager"; +public class ImsMmTelManager implements RegistrationManager { /** * @hide @@ -97,94 +91,18 @@ public class ImsMmTelManager { * Callback class for receiving IMS network Registration callback events. * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback) * @see #unregisterImsRegistrationCallback(RegistrationCallback) + * @deprecated Use {@link RegistrationManager.RegistrationCallback} instead. */ - public static class RegistrationCallback { - - private static class RegistrationBinder extends IImsRegistrationCallback.Stub { - - // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN - // and WWAN are more accurate constants. - private static final Map<Integer, Integer> IMS_REG_TO_ACCESS_TYPE_MAP = - new HashMap<Integer, Integer>() {{ - // Map NONE to -1 to make sure that we handle the REGISTRATION_TECH_NONE - // case, since it is defined. - put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, -1); - put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE, - AccessNetworkConstants.TRANSPORT_TYPE_WWAN); - put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, - AccessNetworkConstants.TRANSPORT_TYPE_WLAN); - }}; - - private final RegistrationCallback mLocalCallback; - private Executor mExecutor; - - RegistrationBinder(RegistrationCallback localCallback) { - mLocalCallback = localCallback; - } - - @Override - public void onRegistered(int imsRadioTech) { - if (mLocalCallback == null) return; - - Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> - mLocalCallback.onRegistered(getAccessType(imsRadioTech)))); - } - - @Override - public void onRegistering(int imsRadioTech) { - if (mLocalCallback == null) return; - - Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> - mLocalCallback.onRegistering(getAccessType(imsRadioTech)))); - } - - @Override - public void onDeregistered(ImsReasonInfo info) { - if (mLocalCallback == null) return; - - Binder.withCleanCallingIdentity(() -> - mExecutor.execute(() -> mLocalCallback.onUnregistered(info))); - } - - @Override - public void onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) { - if (mLocalCallback == null) return; - - Binder.withCleanCallingIdentity(() -> - mExecutor.execute(() -> mLocalCallback.onTechnologyChangeFailed( - getAccessType(imsRadioTech), info))); - } - - @Override - public void onSubscriberAssociatedUriChanged(Uri[] uris) { - if (mLocalCallback == null) return; - - Binder.withCleanCallingIdentity(() -> - mExecutor.execute(() -> - mLocalCallback.onSubscriberAssociatedUriChanged(uris))); - } - - private void setExecutor(Executor executor) { - mExecutor = executor; - } - - private static int getAccessType(int regType) { - if (!IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regType)) { - Log.w("ImsMmTelManager", "RegistrationBinder - invalid regType returned: " - + regType); - return -1; - } - return IMS_REG_TO_ACCESS_TYPE_MAP.get(regType); - } - } - - private final RegistrationBinder mBinder = new RegistrationBinder(this); + // Do not add to this class, add to RegistrationManager.RegistrationCallback instead. + @Deprecated + public static class RegistrationCallback extends RegistrationManager.RegistrationCallback { /** * Notifies the framework when the IMS Provider is registered to the IMS network. * * @param imsTransportType the radio access technology. */ + @Override public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) { } @@ -193,6 +111,7 @@ public class ImsMmTelManager { * * @param imsTransportType the radio access technology. */ + @Override public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) { } @@ -201,6 +120,7 @@ public class ImsMmTelManager { * * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. */ + @Override public void onUnregistered(@Nullable ImsReasonInfo info) { } @@ -210,33 +130,11 @@ public class ImsMmTelManager { * @param imsTransportType The transport type that has failed to handover registration to. * @param info A {@link ImsReasonInfo} that identifies the reason for failure. */ + @Override public void onTechnologyChangeFailed( @AccessNetworkConstants.TransportType int imsTransportType, @Nullable ImsReasonInfo info) { } - - /** - * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when - * it changes. Per RFC3455, an associated URI is a URI that the service provider has - * allocated to a user for their own usage. A user's phone number is typically one of the - * associated URIs. - * @param uris new array of subscriber {@link Uri}s that are associated with this IMS - * subscription. - * @hide - */ - public void onSubscriberAssociatedUriChanged(@Nullable Uri[] uris) { - } - - /**@hide*/ - public final IImsRegistrationCallback getBinder() { - return mBinder; - } - - /**@hide*/ - //Only exposed as public for compatibility with deprecated ImsManager APIs. - public void setExecutor(Executor executor) { - mBinder.setExecutor(executor); - } } /** @@ -311,7 +209,7 @@ public class ImsMmTelManager { } } - private int mSubId; + private final int mSubId; /** * Create an instance of {@link ImsMmTelManager} for the subscription id specified. @@ -356,7 +254,10 @@ public class ImsMmTelManager { * the {@link ImsService} associated with the subscription is not available. This can happen if * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed * reason. + * @deprecated Use {@link #registerImsRegistrationCallback( + * RegistrationManager.RegistrationCallback, Executor)} instead. */ + @Deprecated @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor, @NonNull RegistrationCallback c) throws ImsException { @@ -366,10 +267,6 @@ public class ImsMmTelManager { if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } - if (!isImsAvailableOnDevice()) { - throw new ImsException("IMS not available on device.", - ImsException.CODE_ERROR_UNSUPPORTED_OPERATION); - } c.setExecutor(executor); try { getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder()); @@ -378,13 +275,35 @@ public class ImsMmTelManager { // Rethrow as runtime error to keep API compatible. throw new IllegalArgumentException(e.getMessage()); } else { - throw new RuntimeException(e.getMessage()); + throw new ImsException(e.getMessage(), e.errorCode); } } catch (RemoteException | IllegalStateException e) { throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } + /**{@inheritDoc}*/ + @Override + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void registerImsRegistrationCallback( + @NonNull RegistrationManager.RegistrationCallback c, + @NonNull @CallbackExecutor Executor executor) throws ImsException { + if (c == null) { + throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); + } + if (executor == null) { + throw new IllegalArgumentException("Must include a non-null Executor."); + } + c.setExecutor(executor); + try { + getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder()); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); + } catch (RemoteException | IllegalStateException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + /** * Removes an existing {@link RegistrationCallback}. * @@ -395,7 +314,10 @@ public class ImsMmTelManager { * @param c The {@link RegistrationCallback} to be removed. * @see SubscriptionManager.OnSubscriptionsChangedListener * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) + * @deprecated Use {@link #unregisterImsRegistrationCallback( + * RegistrationManager.RegistrationCallback)}. */ + @Deprecated @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) { if (c == null) { @@ -408,6 +330,69 @@ public class ImsMmTelManager { } } + /**{@inheritDoc}*/ + @Override + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void unregisterImsRegistrationCallback( + @NonNull RegistrationManager.RegistrationCallback c) { + if (c == null) { + throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); + } + try { + getITelephony().unregisterImsRegistrationCallback(mSubId, c.getBinder()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /**{@inheritDoc}*/ + @Override + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void getRegistrationState(@NonNull @ImsRegistrationState Consumer<Integer> stateCallback, + @NonNull @CallbackExecutor Executor executor) { + if (stateCallback == null) { + throw new IllegalArgumentException("Must include a non-null callback."); + } + if (executor == null) { + throw new IllegalArgumentException("Must include a non-null Executor."); + } + try { + getITelephony().getImsMmTelRegistrationState(mSubId, new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + executor.execute(() -> stateCallback.accept(result)); + } + }); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /**{@inheritDoc}*/ + @Override + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void getRegistrationTransportType( + @NonNull @AccessNetworkConstants.TransportType Consumer<Integer> transportTypeCallback, + @NonNull @CallbackExecutor Executor executor) { + if (transportTypeCallback == null) { + throw new IllegalArgumentException("Must include a non-null callback."); + } + if (executor == null) { + throw new IllegalArgumentException("Must include a non-null Executor."); + } + try { + getITelephony().getImsMmTelRegistrationTransportType(mSubId, + new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + executor.execute(() -> transportTypeCallback.accept(result)); + } + }); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + /** * Registers a {@link CapabilityCallback} with the system, which will provide MmTel service * availability updates for the subscription specified in @@ -416,7 +401,7 @@ public class ImsMmTelManager { * * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to * subscription changed events and call - * {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up. + * {@link #unregisterMmTelCapabilityCallback(CapabilityCallback)} to clean up. * * When the callback is registered, it will initiate the callback c to be called with the * current capabilities. @@ -441,10 +426,6 @@ public class ImsMmTelManager { if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } - if (!isImsAvailableOnDevice()) { - throw new ImsException("IMS not available on device.", - ImsException.CODE_ERROR_UNSUPPORTED_OPERATION); - } c.setExecutor(executor); try { getITelephony().registerMmTelCapabilityCallback(mSubId, c.getBinder()); @@ -453,7 +434,7 @@ public class ImsMmTelManager { // Rethrow as runtime error to keep API compatible. throw new IllegalArgumentException(e.getMessage()); } else { - throw new RuntimeException(e.getMessage()); + throw new ImsException(e.getMessage(), e.errorCode); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); @@ -618,6 +599,46 @@ public class ImsMmTelManager { } /** + * Query whether or not the requested MmTel capability is supported by the carrier on the + * specified network transport. + * <p> + * This is a configuration option and does not change. The only time this may change is if a + * new IMS configuration is loaded when there is a + * {@link CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED} broadcast for this subscription. + * @param capability The capability that is being queried for support on the carrier network. + * @param transportType The transport type of the capability to check support for. + * @param callback A consumer containing a Boolean result specifying whether or not the + * capability is supported on this carrier network for the transport specified. + * @param executor The executor that the callback will be called with. + * @throws ImsException if the subscription is no longer valid or the IMS service is not + * available. + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void isSupported(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, + @AccessNetworkConstants.TransportType int transportType, + @NonNull Consumer<Boolean> callback, + @NonNull @CallbackExecutor Executor executor) throws ImsException { + if (callback == null) { + throw new IllegalArgumentException("Must include a non-null Consumer."); + } + if (executor == null) { + throw new IllegalArgumentException("Must include a non-null Executor."); + } + try { + getITelephony().isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + executor.execute(() -> callback.accept(result == 1)); + } + }, capability, transportType); + } catch (ServiceSpecificException sse) { + throw new ImsException(sse.getMessage(), sse.errorCode); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } + + /** * The user's setting for whether or not they have enabled the "Video Calling" setting. * * @throws IllegalArgumentException if the subscription associated with this operation is not @@ -940,7 +961,7 @@ public class ImsMmTelManager { * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - boolean isTtyOverVolteEnabled() { + public boolean isTtyOverVolteEnabled() { try { return getITelephony().isTtyOverVolteEnabled(mSubId); } catch (ServiceSpecificException e) { @@ -955,20 +976,39 @@ public class ImsMmTelManager { } } - private static boolean isImsAvailableOnDevice() { - IPackageManager pm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); - if (pm == null) { - // For some reason package manger is not available.. This will fail internally anyways, - // so do not throw error and allow. - return true; + /** + * Get the status of the MmTel Feature registered on this subscription. + * @param callback A callback containing an Integer describing the current state of the + * MmTel feature, Which will be one of the following: + * {@link ImsFeature#STATE_UNAVAILABLE}, + * {@link ImsFeature#STATE_INITIALIZING}, + * {@link ImsFeature#STATE_READY}. Will be called using the executor + * specified when the service state has been retrieved from the IMS service. + * @param executor The executor that will be used to call the callback. + * @throws ImsException if the IMS service associated with this subscription is not available or + * the IMS service is not available. + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void getFeatureState(@NonNull @ImsFeature.ImsState Consumer<Integer> callback, + @NonNull @CallbackExecutor Executor executor) throws ImsException { + if (callback == null) { + throw new IllegalArgumentException("Must include a non-null Consumer."); + } + if (executor == null) { + throw new IllegalArgumentException("Must include a non-null Executor."); } try { - return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS, 0); + getITelephony().getImsMmTelFeatureState(mSubId, new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + executor.execute(() -> callback.accept(result)); + } + }); + } catch (ServiceSpecificException sse) { + throw new ImsException(sse.getMessage(), sse.errorCode); } catch (RemoteException e) { - // For some reason package manger is not available.. This will fail internally anyways, - // so do not throw error and allow. + e.rethrowAsRuntimeException(); } - return true; } private static ITelephony getITelephony() { diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java index 3c343dd19a86..25bd1caea431 100644 --- a/telephony/java/android/telephony/ims/ImsRcsManager.java +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -22,12 +22,14 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.content.Context; import android.os.Binder; +import android.telephony.AccessNetworkConstants; import android.telephony.SubscriptionManager; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.feature.RcsFeature; import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * Manager for interfacing with the framework RCS services, including the User Capability Exchange @@ -36,7 +38,7 @@ import java.util.concurrent.Executor; * Use {@link #createForSubscriptionId(Context, int)} to create an instance of this manager. * @hide */ -public class ImsRcsManager { +public class ImsRcsManager implements RegistrationManager { /** * Receives RCS availability status updates from the ImsService. @@ -136,6 +138,64 @@ public class ImsRcsManager { mSubId = subId; } + /**{@inheritDoc}*/ + @Override + public void registerImsRegistrationCallback( + @NonNull RegistrationManager.RegistrationCallback c, + @NonNull @CallbackExecutor Executor executor) + throws ImsException { + if (c == null) { + throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); + } + if (executor == null) { + throw new IllegalArgumentException("Must include a non-null Executor."); + } + c.setExecutor(executor); + throw new UnsupportedOperationException("registerImsRegistrationCallback is not" + + "supported."); + } + + /**{@inheritDoc}*/ + @Override + public void unregisterImsRegistrationCallback( + @NonNull RegistrationManager.RegistrationCallback c) { + if (c == null) { + throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); + } + throw new UnsupportedOperationException("unregisterImsRegistrationCallback is not" + + "supported."); + } + + /**{@inheritDoc}*/ + @Override + public void getRegistrationState(@NonNull @ImsRegistrationState Consumer<Integer> stateCallback, + @NonNull @CallbackExecutor Executor executor) { + if (stateCallback == null) { + throw new IllegalArgumentException("Must include a non-null stateCallback."); + } + if (executor == null) { + throw new IllegalArgumentException("Must include a non-null Executor."); + } + throw new UnsupportedOperationException("getRegistrationState is not" + + "supported."); + } + + /**{@inheritDoc}*/ + @Override + public void getRegistrationTransportType( + @NonNull @AccessNetworkConstants.TransportType Consumer<Integer> transportTypeCallback, + @NonNull @CallbackExecutor Executor executor) { + if (transportTypeCallback == null) { + throw new IllegalArgumentException("Must include a non-null transportTypeCallback."); + } + if (executor == null) { + throw new IllegalArgumentException("Must include a non-null Executor."); + } + throw new UnsupportedOperationException("getRegistrationTransportType is not" + + "supported."); + } + + /** * Registers an {@link AvailabilityCallback} with the system, which will provide RCS * availability updates for the subscription specified. diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java new file mode 100644 index 000000000000..b4c11e3c32a1 --- /dev/null +++ b/telephony/java/android/telephony/ims/RegistrationManager.java @@ -0,0 +1,285 @@ +/* + * 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.telephony.ims; + +import android.Manifest; +import android.annotation.CallbackExecutor; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.net.Uri; +import android.os.Binder; +import android.telephony.AccessNetworkConstants; +import android.telephony.ims.aidl.IImsRegistrationCallback; +import android.telephony.ims.feature.ImsFeature; +import android.telephony.ims.stub.ImsRegistrationImplBase; +import android.util.Log; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** + * Manages IMS Service registration state for associated {@link ImsFeature}s. + * @hide + */ +@SystemApi +public interface RegistrationManager { + + /** + * @hide + */ + // Defines the underlying radio technology type that we have registered for IMS over. + @IntDef(prefix = "REGISTRATION_STATE_", + value = { + REGISTRATION_STATE_NOT_REGISTERED, + REGISTRATION_STATE_REGISTERING, + REGISTRATION_STATE_REGISTERED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ImsRegistrationState {} + + /** + * The IMS service is currently not registered to the carrier network. + */ + int REGISTRATION_STATE_NOT_REGISTERED = 0; + + /** + * The IMS service is currently in the process of registering to the carrier network. + */ + int REGISTRATION_STATE_REGISTERING = 1; + + /** + * The IMS service is currently registered to the carrier network. + */ + int REGISTRATION_STATE_REGISTERED = 2; + + + /**@hide*/ + // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN + // and WWAN are more accurate constants. + Map<Integer, Integer> IMS_REG_TO_ACCESS_TYPE_MAP = + new HashMap<Integer, Integer>() {{ + // Map NONE to -1 to make sure that we handle the REGISTRATION_TECH_NONE + // case, since it is defined. + put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, -1); + put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE, + AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN); + }}; + + /** + * Callback class for receiving IMS network Registration callback events. + * @see #registerImsRegistrationCallback(RegistrationCallback, Executor) + * @see #unregisterImsRegistrationCallback(RegistrationCallback) + */ + class RegistrationCallback { + + private static class RegistrationBinder extends IImsRegistrationCallback.Stub { + + private final RegistrationCallback mLocalCallback; + private Executor mExecutor; + + RegistrationBinder(RegistrationCallback localCallback) { + mLocalCallback = localCallback; + } + + @Override + public void onRegistered(int imsRadioTech) { + if (mLocalCallback == null) return; + + Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> + mLocalCallback.onRegistered(getAccessType(imsRadioTech)))); + } + + @Override + public void onRegistering(int imsRadioTech) { + if (mLocalCallback == null) return; + + Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> + mLocalCallback.onRegistering(getAccessType(imsRadioTech)))); + } + + @Override + public void onDeregistered(ImsReasonInfo info) { + if (mLocalCallback == null) return; + + Binder.withCleanCallingIdentity(() -> + mExecutor.execute(() -> mLocalCallback.onUnregistered(info))); + } + + @Override + public void onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) { + if (mLocalCallback == null) return; + + Binder.withCleanCallingIdentity(() -> + mExecutor.execute(() -> mLocalCallback.onTechnologyChangeFailed( + getAccessType(imsRadioTech), info))); + } + + @Override + public void onSubscriberAssociatedUriChanged(Uri[] uris) { + if (mLocalCallback == null) return; + + Binder.withCleanCallingIdentity(() -> + mExecutor.execute(() -> + mLocalCallback.onSubscriberAssociatedUriChanged(uris))); + } + + private void setExecutor(Executor executor) { + mExecutor = executor; + } + + private static int getAccessType(int regType) { + if (!RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regType)) { + Log.w("RegistrationManager", "RegistrationBinder - invalid regType returned: " + + regType); + return -1; + } + return RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(regType); + } + } + + private final RegistrationBinder mBinder = new RegistrationBinder(this); + + /** + * Notifies the framework when the IMS Provider is registered to the IMS network. + * + * @param imsTransportType the radio access technology. + */ + public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) { + } + + /** + * Notifies the framework when the IMS Provider is trying to register the IMS network. + * + * @param imsTransportType the radio access technology. + */ + public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) { + } + + /** + * Notifies the framework when the IMS Provider is deregistered from the IMS network. + * + * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. + */ + public void onUnregistered(@Nullable ImsReasonInfo info) { + } + + /** + * A failure has occurred when trying to handover registration to another technology type. + * + * @param imsTransportType The transport type that has failed to handover registration to. + * @param info A {@link ImsReasonInfo} that identifies the reason for failure. + */ + public void onTechnologyChangeFailed( + @AccessNetworkConstants.TransportType int imsTransportType, + @Nullable ImsReasonInfo info) { + } + + /** + * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when + * it changes. Per RFC3455, an associated URI is a URI that the service provider has + * allocated to a user for their own usage. A user's phone number is typically one of the + * associated URIs. + * @param uris new array of subscriber {@link Uri}s that are associated with this IMS + * subscription. + * @hide + */ + public void onSubscriberAssociatedUriChanged(@Nullable Uri[] uris) { + } + + /**@hide*/ + public final IImsRegistrationCallback getBinder() { + return mBinder; + } + + /**@hide*/ + //Only exposed as public for compatibility with deprecated ImsManager APIs. + public void setExecutor(Executor executor) { + mBinder.setExecutor(executor); + } + } + + /** + * Registers a {@link RegistrationCallback} with the system. Use + * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed + * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up. + * + * When the callback is registered, it will initiate the callback c to be called with the + * current registration state. + * + * @param c The {@link RegistrationCallback} to be added. + * @param executor The executor the callback events should be run on. + * @see #unregisterImsRegistrationCallback(RegistrationCallback) + * @throws ImsException if the subscription associated with this callback is valid, but + * the {@link ImsService} associated with the subscription is not available. This can happen if + * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed + * reason. + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + void registerImsRegistrationCallback(@NonNull RegistrationCallback c, + @NonNull @CallbackExecutor Executor executor) throws ImsException; + + /** + * Removes an existing {@link RegistrationCallback}. + * + * When the subscription associated with this callback is removed (SIM removed, ESIM swap, + * etc...), this callback will automatically be removed. If this method is called for an + * inactive subscription, it will result in a no-op. + * + * @param c The {@link RegistrationCallback} to be removed. + * @see SubscriptionManager.OnSubscriptionsChangedListener + * @see #registerImsRegistrationCallback(RegistrationCallback, Executor) + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c); + + /** + * Gets the registration state of the IMS service. + * @param stateCallback A callback called on the supplied {@link Executor} that will contain the + * registration state of the IMS service, which will be one of the + * following: {@link #REGISTRATION_STATE_NOT_REGISTERED}, + * {@link #REGISTRATION_STATE_REGISTERING}, or + * {@link #REGISTRATION_STATE_REGISTERED}. + * @param executor The {@link Executor} that will be used to call the IMS registration state + * callback. + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + void getRegistrationState(@NonNull @ImsRegistrationState Consumer<Integer> stateCallback, + @NonNull @CallbackExecutor Executor executor); + + /** + * Gets the Transport Type associated with the current IMS registration. + * @param transportTypeCallback The transport type associated with the current IMS registration, + * which will be one of following: + * {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN}, + * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, or + * {@link AccessNetworkConstants#TRANSPORT_TYPE_INVALID}. + * @param executor The {@link Executor} that will be used to call the transportTypeCallback. + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + void getRegistrationTransportType( + @NonNull @AccessNetworkConstants.TransportType Consumer<Integer> transportTypeCallback, + @NonNull @CallbackExecutor Executor executor); +} diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java index ceb470491dc5..8b27b6fc4bfd 100644 --- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java +++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java @@ -362,6 +362,25 @@ public class MmTelFeature extends ImsFeature { @Retention(RetentionPolicy.SOURCE) public @interface ProcessCallResult {} + /** + * If the flag is present and true, it indicates that the incoming call is for USSD. + * <p> + * This is an optional boolean flag. + */ + public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD"; + + /** + * If this flag is present and true, this call is marked as an unknown dialing call instead + * of an incoming call. An example of such a call is a call that is originated by sending + * commands (like AT commands) directly to the modem without Android involvement or dialing + * calls appearing over IMS when the modem does a silent redial from circuit-switched to IMS in + * certain situations. + * <p> + * This is an optional boolean flag. + */ + public static final String EXTRA_IS_UNKNOWN_CALL = + "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL"; + private IImsMmTelListener mListener; /** @@ -410,6 +429,8 @@ public class MmTelFeature extends ImsFeature { /** * Notify the framework of an incoming call. * @param c The {@link ImsCallSessionImplBase} of the new incoming call. + * @param extras A bundle containing extra parameters related to the call. See + * {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above. */ public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c, @NonNull Bundle extras) { diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java index a08e0313bb5b..b455c2eff803 100644 --- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java @@ -22,6 +22,7 @@ import android.net.Uri; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.telephony.ims.ImsReasonInfo; +import android.telephony.ims.RegistrationManager; import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.aidl.IImsRegistrationCallback; import android.util.Log; @@ -72,9 +73,6 @@ public class ImsRegistrationImplBase { // with NOT_REGISTERED in the case where the ImsService has not updated the registration state // yet. private static final int REGISTRATION_STATE_UNKNOWN = -1; - private static final int REGISTRATION_STATE_NOT_REGISTERED = 0; - private static final int REGISTRATION_STATE_REGISTERING = 1; - private static final int REGISTRATION_STATE_REGISTERED = 2; private final IImsRegistration mBinder = new IImsRegistration.Stub() { @@ -128,7 +126,7 @@ public class ImsRegistrationImplBase { * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}. */ public final void onRegistered(@ImsRegistrationTech int imsRadioTech) { - updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERED); + updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERED); mCallbacks.broadcast((c) -> { try { c.onRegistered(imsRadioTech); @@ -146,7 +144,7 @@ public class ImsRegistrationImplBase { * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}. */ public final void onRegistering(@ImsRegistrationTech int imsRadioTech) { - updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERING); + updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERING); mCallbacks.broadcast((c) -> { try { c.onRegistering(imsRadioTech); @@ -230,7 +228,8 @@ public class ImsRegistrationImplBase { private void updateToDisconnectedState(ImsReasonInfo info) { synchronized (mLock) { - updateToState(REGISTRATION_TECH_NONE, REGISTRATION_STATE_NOT_REGISTERED); + updateToState(REGISTRATION_TECH_NONE, + RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED); if (info != null) { mLastDisconnectCause = info; } else { @@ -264,15 +263,15 @@ public class ImsRegistrationImplBase { disconnectInfo = mLastDisconnectCause; } switch (state) { - case REGISTRATION_STATE_NOT_REGISTERED: { + case RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED: { c.onDeregistered(disconnectInfo); break; } - case REGISTRATION_STATE_REGISTERING: { + case RegistrationManager.REGISTRATION_STATE_REGISTERING: { c.onRegistering(getConnectionType()); break; } - case REGISTRATION_STATE_REGISTERED: { + case RegistrationManager.REGISTRATION_STATE_REGISTERED: { c.onRegistered(getConnectionType()); break; } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index f79a5c654a63..ba3ffd461316 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -867,6 +867,11 @@ interface ITelephony { String getImsService(int slotId, boolean isCarrierImsService); /** + * Get the MmTelFeature state attached to this subscription id. + */ + void getImsMmTelFeatureState(int subId, IIntegerConsumer callback); + + /** * Set the network selection mode to automatic. * * @param subId the id of the subscription to update. @@ -1795,6 +1800,16 @@ interface ITelephony { void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback c); /** + * Get the IMS service registration state for the MmTelFeature associated with this sub id. + */ + void getImsMmTelRegistrationState(int subId, IIntegerConsumer consumer); + + /** + * Get the transport type for the IMS service registration state. + */ + void getImsMmTelRegistrationTransportType(int subId, IIntegerConsumer consumer); + + /** * Adds an IMS MmTel capabilities callback for the subscription specified. */ void registerMmTelCapabilityCallback(int subId, IImsCapabilityCallback c); @@ -1815,6 +1830,12 @@ interface ITelephony { boolean isAvailable(int subId, int capability, int regTech); /** + * Return whether or not the MmTel capability is supported for the requested transport type. + */ + void isMmTelCapabilitySupported(int subId, IIntegerConsumer callback, int capability, + int transportType); + + /** * Returns true if the user's setting for 4G LTE is enabled, for the subscription specified. */ boolean isAdvancedCallingSettingEnabled(int subId); diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java index 4d8c7d930bde..9d3e12050193 100644 --- a/test-mock/src/android/test/mock/MockContentProvider.java +++ b/test-mock/src/android/test/mock/MockContentProvider.java @@ -56,21 +56,22 @@ public class MockContentProvider extends ContentProvider { */ private class InversionIContentProvider implements IContentProvider { @Override - public ContentProviderResult[] applyBatch(String callingPackage, String authority, + public ContentProviderResult[] applyBatch(String callingPackage, + @Nullable String featureId, String authority, ArrayList<ContentProviderOperation> operations) throws RemoteException, OperationApplicationException { return MockContentProvider.this.applyBatch(authority, operations); } @Override - public int bulkInsert(String callingPackage, Uri url, ContentValues[] initialValues) - throws RemoteException { + public int bulkInsert(String callingPackage, @Nullable String featureId, Uri url, + ContentValues[] initialValues) throws RemoteException { return MockContentProvider.this.bulkInsert(url, initialValues); } @Override - public int delete(String callingPackage, Uri url, String selection, String[] selectionArgs) - throws RemoteException { + public int delete(String callingPackage, @Nullable String featureId, Uri url, + String selection, String[] selectionArgs) throws RemoteException { return MockContentProvider.this.delete(url, selection, selectionArgs); } @@ -80,42 +81,42 @@ public class MockContentProvider extends ContentProvider { } @Override - public Uri insert(String callingPackage, Uri url, ContentValues initialValues) - throws RemoteException { + public Uri insert(String callingPackage, @Nullable String featureId, Uri url, + ContentValues initialValues) throws RemoteException { return MockContentProvider.this.insert(url, initialValues); } @Override - public AssetFileDescriptor openAssetFile( - String callingPackage, Uri url, String mode, ICancellationSignal signal) + public AssetFileDescriptor openAssetFile(String callingPackage, + @Nullable String featureId, Uri url, String mode, ICancellationSignal signal) throws RemoteException, FileNotFoundException { return MockContentProvider.this.openAssetFile(url, mode); } @Override - public ParcelFileDescriptor openFile( - String callingPackage, Uri url, String mode, ICancellationSignal signal, - IBinder callerToken) throws RemoteException, FileNotFoundException { + public ParcelFileDescriptor openFile(String callingPackage, @Nullable String featureId, + Uri url, String mode, ICancellationSignal signal, IBinder callerToken) + throws RemoteException, FileNotFoundException { return MockContentProvider.this.openFile(url, mode); } @Override - public Cursor query(String callingPackage, Uri url, @Nullable String[] projection, - @Nullable Bundle queryArgs, - @Nullable ICancellationSignal cancellationSignal) - throws RemoteException { + public Cursor query(String callingPackage, @Nullable String featureId, Uri url, + @Nullable String[] projection, @Nullable Bundle queryArgs, + @Nullable ICancellationSignal cancellationSignal) throws RemoteException { return MockContentProvider.this.query(url, projection, queryArgs, null); } @Override - public int update(String callingPackage, Uri url, ContentValues values, String selection, - String[] selectionArgs) throws RemoteException { + public int update(String callingPackage, @Nullable String featureId, Uri url, + ContentValues values, String selection, String[] selectionArgs) + throws RemoteException { return MockContentProvider.this.update(url, values, selection, selectionArgs); } @Override - public Bundle call(String callingPackage, String authority, String method, String request, - Bundle args) throws RemoteException { + public Bundle call(String callingPackage, @Nullable String featureId, String authority, + String method, String request, Bundle args) throws RemoteException { return MockContentProvider.this.call(authority, method, request, args); } @@ -130,9 +131,9 @@ public class MockContentProvider extends ContentProvider { } @Override - public AssetFileDescriptor openTypedAssetFile(String callingPackage, Uri url, - String mimeType, Bundle opts, ICancellationSignal signal) - throws RemoteException, FileNotFoundException { + public AssetFileDescriptor openTypedAssetFile(String callingPackage, + @Nullable String featureId, Uri url, String mimeType, Bundle opts, + ICancellationSignal signal) throws RemoteException, FileNotFoundException { return MockContentProvider.this.openTypedAssetFile(url, mimeType, opts); } @@ -142,23 +143,26 @@ public class MockContentProvider extends ContentProvider { } @Override - public Uri canonicalize(String callingPkg, Uri uri) throws RemoteException { + public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) + throws RemoteException { return MockContentProvider.this.canonicalize(uri); } @Override - public Uri uncanonicalize(String callingPkg, Uri uri) throws RemoteException { + public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri) + throws RemoteException { return MockContentProvider.this.uncanonicalize(uri); } @Override - public boolean refresh(String callingPkg, Uri url, Bundle args, - ICancellationSignal cancellationSignal) throws RemoteException { + public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, + Bundle args, ICancellationSignal cancellationSignal) throws RemoteException { return MockContentProvider.this.refresh(url, args); } @Override - public int checkUriPermission(String callingPkg, Uri uri, int uid, int modeFlags) { + public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri, + int uid, int modeFlags) { return MockContentProvider.this.checkUriPermission(uri, uid, modeFlags); } } diff --git a/test-mock/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java index b072d7440de4..e512b52643f3 100644 --- a/test-mock/src/android/test/mock/MockIContentProvider.java +++ b/test-mock/src/android/test/mock/MockIContentProvider.java @@ -16,14 +16,12 @@ package android.test.mock; -import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; import android.content.ContentValues; import android.content.EntityIterator; import android.content.IContentProvider; -import android.content.Intent; import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.net.Uri; @@ -45,14 +43,15 @@ import java.util.ArrayList; */ public class MockIContentProvider implements IContentProvider { @Override - public int bulkInsert(String callingPackage, Uri url, ContentValues[] initialValues) { + public int bulkInsert(String callingPackage, @Nullable String featureId, Uri url, + ContentValues[] initialValues) { throw new UnsupportedOperationException("unimplemented mock method"); } @Override @SuppressWarnings("unused") - public int delete(String callingPackage, Uri url, String selection, String[] selectionArgs) - throws RemoteException { + public int delete(String callingPackage, @Nullable String featureId, Uri url, + String selection, String[] selectionArgs) throws RemoteException { throw new UnsupportedOperationException("unimplemented mock method"); } @@ -63,33 +62,33 @@ public class MockIContentProvider implements IContentProvider { @Override @SuppressWarnings("unused") - public Uri insert(String callingPackage, Uri url, ContentValues initialValues) - throws RemoteException { + public Uri insert(String callingPackage, @Nullable String featureId, Uri url, + ContentValues initialValues) throws RemoteException { throw new UnsupportedOperationException("unimplemented mock method"); } @Override - public ParcelFileDescriptor openFile( - String callingPackage, Uri url, String mode, ICancellationSignal signal, - IBinder callerToken) { + public ParcelFileDescriptor openFile(String callingPackage, @Nullable String featureId, + Uri url, String mode, ICancellationSignal signal, IBinder callerToken) { throw new UnsupportedOperationException("unimplemented mock method"); } @Override - public AssetFileDescriptor openAssetFile( - String callingPackage, Uri uri, String mode, ICancellationSignal signal) { + public AssetFileDescriptor openAssetFile(String callingPackage, @Nullable String featureId, + Uri uri, String mode, ICancellationSignal signal) { throw new UnsupportedOperationException("unimplemented mock method"); } @Override - public ContentProviderResult[] applyBatch(String callingPackage, String authority, - ArrayList<ContentProviderOperation> operations) { + public ContentProviderResult[] applyBatch(String callingPackage, @Nullable String featureId, + String authority, ArrayList<ContentProviderOperation> operations) { throw new UnsupportedOperationException("unimplemented mock method"); } @Override - public Cursor query(String callingPackage, Uri url, @Nullable String[] projection, - @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) { + public Cursor query(String callingPackage, @Nullable String featureId, Uri url, + @Nullable String[] projection, @Nullable Bundle queryArgs, + @Nullable ICancellationSignal cancellationSignal) { throw new UnsupportedOperationException("unimplemented mock method"); } @@ -99,14 +98,14 @@ public class MockIContentProvider implements IContentProvider { } @Override - public int update(String callingPackage, Uri url, ContentValues values, String selection, - String[] selectionArgs) throws RemoteException { + public int update(String callingPackage, @Nullable String featureId, Uri url, + ContentValues values, String selection, String[] selectionArgs) throws RemoteException { throw new UnsupportedOperationException("unimplemented mock method"); } @Override - public Bundle call(String callingPackage, String authority, String method, String request, - Bundle args) throws RemoteException { + public Bundle call(String callingPackage, @Nullable String featureId, String authority, + String method, String request, Bundle args) throws RemoteException { throw new UnsupportedOperationException("unimplemented mock method"); } @@ -121,8 +120,9 @@ public class MockIContentProvider implements IContentProvider { } @Override - public AssetFileDescriptor openTypedAssetFile(String callingPackage, Uri url, String mimeType, - Bundle opts, ICancellationSignal signal) throws RemoteException, FileNotFoundException { + public AssetFileDescriptor openTypedAssetFile(String callingPackage, + @Nullable String featureId, Uri url, String mimeType, Bundle opts, + ICancellationSignal signal) throws RemoteException, FileNotFoundException { throw new UnsupportedOperationException("unimplemented mock method"); } @@ -132,24 +132,27 @@ public class MockIContentProvider implements IContentProvider { } @Override - public Uri canonicalize(String callingPkg, Uri uri) throws RemoteException { + public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) + throws RemoteException { throw new UnsupportedOperationException("unimplemented mock method"); } @Override - public Uri uncanonicalize(String callingPkg, Uri uri) throws RemoteException { + public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri) + throws RemoteException { throw new UnsupportedOperationException("unimplemented mock method"); } @Override - public boolean refresh(String callingPkg, Uri url, Bundle args, + public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, Bundle args, ICancellationSignal cancellationSignal) throws RemoteException { throw new UnsupportedOperationException("unimplemented mock method"); } /** {@hide} */ @Override - public int checkUriPermission(String callingPkg, Uri uri, int uid, int modeFlags) { + public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri, int uid, + int modeFlags) { throw new UnsupportedOperationException("unimplemented mock method call"); } } diff --git a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java index 81937e6b7005..ead4a28beff4 100644 --- a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java +++ b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java @@ -94,6 +94,8 @@ public class BootImageProfileTest implements IDeviceTest { boolean sawServices = false; for (String line : res.split("\n")) { if (line.contains("framework.jar")) { + sawFramework = true; // Legacy + } else if (line.contains("framework-minus-apex.jar")) { sawFramework = true; } else if (line.contains("services.jar")) { sawServices = true; diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp index 085c53cf45ea..2bc129ae4840 100644 --- a/tests/RollbackTest/Android.bp +++ b/tests/RollbackTest/Android.bp @@ -25,6 +25,7 @@ java_test_host { name: "StagedRollbackTest", srcs: ["StagedRollbackTest/src/**/*.java"], libs: ["tradefed"], + static_libs: ["testng"], test_suites: ["general-tests"], test_config: "StagedRollbackTest.xml", } diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java index b51aad1d63f8..bfb49689f98d 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java @@ -229,13 +229,21 @@ public class StagedRollbackTest { RollbackManager rm = RollbackUtils.getRollbackManager(); assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), getNetworkStackPackageName())).isNotNull(); + + // Sleep for < health check deadline + Thread.sleep(TimeUnit.SECONDS.toMillis(5)); + // Verify rollback was not executed before health check deadline + assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(), + getNetworkStackPackageName())).isNull(); } @Test public void testNetworkFailedRollback_Phase3() throws Exception { - RollbackManager rm = RollbackUtils.getRollbackManager(); - assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(), - getNetworkStackPackageName())).isNull(); + // Sleep for > health check deadline + // The device is expected to reboot during sleeping. This device method will fail and + // the host will catch the assertion. If reboot doesn't happen, the host will fail the + // assertion. + Thread.sleep(TimeUnit.SECONDS.toMillis(120)); } @Test diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java index f7fe6c728f6a..c10169b2d3ee 100644 --- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java +++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java @@ -17,6 +17,7 @@ package com.android.tests.rollback.host; import static org.junit.Assert.assertTrue; +import static org.testng.Assert.assertThrows; import com.android.ddmlib.Log.LogLevel; import com.android.tradefed.log.LogUtil.CLog; @@ -124,7 +125,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { runPhase("testNetworkFailedRollback_Phase1"); // Reduce health check deadline getDevice().executeShellCommand("device_config put rollback " - + "watchdog_request_timeout_millis 300000"); + + "watchdog_request_timeout_millis 120000"); // Simulate re-installation of new NetworkStack with rollbacks enabled getDevice().executeShellCommand("pm install -r --staged --enable-rollback " + "/system/priv-app/NetworkStack/NetworkStack.apk"); @@ -136,22 +137,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { // Verify rollback was enabled runPhase("testNetworkFailedRollback_Phase2"); - - // Sleep for < health check deadline - Thread.sleep(5000); - // Verify rollback was not executed before health check deadline - runPhase("testNetworkFailedRollback_Phase3"); - try { - // This is expected to fail due to the device being rebooted out - // from underneath the test. If this fails for reasons other than - // the device reboot, those failures should result in failure of - // the testNetworkFailedRollback_Phase4 phase. - CLog.logAndDisplay(LogLevel.INFO, "Sleep and expect to fail while sleeping"); - // Sleep for > health check deadline - Thread.sleep(260000); - } catch (AssertionError e) { - // AssertionError is expected. - } + assertThrows(AssertionError.class, () -> runPhase("testNetworkFailedRollback_Phase3")); getDevice().waitForDeviceAvailable(); // Verify rollback was executed after health check deadline diff --git a/tests/net/Android.bp b/tests/net/Android.bp index b9b2238572ac..10f27e243c8c 100644 --- a/tests/net/Android.bp +++ b/tests/net/Android.bp @@ -37,7 +37,7 @@ java_defaults { "libvndksupport", "libziparchive", "libz", - "netd_aidl_interface-V2-cpp", + "netd_aidl_interface-cpp", ], } diff --git a/tests/testables/src/android/testing/TestableSettingsProvider.java b/tests/testables/src/android/testing/TestableSettingsProvider.java index b158476bd438..fd92c657cb2e 100644 --- a/tests/testables/src/android/testing/TestableSettingsProvider.java +++ b/tests/testables/src/android/testing/TestableSettingsProvider.java @@ -14,6 +14,8 @@ package android.testing; +import static org.junit.Assert.assertEquals; + import android.content.ContentProviderClient; import android.content.Context; import android.os.Bundle; @@ -25,8 +27,6 @@ import android.util.Log; import java.util.HashMap; -import static org.junit.Assert.*; - /** * Allows calls to android.provider.Settings to be tested easier. * @@ -71,7 +71,7 @@ public class TestableSettingsProvider extends MockContentProvider { public Bundle call(String method, String arg, Bundle extras) { // Methods are "GET_system", "GET_global", "PUT_secure", etc. - final int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, 0); + final int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, UserHandle.myUserId()); final String[] commands = method.split("_", 2); final String op = commands[0]; final String table = commands[1]; diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt index 431f378a8811..bd32f9c6d9cd 100644 --- a/tools/codegen/src/com/android/codegen/Generators.kt +++ b/tools/codegen/src/com/android/codegen/Generators.kt @@ -212,13 +212,15 @@ fun ClassPrinter.generateBuilder() { "Object" } + val maybeFinal = if_(classAst.isFinal, "final ") + +"/**" +" * A builder for {@link $ClassName}" if (FeatureFlag.BUILDER.hidden) +" * @hide" +" */" +"@SuppressWarnings(\"WeakerAccess\")" +GENERATED_MEMBER_HEADER - !"public static class $BuilderClass$genericArgs" + !"public static ${maybeFinal}class $BuilderClass$genericArgs" if (BuilderSupertype != "Object") { appendSameLine(" extends $BuilderSupertype") } @@ -359,7 +361,7 @@ private fun ClassPrinter.generateBuilderSetters(visibility: String) { private fun ClassPrinter.generateBuilderBuild() { +"/** Builds the instance. This builder should not be touched after calling this! */" - "public $ClassType build()" { + "public @$NonNull $ClassType build()" { +"checkNotUsed();" +"mBuilderFieldsSet |= ${bitAtExpr(fields.size)}; // Mark builder used" +"" diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index 3eb9e7bb68c6..270d34a01a64 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,7 +1,7 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "1.0.9" +const val CODEGEN_VERSION = "1.0.11" const val CANONICAL_BUILDER_CLASS = "Builder" const val BASE_BUILDER_CLASS = "BaseBuilder" diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 19be13268d27..023df70ef55a 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -34,6 +34,7 @@ import android.net.wifi.ITrafficStateCallback; import android.net.wifi.ITxPacketCountListener; import android.net.wifi.IOnWifiUsabilityStatsListener; import android.net.wifi.ScanResult; +import android.net.wifi.SoftApConfiguration; import android.net.wifi.WifiActivityEnergyInfo; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; @@ -142,7 +143,8 @@ interface IWifiManager boolean stopSoftAp(); - int startLocalOnlyHotspot(in ILocalOnlyHotspotCallback callback, String packageName); + int startLocalOnlyHotspot(in ILocalOnlyHotspotCallback callback, String packageName, + in SoftApConfiguration customConfig); void stopLocalOnlyHotspot(); diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.aidl b/wifi/java/android/net/wifi/SoftApConfiguration.aidl new file mode 100644 index 000000000000..1d06f458318f --- /dev/null +++ b/wifi/java/android/net/wifi/SoftApConfiguration.aidl @@ -0,0 +1,19 @@ +/* + * 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.wifi; + +parcelable SoftApConfiguration;
\ No newline at end of file diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java new file mode 100644 index 000000000000..4cc86539926c --- /dev/null +++ b/wifi/java/android/net/wifi/SoftApConfiguration.java @@ -0,0 +1,231 @@ +/* + * 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.wifi; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.net.MacAddress; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * WiFi configuration for a soft access point (a.k.a. Soft AP, SAP, Hotspot). + * + * This is input for the framework provided by a client app, i.e. it exposes knobs to instruct the + * framework how it should open a hotspot. It is not meant to describe the network as it will be + * seen by clients; this role is currently served by {@link WifiConfiguration} (see + * {@link WifiManager.LocalOnlyHotspotReservation#getWifiConfiguration()}). + * + * System apps can use this to configure a local-only hotspot using + * {@link WifiManager#startLocalOnlyHotspot(SoftApConfiguration, Executor, + * WifiManager.LocalOnlyHotspotCallback)}. + * + * Instances of this class are immutable; use {@link SoftApConfiguration.Builder} and its methods to + * create a new instance. + * + * @hide + */ +@SystemApi +public final class SoftApConfiguration implements Parcelable { + /** + * SSID for the AP, or null for a framework-determined SSID. + */ + private final @Nullable String mSsid; + /** + * BSSID for the AP, or null to use a framework-determined BSSID. + */ + private final @Nullable MacAddress mBssid; + /** + * Pre-shared key for WPA2-PSK encryption (non-null enables WPA2-PSK). + */ + private final @Nullable String mWpa2Passphrase; + + /** Private constructor for Builder and Parcelable implementation. */ + private SoftApConfiguration( + @Nullable String ssid, @Nullable MacAddress bssid, String wpa2Passphrase) { + mSsid = ssid; + mBssid = bssid; + mWpa2Passphrase = wpa2Passphrase; + } + + @Override + public boolean equals(Object otherObj) { + if (this == otherObj) { + return true; + } + if (!(otherObj instanceof SoftApConfiguration)) { + return false; + } + SoftApConfiguration other = (SoftApConfiguration) otherObj; + return Objects.equals(mSsid, other.mSsid) + && Objects.equals(mBssid, other.mBssid) + && Objects.equals(mWpa2Passphrase, other.mWpa2Passphrase); + } + + @Override + public int hashCode() { + return Objects.hash(mSsid, mBssid, mWpa2Passphrase); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mSsid); + dest.writeParcelable(mBssid, flags); + dest.writeString(mWpa2Passphrase); + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull + public static final Creator<SoftApConfiguration> CREATOR = new Creator<SoftApConfiguration>() { + @Override + public SoftApConfiguration createFromParcel(Parcel in) { + return new SoftApConfiguration( + in.readString(), + in.readParcelable(MacAddress.class.getClassLoader()), + in.readString()); + } + + @Override + public SoftApConfiguration[] newArray(int size) { + return new SoftApConfiguration[size]; + } + }; + + @Nullable + public String getSsid() { + return mSsid; + } + + @Nullable + public MacAddress getBssid() { + return mBssid; + } + + @Nullable + public String getWpa2Passphrase() { + return mWpa2Passphrase; + } + + /** + * Builds a {@link SoftApConfiguration}, which allows an app to configure various aspects of a + * Soft AP. + * + * All fields are optional. By default, SSID and BSSID are automatically chosen by the + * framework, and an open network is created. + */ + public static final class Builder { + private String mSsid; + private MacAddress mBssid; + private String mWpa2Passphrase; + + /** + * Constructs a Builder with default values (see {@link Builder}). + */ + public Builder() { + mSsid = null; + mBssid = null; + mWpa2Passphrase = null; + } + + /** + * Constructs a Builder initialized from an existing {@link SoftApConfiguration} instance. + */ + public Builder(@NonNull SoftApConfiguration other) { + Objects.requireNonNull(other); + + mSsid = other.mSsid; + mBssid = other.mBssid; + mWpa2Passphrase = other.mWpa2Passphrase; + } + + /** + * Builds the {@link SoftApConfiguration}. + * + * @return A new {@link SoftApConfiguration}, as configured by previous method calls. + */ + @NonNull + public SoftApConfiguration build() { + return new SoftApConfiguration(mSsid, mBssid, mWpa2Passphrase); + } + + /** + * Specifies an SSID for the AP. + * + * @param ssid SSID of valid Unicode characters, or null to have the SSID automatically + * chosen by the framework. + * @return Builder for chaining. + * @throws IllegalArgumentException when the SSID is empty or not valid Unicode. + */ + @NonNull + public Builder setSsid(@Nullable String ssid) { + if (ssid != null) { + Preconditions.checkStringNotEmpty(ssid); + Preconditions.checkArgument(StandardCharsets.UTF_8.newEncoder().canEncode(ssid)); + } + mSsid = ssid; + return this; + } + + /** + * Specifies a BSSID for the AP. + * + * @param bssid BSSID, or null to have the BSSID chosen by the framework. The caller is + * responsible for avoiding collisions. + * @return Builder for chaining. + * @throws IllegalArgumentException when the given BSSID is the all-zero or broadcast MAC + * address. + */ + @NonNull + public Builder setBssid(@Nullable MacAddress bssid) { + if (bssid != null) { + Preconditions.checkArgument(!bssid.equals(MacAddress.ALL_ZEROS_ADDRESS)); + Preconditions.checkArgument(!bssid.equals(MacAddress.BROADCAST_ADDRESS)); + } + mBssid = bssid; + return this; + } + + /** + * Specifies that this AP should use WPA2-PSK with the given passphrase. When set to null + * and no other encryption method is configured, an open network is created. + * + * @param passphrase The passphrase to use, or null to unset a previously-set WPA2-PSK + * configuration. + * @return Builder for chaining. + * @throws IllegalArgumentException when the passphrase is the empty string + */ + @NonNull + public Builder setWpa2Passphrase(@Nullable String passphrase) { + if (passphrase != null) { + Preconditions.checkStringNotEmpty(passphrase); + } + mWpa2Passphrase = passphrase; + return this; + } + } +} diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 3bedddc8ec7d..30c24d37a494 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -34,6 +34,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; import android.os.UserHandle; +import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.BackupUtils; import android.util.Log; @@ -730,6 +731,14 @@ public class WifiConfiguration implements Parcelable { public String lastUpdateName; /** + * The carrier ID identifies the operator who provides this network configuration. + * see {@link TelephonyManager#getSimCarrierId()} + * @hide + */ + @SystemApi + public int carrierId = TelephonyManager.UNKNOWN_CARRIER_ID; + + /** * @hide * Status of user approval for connection */ @@ -1046,10 +1055,10 @@ public class WifiConfiguration implements Parcelable { /** * @hide - * The wall clock time of when |mRandomizedMacAddress| last changed. - * Used to determine when we should re-randomize in aggressive mode. + * The wall clock time of when |mRandomizedMacAddress| should be re-randomized in aggressive + * randomization mode. */ - public long randomizedMacLastModifiedTimeMs = 0; + public long randomizedMacExpirationTimeMs = 0; /** * @hide @@ -1850,6 +1859,7 @@ public class WifiConfiguration implements Parcelable { .append(" PRIO: ").append(this.priority) .append(" HIDDEN: ").append(this.hiddenSSID) .append(" PMF: ").append(this.requirePMF) + .append("CarrierId: ").append(this.carrierId) .append('\n'); @@ -1910,8 +1920,9 @@ public class WifiConfiguration implements Parcelable { } sbuf.append(" macRandomizationSetting: ").append(macRandomizationSetting).append("\n"); sbuf.append(" mRandomizedMacAddress: ").append(mRandomizedMacAddress).append("\n"); - sbuf.append(" randomizedMacLastModifiedTimeMs: ").append(randomizedMacLastModifiedTimeMs) - .append("\n"); + sbuf.append(" randomizedMacExpirationTimeMs: ") + .append(randomizedMacExpirationTimeMs == 0 ? "<none>" + : TimeUtils.logTimeOfDay(randomizedMacExpirationTimeMs)).append("\n"); sbuf.append(" KeyMgmt:"); for (int k = 0; k < this.allowedKeyManagement.size(); k++) { if (this.allowedKeyManagement.get(k)) { @@ -2439,9 +2450,10 @@ public class WifiConfiguration implements Parcelable { recentFailure.setAssociationStatus(source.recentFailure.getAssociationStatus()); mRandomizedMacAddress = source.mRandomizedMacAddress; macRandomizationSetting = source.macRandomizationSetting; - randomizedMacLastModifiedTimeMs = source.randomizedMacLastModifiedTimeMs; + randomizedMacExpirationTimeMs = source.randomizedMacExpirationTimeMs; requirePMF = source.requirePMF; updateIdentifier = source.updateIdentifier; + carrierId = source.carrierId; } } @@ -2515,7 +2527,8 @@ public class WifiConfiguration implements Parcelable { dest.writeParcelable(mRandomizedMacAddress, flags); dest.writeInt(macRandomizationSetting); dest.writeInt(osu ? 1 : 0); - dest.writeLong(randomizedMacLastModifiedTimeMs); + dest.writeLong(randomizedMacExpirationTimeMs); + dest.writeInt(carrierId); } /** Implement the Parcelable interface {@hide} */ @@ -2591,7 +2604,8 @@ public class WifiConfiguration implements Parcelable { config.mRandomizedMacAddress = in.readParcelable(null); config.macRandomizationSetting = in.readInt(); config.osu = in.readInt() != 0; - config.randomizedMacLastModifiedTimeMs = in.readLong(); + config.randomizedMacExpirationTimeMs = in.readLong(); + config.carrierId = in.readInt(); return config; } diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index f8c20111d79b..7b99a2b5502f 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -1263,4 +1263,23 @@ public class WifiEnterpriseConfig implements Parcelable { public @Ocsp int getOcsp() { return mOcsp; } + + /** + * If the current authentication method needs SIM card. + * @return true if the credential information require SIM card for current authentication + * method, otherwise it returns false. + * @hide + */ + public boolean requireSimCredential() { + if (mEapMethod == Eap.SIM || mEapMethod == Eap.AKA || mEapMethod == Eap.AKA_PRIME) { + return true; + } + if (mEapMethod == Eap.PEAP) { + if (mPhase2Method == Phase2.SIM || mPhase2Method == Phase2.AKA + || mPhase2Method == Phase2.AKA_PRIME) { + return true; + } + } + return false; + } } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 3b7690b1e8fa..380ebf104a14 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -71,6 +71,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; @@ -2154,69 +2155,69 @@ public class WifiManager { } /** @hide */ - public static final int WIFI_FEATURE_INFRA = 0x0001; // Basic infrastructure mode + public static final long WIFI_FEATURE_INFRA = 0x0001L; // Basic infrastructure mode /** @hide */ - public static final int WIFI_FEATURE_INFRA_5G = 0x0002; // Support for 5 GHz Band + public static final long WIFI_FEATURE_INFRA_5G = 0x0002L; // Support for 5 GHz Band /** @hide */ - public static final int WIFI_FEATURE_PASSPOINT = 0x0004; // Support for GAS/ANQP + public static final long WIFI_FEATURE_PASSPOINT = 0x0004L; // Support for GAS/ANQP /** @hide */ - public static final int WIFI_FEATURE_P2P = 0x0008; // Wifi-Direct + public static final long WIFI_FEATURE_P2P = 0x0008L; // Wifi-Direct /** @hide */ - public static final int WIFI_FEATURE_MOBILE_HOTSPOT = 0x0010; // Soft AP + public static final long WIFI_FEATURE_MOBILE_HOTSPOT = 0x0010L; // Soft AP /** @hide */ - public static final int WIFI_FEATURE_SCANNER = 0x0020; // WifiScanner APIs + public static final long WIFI_FEATURE_SCANNER = 0x0020L; // WifiScanner APIs /** @hide */ - public static final int WIFI_FEATURE_AWARE = 0x0040; // Wi-Fi AWare networking + public static final long WIFI_FEATURE_AWARE = 0x0040L; // Wi-Fi AWare networking /** @hide */ - public static final int WIFI_FEATURE_D2D_RTT = 0x0080; // Device-to-device RTT + public static final long WIFI_FEATURE_D2D_RTT = 0x0080L; // Device-to-device RTT /** @hide */ - public static final int WIFI_FEATURE_D2AP_RTT = 0x0100; // Device-to-AP RTT + public static final long WIFI_FEATURE_D2AP_RTT = 0x0100L; // Device-to-AP RTT /** @hide */ - public static final int WIFI_FEATURE_BATCH_SCAN = 0x0200; // Batched Scan (deprecated) + public static final long WIFI_FEATURE_BATCH_SCAN = 0x0200L; // Batched Scan (deprecated) /** @hide */ - public static final int WIFI_FEATURE_PNO = 0x0400; // Preferred network offload + public static final long WIFI_FEATURE_PNO = 0x0400L; // Preferred network offload /** @hide */ - public static final int WIFI_FEATURE_ADDITIONAL_STA = 0x0800; // Support for two STAs + public static final long WIFI_FEATURE_ADDITIONAL_STA = 0x0800L; // Support for two STAs /** @hide */ - public static final int WIFI_FEATURE_TDLS = 0x1000; // Tunnel directed link setup + public static final long WIFI_FEATURE_TDLS = 0x1000L; // Tunnel directed link setup /** @hide */ - public static final int WIFI_FEATURE_TDLS_OFFCHANNEL = 0x2000; // Support for TDLS off channel + public static final long WIFI_FEATURE_TDLS_OFFCHANNEL = 0x2000L; // TDLS off channel /** @hide */ - public static final int WIFI_FEATURE_EPR = 0x4000; // Enhanced power reporting + public static final long WIFI_FEATURE_EPR = 0x4000L; // Enhanced power reporting /** @hide */ - public static final int WIFI_FEATURE_AP_STA = 0x8000; // AP STA Concurrency + public static final long WIFI_FEATURE_AP_STA = 0x8000L; // AP STA Concurrency /** @hide */ - public static final int WIFI_FEATURE_LINK_LAYER_STATS = 0x10000; // Link layer stats collection + public static final long WIFI_FEATURE_LINK_LAYER_STATS = 0x10000L; // Link layer stats /** @hide */ - public static final int WIFI_FEATURE_LOGGER = 0x20000; // WiFi Logger + public static final long WIFI_FEATURE_LOGGER = 0x20000L; // WiFi Logger /** @hide */ - public static final int WIFI_FEATURE_HAL_EPNO = 0x40000; // Enhanced PNO + public static final long WIFI_FEATURE_HAL_EPNO = 0x40000L; // Enhanced PNO /** @hide */ - public static final int WIFI_FEATURE_RSSI_MONITOR = 0x80000; // RSSI Monitor + public static final long WIFI_FEATURE_RSSI_MONITOR = 0x80000L; // RSSI Monitor /** @hide */ - public static final int WIFI_FEATURE_MKEEP_ALIVE = 0x100000; // mkeep_alive + public static final long WIFI_FEATURE_MKEEP_ALIVE = 0x100000L; // mkeep_alive /** @hide */ - public static final int WIFI_FEATURE_CONFIG_NDO = 0x200000; // ND offload + public static final long WIFI_FEATURE_CONFIG_NDO = 0x200000L; // ND offload /** @hide */ - public static final int WIFI_FEATURE_TRANSMIT_POWER = 0x400000; // Capture transmit power + public static final long WIFI_FEATURE_TRANSMIT_POWER = 0x400000L; // Capture transmit power /** @hide */ - public static final int WIFI_FEATURE_CONTROL_ROAMING = 0x800000; // Control firmware roaming + public static final long WIFI_FEATURE_CONTROL_ROAMING = 0x800000L; // Control firmware roaming /** @hide */ - public static final int WIFI_FEATURE_IE_WHITELIST = 0x1000000; // Probe IE white listing + public static final long WIFI_FEATURE_IE_WHITELIST = 0x1000000L; // Probe IE white listing /** @hide */ - public static final int WIFI_FEATURE_SCAN_RAND = 0x2000000; // Random MAC & Probe seq + public static final long WIFI_FEATURE_SCAN_RAND = 0x2000000L; // Random MAC & Probe seq /** @hide */ - public static final int WIFI_FEATURE_TX_POWER_LIMIT = 0x4000000; // Set Tx power limit + public static final long WIFI_FEATURE_TX_POWER_LIMIT = 0x4000000L; // Set Tx power limit /** @hide */ - public static final int WIFI_FEATURE_WPA3_SAE = 0x8000000; // WPA3-Personal SAE + public static final long WIFI_FEATURE_WPA3_SAE = 0x8000000L; // WPA3-Personal SAE /** @hide */ - public static final int WIFI_FEATURE_WPA3_SUITE_B = 0x10000000; // WPA3-Enterprise Suite-B + public static final long WIFI_FEATURE_WPA3_SUITE_B = 0x10000000L; // WPA3-Enterprise Suite-B /** @hide */ - public static final int WIFI_FEATURE_OWE = 0x20000000; // Enhanced Open + public static final long WIFI_FEATURE_OWE = 0x20000000L; // Enhanced Open /** @hide */ - public static final int WIFI_FEATURE_LOW_LATENCY = 0x40000000; // Low Latency modes + public static final long WIFI_FEATURE_LOW_LATENCY = 0x40000000L; // Low Latency modes /** @hide */ - public static final int WIFI_FEATURE_DPP = 0x80000000; // DPP (Easy-Connect) + public static final long WIFI_FEATURE_DPP = 0x80000000L; // DPP (Easy-Connect) /** @hide */ public static final long WIFI_FEATURE_P2P_RAND_MAC = 0x100000000L; // Random P2P MAC @@ -2747,13 +2748,6 @@ public class WifiManager { } } - private Executor executorForHandler(@Nullable Handler handler) { - if (handler == null) { - return mContext.getMainExecutor(); - } - return new HandlerExecutor(handler); - } - /** * Request a local only hotspot that an application can use to communicate between co-located * devices connected to the created WiFi hotspot. The network created by this method will not @@ -2809,9 +2803,59 @@ public class WifiManager { * @param handler Handler to be used for callbacks. If the caller passes a null Handler, the * main thread will be used. */ + @RequiresPermission(allOf = { + android.Manifest.permission.CHANGE_WIFI_STATE, + android.Manifest.permission.ACCESS_FINE_LOCATION}) public void startLocalOnlyHotspot(LocalOnlyHotspotCallback callback, @Nullable Handler handler) { - Executor executor = executorForHandler(handler); + Executor executor = handler == null ? null : new HandlerExecutor(handler); + startLocalOnlyHotspotInternal(null, executor, callback); + } + + /** + * Starts a local-only hotspot with a specific configuration applied. See + * {@link #startLocalOnlyHotspot(LocalOnlyHotspotCallback, Handler)}. + * + * Applications need either {@link android.Manifest.permission#NETWORK_SETUP_WIZARD} or + * {@link android.Manifest.permission#NETWORK_SETTINGS} to call this method. + * + * Since custom configuration settings may be incompatible with each other, the hotspot started + * through this method cannot coexist with another hotspot created through + * startLocalOnlyHotspot. If this is attempted, the first hotspot request wins and others + * receive {@link LocalOnlyHotspotCallback#ERROR_GENERIC} through + * {@link LocalOnlyHotspotCallback#onFailed}. + * + * @param config Custom configuration for the hotspot. See {@link SoftApConfiguration}. + * @param executor Executor to run callback methods on, or null to use the main thread. + * @param callback Callback object for updates about hotspot status, or null for no updates. + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) + public void startLocalOnlyHotspot(@NonNull SoftApConfiguration config, + @Nullable Executor executor, + @Nullable LocalOnlyHotspotCallback callback) { + Objects.requireNonNull(config); + startLocalOnlyHotspotInternal(config, executor, callback); + } + + /** + * Common implementation of both configurable and non-configurable LOHS. + * + * @param config App-specified configuration, or null. When present, additional privileges are + * required, and the hotspot cannot be shared with other clients. + * @param executor Executor to run callback methods on, or null to use the main thread. + * @param callback Callback object for updates about hotspot status, or null for no updates. + */ + private void startLocalOnlyHotspotInternal( + @Nullable SoftApConfiguration config, + @Nullable Executor executor, + @Nullable LocalOnlyHotspotCallback callback) { + if (executor == null) { + executor = mContext.getMainExecutor(); + } synchronized (mLock) { LocalOnlyHotspotCallbackProxy proxy = new LocalOnlyHotspotCallbackProxy(this, executor, callback); @@ -2821,7 +2865,7 @@ public class WifiManager { throw new RemoteException("Wifi service is not running"); } String packageName = mContext.getOpPackageName(); - int returnCode = iWifiManager.startLocalOnlyHotspot(proxy, packageName); + int returnCode = iWifiManager.startLocalOnlyHotspot(proxy, packageName, config); if (returnCode != LocalOnlyHotspotCallback.REQUEST_REGISTERED) { // Send message to the proxy to make sure we call back on the correct thread proxy.onHotspotFailed(returnCode); @@ -2902,7 +2946,8 @@ public class WifiManager { */ public void watchLocalOnlyHotspot(LocalOnlyHotspotObserver observer, @Nullable Handler handler) { - Executor executor = executorForHandler(handler); + Executor executor = handler == null ? mContext.getMainExecutor() + : new HandlerExecutor(handler); synchronized (mLock) { mLOHSObserverProxy = new LocalOnlyHotspotObserverProxy(this, executor, observer); @@ -3484,10 +3529,12 @@ public class WifiManager { * * @param manager WifiManager * @param executor Executor for delivering callbacks. - * @param callback LocalOnlyHotspotCallback to notify the calling application. + * @param callback LocalOnlyHotspotCallback to notify the calling application, or null. */ - LocalOnlyHotspotCallbackProxy(WifiManager manager, Executor executor, - LocalOnlyHotspotCallback callback) { + LocalOnlyHotspotCallbackProxy( + @NonNull WifiManager manager, + @NonNull Executor executor, + @Nullable LocalOnlyHotspotCallback callback) { mWifiManager = new WeakReference<>(manager); mExecutor = executor; mCallback = callback; @@ -3505,6 +3552,7 @@ public class WifiManager { } final LocalOnlyHotspotReservation reservation = manager.new LocalOnlyHotspotReservation(config); + if (mCallback == null) return; mExecutor.execute(() -> mCallback.onStarted(reservation)); } @@ -3514,6 +3562,7 @@ public class WifiManager { if (manager == null) return; Log.w(TAG, "LocalOnlyHotspotCallbackProxy: hotspot stopped"); + if (mCallback == null) return; mExecutor.execute(() -> mCallback.onStopped()); } @@ -3524,6 +3573,7 @@ public class WifiManager { Log.w(TAG, "LocalOnlyHotspotCallbackProxy: failed to start. reason: " + reason); + if (mCallback == null) return; mExecutor.execute(() -> mCallback.onFailed(reason)); } } diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java index 9b529cee58b3..246e96f4ce3f 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java +++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java @@ -21,12 +21,15 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.app.ActivityThread; import android.net.MacAddress; import android.net.wifi.hotspot2.PasspointConfiguration; import android.os.Parcel; import android.os.Parcelable; import android.os.Process; +import android.telephony.TelephonyManager; import android.text.TextUtils; import java.nio.charset.CharsetEncoder; @@ -107,6 +110,12 @@ public final class WifiNetworkSuggestion implements Parcelable { */ private int mPriority; + /** + * The carrier ID identifies the operator who provides this network configuration. + * see {@link TelephonyManager#getSimCarrierId()} + */ + private int mCarrierId; + public Builder() { mSsid = null; mBssid = null; @@ -121,6 +130,7 @@ public final class WifiNetworkSuggestion implements Parcelable { mIsUserInteractionRequired = false; mIsMetered = false; mPriority = UNASSIGNED_PRIORITY; + mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID; } /** @@ -258,6 +268,23 @@ public final class WifiNetworkSuggestion implements Parcelable { } /** + * Set the carrier ID of the network operator. The carrier ID associates a Suggested + * network with a specific carrier (and therefore SIM). The carrier ID must be provided + * for any network which uses the SIM-based authentication: e.g. EAP-SIM, EAP-AKA, + * EAP-AKA', and EAP-PEAP with SIM-based phase 2 authentication. + * @param carrierId see {@link TelephonyManager#getSimCarrierId()}. + * @return Instance of {@link Builder} to enable chaining of the builder method. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) + public @NonNull Builder setCarrierId(int carrierId) { + mCarrierId = carrierId; + return this; + } + + /** * Specifies whether this represents a hidden network. * <p> * <li>If not set, defaults to false (i.e not a hidden network).</li> @@ -380,6 +407,7 @@ public final class WifiNetworkSuggestion implements Parcelable { wifiConfiguration.meteredOverride = mIsMetered ? WifiConfiguration.METERED_OVERRIDE_METERED : WifiConfiguration.METERED_OVERRIDE_NONE; + wifiConfiguration.carrierId = mCarrierId; return wifiConfiguration; } @@ -405,6 +433,7 @@ public final class WifiNetworkSuggestion implements Parcelable { wifiConfiguration.meteredOverride = mIsMetered ? WifiConfiguration.METERED_OVERRIDE_METERED : WifiConfiguration.METERED_OVERRIDE_NONE; + mPasspointConfiguration.setCarrierId(mCarrierId); return wifiConfiguration; } diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index 21189a411d94..67993e1b72db 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -19,7 +19,6 @@ package android.net.wifi; import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; @@ -134,14 +133,14 @@ public class WifiScanner { * @hide */ @SystemApi - @Nullable + @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public List<Integer> getAvailableChannels(@WifiBand int band) { try { Bundle bundle = mService.getAvailableChannels(band, mContext.getOpPackageName()); return bundle.getIntegerArrayList(GET_AVAILABLE_CHANNELS_EXTRA); } catch (RemoteException e) { - return null; + throw e.rethrowFromSystemServer(); } } @@ -344,7 +343,7 @@ public class WifiScanner { } /** Implement the Parcelable interface {@hide} */ - public static final @android.annotation.NonNull Creator<ScanSettings> CREATOR = + public static final @NonNull Creator<ScanSettings> CREATOR = new Creator<ScanSettings>() { public ScanSettings createFromParcel(Parcel in) { ScanSettings settings = new ScanSettings(); @@ -492,7 +491,7 @@ public class WifiScanner { } /** Implement the Parcelable interface {@hide} */ - public static final @android.annotation.NonNull Creator<ScanData> CREATOR = + public static final @NonNull Creator<ScanData> CREATOR = new Creator<ScanData>() { public ScanData createFromParcel(Parcel in) { int id = in.readInt(); @@ -541,7 +540,7 @@ public class WifiScanner { } /** Implement the Parcelable interface {@hide} */ - public static final @android.annotation.NonNull Creator<ParcelableScanData> CREATOR = + public static final @NonNull Creator<ParcelableScanData> CREATOR = new Creator<ParcelableScanData>() { public ParcelableScanData createFromParcel(Parcel in) { int n = in.readInt(); @@ -589,7 +588,7 @@ public class WifiScanner { } /** Implement the Parcelable interface {@hide} */ - public static final @android.annotation.NonNull Creator<ParcelableScanResults> CREATOR = + public static final @NonNull Creator<ParcelableScanResults> CREATOR = new Creator<ParcelableScanResults>() { public ParcelableScanResults createFromParcel(Parcel in) { int n = in.readInt(); @@ -720,7 +719,7 @@ public class WifiScanner { } /** Implement the Parcelable interface {@hide} */ - public static final @android.annotation.NonNull Creator<PnoSettings> CREATOR = + public static final @NonNull Creator<PnoSettings> CREATOR = new Creator<PnoSettings>() { public PnoSettings createFromParcel(Parcel in) { PnoSettings settings = new PnoSettings(); @@ -1068,7 +1067,7 @@ public class WifiScanner { } /** Implement the Parcelable interface {@hide} */ - public static final @android.annotation.NonNull Creator<WifiChangeSettings> CREATOR = + public static final @NonNull Creator<WifiChangeSettings> CREATOR = new Creator<WifiChangeSettings>() { public WifiChangeSettings createFromParcel(Parcel in) { return new WifiChangeSettings(); @@ -1179,7 +1178,7 @@ public class WifiScanner { } /** Implement the Parcelable interface {@hide} */ - public static final @android.annotation.NonNull Creator<HotlistSettings> CREATOR = + public static final @NonNull Creator<HotlistSettings> CREATOR = new Creator<HotlistSettings>() { public HotlistSettings createFromParcel(Parcel in) { HotlistSettings settings = new HotlistSettings(); @@ -1412,7 +1411,7 @@ public class WifiScanner { } /** Implement the Parcelable interface {@hide} */ - public static final @android.annotation.NonNull Creator<OperationResult> CREATOR = + public static final @NonNull Creator<OperationResult> CREATOR = new Creator<OperationResult>() { public OperationResult createFromParcel(Parcel in) { int reason = in.readInt(); diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java index e9aa076798e1..5befb54ce50a 100644 --- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java +++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java @@ -24,6 +24,7 @@ import android.net.wifi.hotspot2.pps.UpdateParameter; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -398,6 +399,30 @@ public final class PasspointConfiguration implements Parcelable { } /** + * The carrier ID identifies the operator who provides this network configuration. + * see {@link TelephonyManager#getSimCarrierId()} + */ + private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID; + + /** + * Set the carrier ID associated with current configuration. + * @param carrierId {@code mCarrierId} + * @hide + */ + public void setCarrierId(int carrierId) { + this.mCarrierId = carrierId; + } + + /** + * Get the carrier ID associated with current configuration. + * @return {@code mCarrierId} + * @hide + */ + public int getCarrierId() { + return mCarrierId; + } + + /** * Constructor for creating PasspointConfiguration with default values. */ public PasspointConfiguration() {} @@ -438,6 +463,7 @@ public final class PasspointConfiguration implements Parcelable { mUsageLimitUsageTimePeriodInMinutes = source.mUsageLimitUsageTimePeriodInMinutes; mServiceFriendlyNames = source.mServiceFriendlyNames; mAaaServerTrustedNames = source.mAaaServerTrustedNames; + mCarrierId = source.mCarrierId; } @Override @@ -466,6 +492,7 @@ public final class PasspointConfiguration implements Parcelable { bundle.putSerializable("serviceFriendlyNames", (HashMap<String, String>) mServiceFriendlyNames); dest.writeBundle(bundle); + dest.writeInt(mCarrierId); } @Override @@ -495,6 +522,7 @@ public final class PasspointConfiguration implements Parcelable { && mUsageLimitStartTimeInMillis == that.mUsageLimitStartTimeInMillis && mUsageLimitDataLimit == that.mUsageLimitDataLimit && mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes + && mCarrierId == that.mCarrierId && (mServiceFriendlyNames == null ? that.mServiceFriendlyNames == null : mServiceFriendlyNames.equals(that.mServiceFriendlyNames)); } @@ -505,7 +533,7 @@ public final class PasspointConfiguration implements Parcelable { mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMillis, mSubscriptionExpirationTimeInMillis, mUsageLimitUsageTimePeriodInMinutes, mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes, - mServiceFriendlyNames); + mServiceFriendlyNames, mCarrierId); } @Override @@ -558,6 +586,7 @@ public final class PasspointConfiguration implements Parcelable { if (mServiceFriendlyNames != null) { builder.append("ServiceFriendlyNames: ").append(mServiceFriendlyNames); } + builder.append("CarrierId:" + mCarrierId); return builder.toString(); } @@ -662,6 +691,7 @@ public final class PasspointConfiguration implements Parcelable { Map<String, String> friendlyNamesMap = (HashMap) bundle.getSerializable( "serviceFriendlyNames"); config.setServiceFriendlyNames(friendlyNamesMap); + config.mCarrierId = in.readInt(); return config; } diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java index 5e6c107dc187..4b7d205e7922 100644 --- a/wifi/java/com/android/server/wifi/BaseWifiService.java +++ b/wifi/java/com/android/server/wifi/BaseWifiService.java @@ -32,6 +32,7 @@ import android.net.wifi.ITrafficStateCallback; import android.net.wifi.ITxPacketCountListener; import android.net.wifi.IWifiManager; import android.net.wifi.ScanResult; +import android.net.wifi.SoftApConfiguration; import android.net.wifi.WifiActivityEnergyInfo; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; @@ -293,8 +294,15 @@ public class BaseWifiService extends IWifiManager.Stub { throw new UnsupportedOperationException(); } - @Override + /** @deprecated replaced by newer signature */ + @Deprecated public int startLocalOnlyHotspot(ILocalOnlyHotspotCallback callback, String packageName) { + return startLocalOnlyHotspot(callback, packageName, null); + } + + @Override + public int startLocalOnlyHotspot(ILocalOnlyHotspotCallback callback, String packageName, + SoftApConfiguration customConfig) { throw new UnsupportedOperationException(); } diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java new file mode 100644 index 000000000000..949b4790d998 --- /dev/null +++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java @@ -0,0 +1,78 @@ +/* + * 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.wifi; + +import static com.google.common.truth.Truth.assertThat; + +import android.net.MacAddress; +import android.os.Parcel; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +@SmallTest +public class SoftApConfigurationTest { + private SoftApConfiguration parcelUnparcel(SoftApConfiguration configIn) { + Parcel parcel = Parcel.obtain(); + parcel.writeParcelable(configIn, 0); + parcel.setDataPosition(0); + SoftApConfiguration configOut = + parcel.readParcelable(SoftApConfiguration.class.getClassLoader()); + parcel.recycle(); + return configOut; + } + + @Test + public void testBasicSettings() { + SoftApConfiguration original = new SoftApConfiguration.Builder() + .setSsid("ssid") + .setBssid(MacAddress.fromString("11:22:33:44:55:66")) + .build(); + assertThat(original.getSsid()).isEqualTo("ssid"); + assertThat(original.getBssid()).isEqualTo(MacAddress.fromString("11:22:33:44:55:66")); + assertThat(original.getWpa2Passphrase()).isNull(); + + SoftApConfiguration unparceled = parcelUnparcel(original); + assertThat(unparceled).isNotSameAs(original); + assertThat(unparceled).isEqualTo(original); + assertThat(unparceled.hashCode()).isEqualTo(original.hashCode()); + + SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build(); + assertThat(copy).isNotSameAs(original); + assertThat(copy).isEqualTo(original); + assertThat(copy.hashCode()).isEqualTo(original.hashCode()); + } + + @Test + public void testWpa2() { + SoftApConfiguration original = new SoftApConfiguration.Builder() + .setWpa2Passphrase("secretsecret") + .build(); + assertThat(original.getWpa2Passphrase()).isEqualTo("secretsecret"); + + SoftApConfiguration unparceled = parcelUnparcel(original); + assertThat(unparceled).isNotSameAs(original); + assertThat(unparceled).isEqualTo(original); + assertThat(unparceled.hashCode()).isEqualTo(original.hashCode()); + + SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build(); + assertThat(copy).isNotSameAs(original); + assertThat(copy).isEqualTo(original); + assertThat(copy.hashCode()).isEqualTo(original.hashCode()); + } +} diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index 8d0579bde92c..d2516a33fead 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -173,7 +173,7 @@ public class WifiManagerTest { public void testCreationAndCloseOfLocalOnlyHotspotReservation() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), - anyString())).thenReturn(REQUEST_REGISTERED); + anyString(), eq(null))).thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, mHandler); callback.onStarted(mWifiManager.new LocalOnlyHotspotReservation(mApConfig)); @@ -191,7 +191,7 @@ public class WifiManagerTest { throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), - anyString())).thenReturn(REQUEST_REGISTERED); + anyString(), eq(null))).thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, mHandler); callback.onStarted(mWifiManager.new LocalOnlyHotspotReservation(mApConfig)); @@ -351,7 +351,7 @@ public class WifiManagerTest { mWifiManager.startLocalOnlyHotspot(callback, mHandler); verify(mWifiService) - .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString()); + .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), eq(null)); } /** @@ -362,7 +362,7 @@ public class WifiManagerTest { public void testStartLocalOnlyHotspotThrowsSecurityException() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); doThrow(new SecurityException()).when(mWifiService) - .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString()); + .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), eq(null)); mWifiManager.startLocalOnlyHotspot(callback, mHandler); } @@ -374,7 +374,7 @@ public class WifiManagerTest { public void testStartLocalOnlyHotspotThrowsIllegalStateException() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); doThrow(new IllegalStateException()).when(mWifiService) - .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString()); + .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), eq(null)); mWifiManager.startLocalOnlyHotspot(callback, mHandler); } @@ -385,7 +385,7 @@ public class WifiManagerTest { public void testCorrectLooperIsUsedForHandler() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), - anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE); + anyString(), eq(null))).thenReturn(ERROR_INCOMPATIBLE_MODE); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mLooper.dispatchAll(); assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason); @@ -404,7 +404,7 @@ public class WifiManagerTest { when(mContext.getMainExecutor()).thenReturn(altLooper.getNewExecutor()); TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), - anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE); + anyString(), eq(null))).thenReturn(ERROR_INCOMPATIBLE_MODE); mWifiManager.startLocalOnlyHotspot(callback, null); altLooper.dispatchAll(); assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason); @@ -423,7 +423,7 @@ public class WifiManagerTest { Handler callbackHandler = new Handler(callbackLooper.getLooper()); ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback = ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class); - when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString())) + when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), eq(null))) .thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, callbackHandler); callbackLooper.dispatchAll(); @@ -449,7 +449,7 @@ public class WifiManagerTest { Handler callbackHandler = new Handler(callbackLooper.getLooper()); ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback = ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class); - when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString())) + when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), eq(null))) .thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, callbackHandler); callbackLooper.dispatchAll(); @@ -474,7 +474,7 @@ public class WifiManagerTest { Handler callbackHandler = new Handler(callbackLooper.getLooper()); ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback = ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class); - when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString())) + when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), eq(null))) .thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, callbackHandler); callbackLooper.dispatchAll(); @@ -497,7 +497,7 @@ public class WifiManagerTest { Handler callbackHandler = new Handler(callbackLooper.getLooper()); ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback = ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class); - when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString())) + when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), eq(null))) .thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, callbackHandler); callbackLooper.dispatchAll(); @@ -517,7 +517,7 @@ public class WifiManagerTest { public void testLocalOnlyHotspotCallbackFullOnIncompatibleMode() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), - anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE); + anyString(), eq(null))).thenReturn(ERROR_INCOMPATIBLE_MODE); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mLooper.dispatchAll(); assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason); @@ -533,7 +533,7 @@ public class WifiManagerTest { public void testLocalOnlyHotspotCallbackFullOnTetheringDisallowed() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), - anyString())).thenReturn(ERROR_TETHERING_DISALLOWED); + anyString(), eq(null))).thenReturn(ERROR_TETHERING_DISALLOWED); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mLooper.dispatchAll(); assertEquals(ERROR_TETHERING_DISALLOWED, callback.mFailureReason); @@ -550,7 +550,7 @@ public class WifiManagerTest { public void testLocalOnlyHotspotCallbackFullOnSecurityException() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); doThrow(new SecurityException()).when(mWifiService) - .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString()); + .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), eq(null)); try { mWifiManager.startLocalOnlyHotspot(callback, mHandler); } catch (SecurityException e) { @@ -571,7 +571,7 @@ public class WifiManagerTest { public void testLocalOnlyHotspotCallbackFullOnNoChannelError() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), - anyString())).thenReturn(REQUEST_REGISTERED); + anyString(), eq(null))).thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mLooper.dispatchAll(); //assertEquals(ERROR_NO_CHANNEL, callback.mFailureReason); @@ -587,7 +587,7 @@ public class WifiManagerTest { public void testCancelLocalOnlyHotspotRequestCallsStopOnWifiService() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), - anyString())).thenReturn(REQUEST_REGISTERED); + anyString(), eq(null))).thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mWifiManager.cancelLocalOnlyHotspotRequest(); verify(mWifiService).stopLocalOnlyHotspot(); @@ -609,7 +609,7 @@ public class WifiManagerTest { public void testCallbackAfterLocalOnlyHotspotWasCancelled() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), - anyString())).thenReturn(REQUEST_REGISTERED); + anyString(), eq(null))).thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mWifiManager.cancelLocalOnlyHotspotRequest(); verify(mWifiService).stopLocalOnlyHotspot(); @@ -628,7 +628,7 @@ public class WifiManagerTest { public void testCancelAfterLocalOnlyHotspotCallbackTriggered() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), - anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE); + anyString(), eq(null))).thenReturn(ERROR_INCOMPATIBLE_MODE); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mLooper.dispatchAll(); assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason); @@ -639,6 +639,17 @@ public class WifiManagerTest { verify(mWifiService, never()).stopLocalOnlyHotspot(); } + @Test + public void testStartLocalOnlyHotspotForwardsCustomConfig() throws Exception { + SoftApConfiguration customConfig = new SoftApConfiguration.Builder() + .setSsid("customSsid") + .build(); + TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); + mWifiManager.startLocalOnlyHotspot(customConfig, mExecutor, callback); + verify(mWifiService).startLocalOnlyHotspot( + any(ILocalOnlyHotspotCallback.class), anyString(), eq(customConfig)); + } + /** * Verify the watchLocalOnlyHotspot call goes to WifiServiceImpl. */ |