diff options
526 files changed, 10877 insertions, 5541 deletions
diff --git a/.gitignore b/.gitignore index 45884c46ffea..d7aebc6d7ee6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /.idea *.iml +*.sw* diff --git a/Android.bp b/Android.bp index 183121b3cf67..1ee7405b42ab 100644 --- a/Android.bp +++ b/Android.bp @@ -209,6 +209,7 @@ java_defaults { "core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl", "core/java/android/hardware/usb/IUsbManager.aidl", "core/java/android/hardware/usb/IUsbSerialReader.aidl", + "core/java/android/net/ICaptivePortal.aidl", "core/java/android/net/IConnectivityManager.aidl", "core/java/android/hardware/ISensorPrivacyListener.aidl", "core/java/android/hardware/ISensorPrivacyManager.aidl", @@ -772,8 +773,8 @@ java_defaults { "android.hardware.vibrator-V1.2-java", "android.hardware.vibrator-V1.3-java", "android.hardware.wifi-V1.0-java-constants", - "networkstack-aidl-interfaces-java", - "netd_aidl_interface-java", + "networkstack-aidl-framework-java", + "netd_aidl_parcelables-java", "devicepolicyprotosnano", ], @@ -893,10 +894,8 @@ aidl_interface { srcs: [ "core/java/android/net/ApfCapabilitiesParcelable.aidl", "core/java/android/net/DhcpResultsParcelable.aidl", - "core/java/android/net/ICaptivePortal.aidl", "core/java/android/net/INetworkMonitor.aidl", "core/java/android/net/INetworkMonitorCallbacks.aidl", - "core/java/android/net/IIpMemoryStore.aidl", "core/java/android/net/INetworkStackConnector.aidl", "core/java/android/net/INetworkStackStatusCallback.aidl", "core/java/android/net/InitialConfigurationParcelable.aidl", @@ -915,6 +914,16 @@ aidl_interface { "core/java/android/net/dhcp/IDhcpServerCallbacks.aidl", "core/java/android/net/ip/IIpClient.aidl", "core/java/android/net/ip/IIpClientCallbacks.aidl", + ], + api_dir: "aidl/networkstack", +} + +aidl_interface { + name: "networkstack-aidl-framework", + local_include_dir: "core/java", + srcs: [ + "core/java/android/net/TcpKeepalivePacketDataParcelable.aidl", + "core/java/android/net/IIpMemoryStore.aidl", "core/java/android/net/ipmemorystore/**/*.aidl", ], api_dir: "aidl/networkstack", @@ -1187,6 +1196,15 @@ packages_to_document = [ "org/apache/http/params", ] +// Make the api/current.txt file available for use by modules in other +// directories. +filegroup { + name: "frameworks-base-api-current.txt", + srcs: [ + "api/current.txt", + ], +} + framework_docs_only_args = " -android -manifest $(location core/res/AndroidManifest.xml) " + "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " + "-overview $(location core/java/overview.html) " + diff --git a/Android.mk b/Android.mk index 9a91dd1c491a..c58f7af1d7d5 100644 --- a/Android.mk +++ b/Android.mk @@ -77,8 +77,6 @@ docs offline-sdk-docs: $(OUT_DOCS)/offline-sdk-timestamp # Run this for checkbuild checkbuild: doc-comment-check-docs -# Check comment when you are updating the API -update-api: doc-comment-check-docs # ==== hiddenapi lists ======================================= ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true) diff --git a/api/current.txt b/api/current.txt index 5da83c1c5f04..fe86cbe48375 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2222,6 +2222,7 @@ package android { field public static final int TextAppearance_WindowTitle = 16973907; // 0x1030053 field public static final int Theme = 16973829; // 0x1030005 field public static final int ThemeOverlay = 16974407; // 0x1030247 + field public static final int ThemeOverlay_DeviceDefault_Accent_DayNight = 16974564; // 0x10302e4 field public static final int ThemeOverlay_Material = 16974408; // 0x1030248 field public static final int ThemeOverlay_Material_ActionBar = 16974409; // 0x1030249 field public static final int ThemeOverlay_Material_Dark = 16974411; // 0x103024b @@ -2233,6 +2234,7 @@ package android { field public static final int Theme_Black_NoTitleBar = 16973833; // 0x1030009 field public static final int Theme_Black_NoTitleBar_Fullscreen = 16973834; // 0x103000a field public static final int Theme_DeviceDefault = 16974120; // 0x1030128 + field public static final int Theme_DeviceDefault_DayNight = 16974563; // 0x10302e3 field public static final int Theme_DeviceDefault_Dialog = 16974126; // 0x103012e field public static final int Theme_DeviceDefault_DialogWhenLarge = 16974134; // 0x1030136 field public static final int Theme_DeviceDefault_DialogWhenLarge_NoActionBar = 16974135; // 0x1030137 @@ -13064,6 +13066,10 @@ package android.database.sqlite { method public String buildUnionSubQuery(String, String[], java.util.Set<java.lang.String>, int, String, String, String, String); method @Deprecated public String buildUnionSubQuery(String, String[], java.util.Set<java.lang.String>, int, String, String, String[], String, String); method public int delete(@NonNull android.database.sqlite.SQLiteDatabase, @Nullable String, @Nullable String[]); + method public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory(); + method public boolean getDistinct(); + method public java.util.Map<java.lang.String,java.lang.String> getProjectionMap(); + method public boolean getStrict(); method public String getTables(); method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String); method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String, String); @@ -14096,6 +14102,34 @@ package android.graphics { ctor @Deprecated public EmbossMaskFilter(float[], float, float, float); } + public class HardwareRenderer { + ctor public HardwareRenderer(); + method public void clearContent(); + method public android.graphics.HardwareRenderer.FrameRenderRequest createRenderRequest(); + method public void destroy(); + method public boolean isOpaque(); + method public void notifyFramePending(); + method public void setContentRoot(@Nullable android.graphics.RenderNode); + method public void setLightSourceAlpha(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float); + method public void setLightSourceGeometry(float, float, float, float); + method public void setName(String); + method public void setOpaque(boolean); + method public void setStopped(boolean); + method public void setSurface(@Nullable android.view.Surface); + field public static final int SYNC_CONTEXT_IS_STOPPED = 4; // 0x4 + field public static final int SYNC_FRAME_DROPPED = 8; // 0x8 + field public static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 2; // 0x2 + field public static final int SYNC_OK = 0; // 0x0 + field public static final int SYNC_REDRAW_REQUESTED = 1; // 0x1 + } + + public final class HardwareRenderer.FrameRenderRequest { + method public android.graphics.HardwareRenderer.FrameRenderRequest setFrameCommitCallback(@NonNull java.util.concurrent.Executor, @NonNull Runnable); + method public android.graphics.HardwareRenderer.FrameRenderRequest setVsyncTime(long); + method public android.graphics.HardwareRenderer.FrameRenderRequest setWaitForPresent(boolean); + method public int syncAndDraw(); + } + public final class ImageDecoder implements java.lang.AutoCloseable { method public void close(); method @AnyThread @NonNull public static android.graphics.ImageDecoder.Source createSource(@NonNull android.content.res.Resources, int); @@ -23380,7 +23414,7 @@ package android.media { method @NonNull public android.media.AudioPresentation.Builder setProgramId(int); } - public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting { + public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection { ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException; method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler); method @Deprecated public void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler); @@ -23415,6 +23449,8 @@ package android.media { method public void release(); method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener); method @Deprecated public void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener); + method public boolean setMicrophoneDirection(int); + method public boolean setMicrophoneFieldDimension(@FloatRange(from=-1.0, to=1.0) float); method public int setNotificationMarkerPosition(int); method public int setPositionNotificationPeriod(int); method public boolean setPreferredDevice(android.media.AudioDeviceInfo); @@ -26064,6 +26100,15 @@ package android.media { field public static final android.media.MediaTimestamp TIMESTAMP_UNKNOWN; } + public interface MicrophoneDirection { + method public boolean setMicrophoneDirection(int); + method public boolean setMicrophoneFieldDimension(@FloatRange(from=-1.0, to=1.0) float); + field public static final int MIC_DIRECTION_BACK = 2; // 0x2 + field public static final int MIC_DIRECTION_EXTERNAL = 3; // 0x3 + field public static final int MIC_DIRECTION_FRONT = 1; // 0x1 + field public static final int MIC_DIRECTION_UNSPECIFIED = 0; // 0x0 + } + public final class MicrophoneInfo { method @NonNull public String getAddress(); method public java.util.List<android.util.Pair<java.lang.Integer,java.lang.Integer>> getChannelMapping(); @@ -27445,6 +27490,7 @@ package android.media.session { method public void seekTo(long); method public void sendCustomAction(@NonNull android.media.session.PlaybackState.CustomAction, @Nullable android.os.Bundle); method public void sendCustomAction(@NonNull String, @Nullable android.os.Bundle); + method public void setPlaybackSpeed(float); method public void setRating(android.media.Rating); method public void skipToNext(); method public void skipToPrevious(); @@ -27495,6 +27541,7 @@ package android.media.session { method public void onPrepareFromUri(android.net.Uri, android.os.Bundle); method public void onRewind(); method public void onSeekTo(long); + method public void onSetPlaybackSpeed(float); method public void onSetRating(@NonNull android.media.Rating); method public void onSkipToNext(); method public void onSkipToPrevious(); @@ -35050,7 +35097,7 @@ package android.os { method public android.os.PowerManager.WakeLock newWakeLock(int, String); method public void reboot(String); method public void registerThermalStatusCallback(@NonNull android.os.PowerManager.ThermalStatusCallback, @NonNull java.util.concurrent.Executor); - method public void unregisterThermalStatusCallback(android.os.PowerManager.ThermalStatusCallback); + method public void unregisterThermalStatusCallback(@NonNull android.os.PowerManager.ThermalStatusCallback); field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000 field public static final String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED"; field public static final String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED"; @@ -41558,6 +41605,7 @@ package android.service.notification { field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR; field public static final String KEY_CONTEXTUAL_ACTIONS = "key_contextual_actions"; field public static final String KEY_IMPORTANCE = "key_importance"; + field public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria"; field public static final String KEY_TEXT_REPLIES = "key_text_replies"; field public static final String KEY_USER_SENTIMENT = "key_user_sentiment"; } @@ -41615,12 +41663,14 @@ package android.service.notification { method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int); method public final android.os.IBinder onBind(android.content.Intent); method public void onNotificationDirectReplied(@NonNull String); - method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification); + method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification); method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel); method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean); method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int); + method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, String); method public void onNotificationsSeen(java.util.List<java.lang.String>); method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int); + method public final void unsnoozeNotification(String); field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService"; field public static final int SOURCE_FROM_APP = 0; // 0x0 field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1 @@ -41953,7 +42003,6 @@ package android.service.voice { public class VoiceInteractionService extends android.app.Service { ctor public VoiceInteractionService(); - method public final void clearTranscription(boolean); method public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback); method public int getDisabledShowContext(); method public static boolean isActiveService(android.content.Context, android.content.ComponentName); @@ -41963,8 +42012,7 @@ package android.service.voice { method public void onReady(); method public void onShutdown(); method public void setDisabledShowContext(int); - method public final void setTranscription(@NonNull String); - method public final void setVoiceState(int); + method public final void setUiHints(@NonNull android.os.Bundle); method public void showSession(android.os.Bundle, int); field public static final String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService"; field public static final String SERVICE_META_DATA = "android.voice_interaction"; @@ -49219,6 +49267,7 @@ package android.view { ctor public ContextThemeWrapper(android.content.Context, android.content.res.Resources.Theme); method public void applyOverrideConfiguration(android.content.res.Configuration); method protected void onApplyThemeResource(android.content.res.Resources.Theme, int, boolean); + method public void setTheme(@Nullable android.content.res.Resources.Theme); } public final class Display { @@ -53474,6 +53523,7 @@ package android.view.contentcapture { method public void close(); method @NonNull public final android.view.contentcapture.ContentCaptureSession createContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext); method public final void destroy(); + method @Nullable public final android.view.contentcapture.ContentCaptureContext getContentCaptureContext(); method public final android.view.contentcapture.ContentCaptureSessionId getContentCaptureSessionId(); method @NonNull public android.view.autofill.AutofillId newAutofillId(@NonNull android.view.autofill.AutofillId, long); method @NonNull public final android.view.ViewStructure newVirtualViewStructure(@NonNull android.view.autofill.AutofillId, long); @@ -53481,6 +53531,7 @@ package android.view.contentcapture { method public final void notifyViewDisappeared(@NonNull android.view.autofill.AutofillId); method public final void notifyViewTextChanged(@NonNull android.view.autofill.AutofillId, @Nullable CharSequence); method public final void notifyViewsDisappeared(@NonNull android.view.autofill.AutofillId, @NonNull long[]); + method public final void setContentCaptureContext(@Nullable android.view.contentcapture.ContentCaptureContext); } public final class ContentCaptureSessionId implements android.os.Parcelable { @@ -55741,6 +55792,7 @@ package android.widget { method public int getDropDownVerticalOffset(); method public int getDropDownWidth(); method protected android.widget.Filter getFilter(); + method public int getInputMethodMode(); method @Deprecated public android.widget.AdapterView.OnItemClickListener getItemClickListener(); method @Deprecated public android.widget.AdapterView.OnItemSelectedListener getItemSelectedListener(); method public int getListSelection(); @@ -55765,6 +55817,7 @@ package android.widget { method public void setDropDownHorizontalOffset(int); method public void setDropDownVerticalOffset(int); method public void setDropDownWidth(int); + method public void setInputMethodMode(int); method public void setListSelection(int); method public void setOnDismissListener(android.widget.AutoCompleteTextView.OnDismissListener); method public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener); diff --git a/api/system-current.txt b/api/system-current.txt index 005d1ba2795a..142068cdb48f 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -285,9 +285,11 @@ package android.app { method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String); method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public static int getCurrentUser(); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String); + method @NonNull public java.util.Collection<java.util.Locale> getSupportedLocales(); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int); method @RequiresPermission(android.Manifest.permission.KILL_UID) public void killUid(int, String); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener); + method public void setDeviceLocales(@NonNull android.os.LocaleList); method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public static void setPersistentVrThread(int); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchUser(@NonNull android.os.UserHandle); } @@ -307,11 +309,13 @@ package android.app { method @Deprecated @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, String, int[]); method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable java.lang.String...); method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getPackagesForOps(@Nullable String[]); + method public int noteProxyOpNoThrow(@NonNull String, @Nullable String, int); method public static int opToDefaultMode(@NonNull String); method @Nullable public static String opToPermission(@NonNull String); method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(String, int, String, int); method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(String, int, int); field public static final String OPSTR_ACCEPT_HANDOVER = "android:accept_handover"; + field public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility"; field public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications"; field public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn"; field public static final String OPSTR_ASSIST_SCREENSHOT = "android:assist_screenshot"; @@ -545,15 +549,17 @@ package android.app { } public class StatusBarManager { - method public android.util.Pair<java.lang.Integer,java.lang.Integer> getDisableFlags(); + method public android.app.StatusBarManager.DisableInfo getDisableInfo(); method public void setDisabledForSetup(boolean); - field public static final int DISABLE2_NONE = 0; // 0x0 - field public static final int DISABLE_EXPAND = 65536; // 0x10000 - field public static final int DISABLE_HOME = 2097152; // 0x200000 - field public static final int DISABLE_NONE = 0; // 0x0 - field public static final int DISABLE_NOTIFICATION_ALERTS = 262144; // 0x40000 - field public static final int DISABLE_RECENT = 16777216; // 0x1000000 - field public static final int DISABLE_SEARCH = 33554432; // 0x2000000 + } + + public static final class StatusBarManager.DisableInfo { + method public boolean areNoComponentsDisabled(); + method public boolean isNavigateToHomeDisabled(); + method public boolean isNotificationPeekingDisabled(); + method public boolean isRecentsDisabled(); + method public boolean isSearchDisabled(); + method public boolean isStatusBarExpansionDisabled(); } public final class Vr2dDisplayProperties implements android.os.Parcelable { @@ -1366,6 +1372,7 @@ package android.content { field public static final String ACTION_INSTALL_INSTANT_APP_PACKAGE = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE"; field public static final String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS"; field public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION"; + field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_MANAGE_APP_PERMISSION = "android.intent.action.MANAGE_APP_PERMISSION"; field public static final String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS"; field @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public static final String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP"; field public static final String ACTION_MANAGE_PERMISSIONS = "android.intent.action.MANAGE_PERMISSIONS"; @@ -1376,6 +1383,7 @@ package android.content { field public static final String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED"; field public static final String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART"; field public static final String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE"; + field public static final String ACTION_REVIEW_ACCESSIBILITY_SERVICES = "android.intent.action.REVIEW_ACCESSIBILITY_SERVICES"; field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_APP_PERMISSION_USAGE = "android.intent.action.REVIEW_APP_PERMISSION_USAGE"; field public static final String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS"; field public static final String ACTION_REVIEW_PERMISSION_USAGE = "android.intent.action.REVIEW_PERMISSION_USAGE"; @@ -3402,13 +3410,13 @@ package android.media { public final class AudioFocusInfo implements android.os.Parcelable { method public int describeContents(); - method public android.media.AudioAttributes getAttributes(); - method public String getClientId(); + method @NonNull public android.media.AudioAttributes getAttributes(); + method @NonNull public String getClientId(); method public int getClientUid(); method public int getFlags(); method public int getGainRequest(); method public int getLossReceived(); - method public String getPackageName(); + method @NonNull public String getPackageName(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.media.AudioFocusInfo> CREATOR; } @@ -3468,7 +3476,7 @@ package android.media { field public static final int PLAYER_TYPE_UNKNOWN = -1; // 0xffffffff } - public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting { + public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection { ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException; } @@ -3926,7 +3934,7 @@ package android.net { method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementValue(int, boolean, @NonNull android.net.ConnectivityManager.TetheringEntitlementValueListener, @Nullable android.os.Handler); method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported(); method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void setAirplaneMode(boolean); - method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.os.Bundle); + method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.net.Network, android.os.Bundle); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int); @@ -5302,6 +5310,10 @@ package android.os { field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR; } + public final class LocaleList implements android.os.Parcelable { + method public static boolean isPseudoLocale(@Nullable android.icu.util.ULocale); + } + public final class NativeHandle implements java.io.Closeable { ctor public NativeHandle(); ctor public NativeHandle(@NonNull java.io.FileDescriptor, boolean); @@ -5780,8 +5792,10 @@ package android.provider { } public static interface DeviceConfig.Rollback { + field public static final String BOOT_NAMESPACE = "rollback_boot"; field public static final String ENABLE_ROLLBACK_TIMEOUT = "enable_rollback_timeout"; field public static final String NAMESPACE = "rollback"; + field public static final String ROLLBACK_LIFETIME_IN_MILLIS = "rollback_lifetime_in_millis"; } public static interface DeviceConfig.Runtime { @@ -5943,6 +5957,7 @@ package android.provider { public final class Settings { field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS"; field public static final String ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS = "android.settings.LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS"; + field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE"; field public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS"; } @@ -6298,20 +6313,11 @@ package android.service.autofill.augmented { } public abstract class PresentationParams { - method public int getFlags(); - method @Nullable public android.service.autofill.augmented.PresentationParams.Area getFullArea(); method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSuggestionArea(); - field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2 - field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4 - field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8 - field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1 - field public static final int FLAG_HOST_IME = 16; // 0x10 - field public static final int FLAG_HOST_SYSTEM = 32; // 0x20 } public abstract static class PresentationParams.Area { method @NonNull public android.graphics.Rect getBounds(); - method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSubArea(@NonNull android.graphics.Rect); } } @@ -6345,7 +6351,8 @@ package android.service.contentcapture { method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId); method public void onDisconnected(); method public void onUserDataRemovalRequest(@NonNull android.view.contentcapture.UserDataRemovalRequest); - method public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>); + method @Deprecated public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>); + method public final void setContentCaptureWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>); field public static final String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService"; } @@ -6516,6 +6523,15 @@ package android.service.euicc { package android.service.notification { + public final class Adjustment implements android.os.Parcelable { + ctor protected Adjustment(android.os.Parcel); + field public static final String KEY_PEOPLE = "key_people"; + } + + public final class NotificationStats implements android.os.Parcelable { + ctor protected NotificationStats(android.os.Parcel); + } + public final class SnoozeCriterion implements android.os.Parcelable { ctor public SnoozeCriterion(String, CharSequence, CharSequence); ctor protected SnoozeCriterion(android.os.Parcel); @@ -9302,7 +9318,8 @@ package android.view.accessibility { package android.view.autofill { public final class AutofillManager { - method public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>); + method @Deprecated public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>); + method public void setAugmentedAutofillWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>); } } @@ -9324,6 +9341,7 @@ package android.view.contentcapture { public final class ContentCaptureEvent implements android.os.Parcelable { method public int describeContents(); + method @Nullable public android.view.contentcapture.ContentCaptureContext getContentCaptureContext(); method public long getEventTime(); method @Nullable public android.view.autofill.AutofillId getId(); method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds(); @@ -9332,6 +9350,7 @@ package android.view.contentcapture { method @Nullable public android.view.contentcapture.ViewNode getViewNode(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR; + field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6 field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5 field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4 field public static final int TYPE_VIEW_APPEARED = 1; // 0x1 diff --git a/api/test-current.txt b/api/test-current.txt index 1a7e4cb83c52..16098c1a80fa 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -516,13 +516,20 @@ package android.content.pm { method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int); method @Nullable public abstract String[] getNamesForUids(int[]); method public abstract String getPermissionControllerPackageName(); + method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS"}) public abstract int getPermissionFlags(String, String, @NonNull android.os.UserHandle); method @NonNull public abstract String getServicesSystemSharedLibraryPackageName(); method @NonNull public abstract String getSharedSystemSharedLibraryPackageName(); method public String getWellbeingPackageName(); method @RequiresPermission("android.permission.GRANT_RUNTIME_PERMISSIONS") public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); + method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS"}) public abstract void updatePermissionFlags(String, String, int, int, @NonNull android.os.UserHandle); field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage"; field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption"; + field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40 + field public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8 + field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80 + field public static final int FLAG_PERMISSION_USER_FIXED = 2; // 0x2 + field public static final int FLAG_PERMISSION_USER_SET = 1; // 0x1 field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000 field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000 field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services"; @@ -802,6 +809,7 @@ package android.location { public class LocationManager { method public String[] getBackgroundThrottlingWhitelist(); + method public String[] getIgnoreSettingsWhitelist(); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(android.location.LocationRequest, android.location.LocationListener, android.os.Looper); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(android.location.LocationRequest, android.app.PendingIntent); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, android.os.UserHandle); @@ -936,7 +944,7 @@ package android.net { } public class ConnectivityManager { - method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.os.Bundle); + method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.net.Network, android.os.Bundle); field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC"; field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT"; } @@ -996,6 +1004,7 @@ package android.net { method public int[] getCapabilities(); method public int[] getTransportTypes(); method public boolean satisfiedByNetworkCapabilities(android.net.NetworkCapabilities); + field public static final int TRANSPORT_TEST = 7; // 0x7 } public class NetworkStack { @@ -1815,6 +1824,7 @@ package android.provider { public final class Settings { field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS"; + field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE"; field public static final int RESET_MODE_PACKAGE_DEFAULTS = 1; // 0x1 } @@ -2073,20 +2083,11 @@ package android.service.autofill.augmented { } public abstract class PresentationParams { - method public int getFlags(); - method @Nullable public android.service.autofill.augmented.PresentationParams.Area getFullArea(); method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSuggestionArea(); - field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2 - field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4 - field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8 - field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1 - field public static final int FLAG_HOST_IME = 16; // 0x10 - field public static final int FLAG_HOST_SYSTEM = 32; // 0x20 } public abstract static class PresentationParams.Area { method @NonNull public android.graphics.Rect getBounds(); - method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSubArea(@NonNull android.graphics.Rect); } } @@ -2110,7 +2111,8 @@ package android.service.contentcapture { method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId); method public void onDisconnected(); method public void onUserDataRemovalRequest(@NonNull android.view.contentcapture.UserDataRemovalRequest); - method public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>); + method @Deprecated public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>); + method public final void setContentCaptureWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>); field public static final String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService"; } @@ -2699,7 +2701,8 @@ package android.view.autofill { } public final class AutofillManager { - method public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>); + method @Deprecated public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>); + method public void setAugmentedAutofillWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>); field public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES = "smart_suggestion_supported_modes"; field public static final int FLAG_SMART_SUGGESTION_OFF = 0; // 0x0 field public static final int FLAG_SMART_SUGGESTION_SYSTEM = 1; // 0x1 @@ -2725,6 +2728,7 @@ package android.view.contentcapture { public final class ContentCaptureEvent implements android.os.Parcelable { method public int describeContents(); + method @Nullable public android.view.contentcapture.ContentCaptureContext getContentCaptureContext(); method public long getEventTime(); method @Nullable public android.view.autofill.AutofillId getId(); method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds(); @@ -2733,6 +2737,7 @@ package android.view.contentcapture { method @Nullable public android.view.contentcapture.ViewNode getViewNode(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR; + field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6 field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5 field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4 field public static final int TYPE_VIEW_APPEARED = 1; // 0x1 @@ -2743,7 +2748,15 @@ package android.view.contentcapture { public final class ContentCaptureManager { method public boolean isContentCaptureFeatureEnabled(); method public void setContentCaptureFeatureEnabled(boolean); + field public static final String DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY = "idle_flush_frequency"; + field public static final String DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL = "logging_level"; + field public static final String DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE = "log_history_size"; + field public static final String DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE = "max_buffer_size"; field public static final String DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED = "service_explicitly_enabled"; + field public static final String DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY = "text_change_flush_frequency"; + field public static final int LOGGING_LEVEL_DEBUG = 1; // 0x1 + field public static final int LOGGING_LEVEL_OFF = 0; // 0x0 + field public static final int LOGGING_LEVEL_VERBOSE = 2; // 0x2 } public final class ViewNode extends android.app.assist.AssistStructure.ViewNode { diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 46917e4f6062..a6c7cae9bdb6 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -110,13 +110,30 @@ BootAnimation::BootAnimation(sp<Callbacks> callbacks) } else { mShuttingDown = true; } + ALOGD("%sAnimationStartTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot", + elapsedRealtime()); +} + +BootAnimation::~BootAnimation() { + if (mAnimation != nullptr) { + releaseAnimation(mAnimation); + mAnimation = nullptr; + } + ALOGD("%sAnimationStopTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot", + elapsedRealtime()); } void BootAnimation::onFirstRef() { status_t err = mSession->linkToComposerDeath(this); SLOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err)); if (err == NO_ERROR) { - run("BootAnimation", PRIORITY_DISPLAY); + // Load the animation content -- this can be slow (eg 200ms) + // called before waitForSurfaceFlinger() in main() to avoid wait + ALOGD("%sAnimationPreloadTiming start time: %" PRId64 "ms", + mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime()); + preloadAnimation(); + ALOGD("%sAnimationPreloadStopTiming start time: %" PRId64 "ms", + mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime()); } } @@ -306,6 +323,20 @@ status_t BootAnimation::readyToRun() { mFlingerSurface = s; mTargetInset = -1; + return NO_ERROR; +} + +bool BootAnimation::preloadAnimation() { + findBootAnimationFile(); + if (!mZipFileName.isEmpty()) { + mAnimation = loadAnimation(mZipFileName); + return (mAnimation != nullptr); + } + + return false; +} + +void BootAnimation::findBootAnimationFile() { // If the device has encryption turned on or is in process // of being encrypted we show the encrypted boot animation. char decrypt[PROPERTY_VALUE_MAX]; @@ -320,7 +351,7 @@ status_t BootAnimation::readyToRun() { for (const char* f : encryptedBootFiles) { if (access(f, R_OK) == 0) { mZipFileName = f; - return NO_ERROR; + return; } } } @@ -332,10 +363,9 @@ status_t BootAnimation::readyToRun() { for (const char* f : (!mShuttingDown ? bootFiles : shutdownFiles)) { if (access(f, R_OK) == 0) { mZipFileName = f; - return NO_ERROR; + return; } } - return NO_ERROR; } bool BootAnimation::threadLoop() @@ -790,8 +820,6 @@ bool BootAnimation::preloadZip(Animation& animation) } } - mCallbacks->init(animation.parts); - zip->endIteration(cookie); return true; @@ -799,13 +827,25 @@ bool BootAnimation::preloadZip(Animation& animation) bool BootAnimation::movie() { - Animation* animation = loadAnimation(mZipFileName); - if (animation == NULL) + if (mAnimation == nullptr) { + mAnimation = loadAnimation(mZipFileName); + } + + if (mAnimation == nullptr) return false; + // mCallbacks->init() may get called recursively, + // this loop is needed to get the same results + for (const Animation::Part& part : mAnimation->parts) { + if (part.animation != nullptr) { + mCallbacks->init(part.animation->parts); + } + } + mCallbacks->init(mAnimation->parts); + bool anyPartHasClock = false; - for (size_t i=0; i < animation->parts.size(); i++) { - if(validClock(animation->parts[i])) { + for (size_t i=0; i < mAnimation->parts.size(); i++) { + if(validClock(mAnimation->parts[i])) { anyPartHasClock = true; break; } @@ -846,7 +886,7 @@ bool BootAnimation::movie() bool clockFontInitialized = false; if (mClockEnabled) { clockFontInitialized = - (initFont(&animation->clockFont, CLOCK_FONT_ASSET) == NO_ERROR); + (initFont(&mAnimation->clockFont, CLOCK_FONT_ASSET) == NO_ERROR); mClockEnabled = clockFontInitialized; } @@ -855,7 +895,7 @@ bool BootAnimation::movie() mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL); } - playAnimation(*animation); + playAnimation(*mAnimation); if (mTimeCheckThread != nullptr) { mTimeCheckThread->requestExit(); @@ -863,10 +903,11 @@ bool BootAnimation::movie() } if (clockFontInitialized) { - glDeleteTextures(1, &animation->clockFont.texture.name); + glDeleteTextures(1, &mAnimation->clockFont.texture.name); } - releaseAnimation(animation); + releaseAnimation(mAnimation); + mAnimation = nullptr; return false; } diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index 19616cb790c7..dc19fb09ef1d 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -115,6 +115,7 @@ public: }; explicit BootAnimation(sp<Callbacks> callbacks); + virtual ~BootAnimation(); sp<SurfaceComposerClient> session() const; @@ -155,6 +156,8 @@ private: void releaseAnimation(Animation*) const; bool parseAnimationDesc(Animation&); bool preloadZip(Animation &animation); + void findBootAnimationFile(); + bool preloadAnimation(); void checkExit(); @@ -182,6 +185,7 @@ private: SortedVector<String8> mLoadedFiles; sp<TimeCheckThread> mTimeCheckThread = nullptr; sp<Callbacks> mCallbacks; + Animation* mAnimation = nullptr; }; // --------------------------------------------------------------------------- diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp index a52a5e92a840..6c7b3e51ff8d 100644 --- a/cmds/bootanimation/bootanimation_main.cpp +++ b/cmds/bootanimation/bootanimation_main.cpp @@ -44,14 +44,16 @@ int main() sp<ProcessState> proc(ProcessState::self()); ProcessState::self()->startThreadPool(); + // create the boot animation object (may take up to 200ms for 2MB zip) + sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks()); + waitForSurfaceFlinger(); - // create the boot animation object - sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks()); + boot->run("BootAnimation", PRIORITY_DISPLAY); + ALOGV("Boot animation set up. Joining pool."); IPCThreadState::self()->joinThreadPool(); } - ALOGV("Boot animation exit"); return 0; } diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 04ab59218af2..f4086557870d 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -76,6 +76,7 @@ cc_defaults { "src/external/SubsystemSleepStatePuller.cpp", "src/external/PowerStatsPuller.cpp", "src/external/ResourceHealthManagerPuller.cpp", + "src/external/TrainInfoPuller.cpp", "src/external/StatsPullerManager.cpp", "src/external/puller_util.cpp", "src/logd/LogEvent.cpp", diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp index 80ed80776829..13f5c8ae5fd8 100644 --- a/cmds/statsd/src/FieldValue.cpp +++ b/cmds/statsd/src/FieldValue.cpp @@ -19,6 +19,7 @@ #include "FieldValue.h" #include "HashableDimensionKey.h" #include "math.h" +#include "statslog.h" namespace android { namespace os { @@ -122,6 +123,24 @@ bool isAttributionUidField(const FieldValue& value) { return false; } +int32_t getUidIfExists(const FieldValue& value) { + bool isUid = false; + // the field is uid field if the field is the uid field in attribution node or marked as + // is_uid in atoms.proto + if (isAttributionUidField(value)) { + isUid = true; + } else { + auto it = android::util::AtomsInfo::kAtomsWithUidField.find(value.mField.getTag()); + if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) { + int uidField = it->second; // uidField is the field number in proto + isUid = value.mField.getDepth() == 0 && value.mField.getPosAtDepth(0) == uidField && + value.mValue.getType() == INT; + } + } + + return isUid ? value.mValue.int_value : -1; +} + bool isAttributionUidField(const Field& field, const Value& value) { int f = field.getField() & 0xff007f; if (f == 0x10001 && value.getType() == INT) { diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h index a5d00ac4e72b..6729e052b5ee 100644 --- a/cmds/statsd/src/FieldValue.h +++ b/cmds/statsd/src/FieldValue.h @@ -386,6 +386,9 @@ bool HasPositionALL(const FieldMatcher& matcher); bool isAttributionUidField(const FieldValue& value); +/* returns uid if the field is uid field, or -1 if the field is not a uid field */ +int getUidIfExists(const FieldValue& value); + void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output); bool isAttributionUidField(const Field& field, const Value& value); diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index d23fc62cf816..c542b6215c88 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -55,9 +55,6 @@ namespace android { namespace os { namespace statsd { -const int FIELD_ID_EXPERIMENT_ID = 1; -const int FIELD_ID_EXPERIMENT_ID_MSG = 7; - constexpr const char* kPermissionDump = "android.permission.DUMP"; constexpr const char* kPermissionUsage = "android.permission.PACKAGE_USAGE_STATS"; @@ -67,6 +64,8 @@ constexpr const char* kOpUsage = "android:get_usage_stats"; // for StatsDataDumpProto const int FIELD_ID_REPORTS_LIST = 1; +// for TrainInfo experiment id serialization +const int FIELD_ID_EXPERIMENT_ID = 1; static binder::Status ok() { return binder::Status::ok(); @@ -160,7 +159,7 @@ StatsService::StatsService(const sp<Looper>& handlerLooper) } })) { - mUidMap = new UidMap(); + mUidMap = UidMap::getInstance(); mPullerManager = new StatsPullerManager(); StatsPuller::SetUidMap(mUidMap); mConfigManager = new ConfigManager(); @@ -1200,12 +1199,10 @@ Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& tra bool requiresLowLatencyMonitor = options | IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR; ProtoOutputStream proto; - uint64_t protoToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_EXPERIMENT_ID_MSG); for (const auto& expId : experimentIds) { proto.write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_EXPERIMENT_ID, (long long)expId); } - proto.end(protoToken); vector<uint8_t> buffer; buffer.resize(proto.size()); diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp index 8d7369935ea4..019a9f7e5f9a 100644 --- a/cmds/statsd/src/anomaly/AlarmTracker.cpp +++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp @@ -78,8 +78,8 @@ void AlarmTracker::informAlarmsFired( } if (!mSubscriptions.empty()) { VLOG("AlarmTracker triggers the subscribers."); - triggerSubscribers(mAlarmConfig.id(), DEFAULT_METRIC_DIMENSION_KEY, mConfigKey, - mSubscriptions); + triggerSubscribers(mAlarmConfig.id(), 0 /*metricId N/A*/, DEFAULT_METRIC_DIMENSION_KEY, + 0 /* metricValue N/A */, mConfigKey, mSubscriptions); } firedAlarms.erase(mInternalAlarm); mAlarmSec = findNextAlarmSec((timestampNs-1) / NS_PER_SEC + 1); // round up diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index ee111cddcfd7..d1dcb5df7838 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -207,7 +207,8 @@ bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt(); } -void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, const MetricDimensionKey& key) { +void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, int64_t metricId, + const MetricDimensionKey& key, int64_t metricValue) { // TODO(b/110563466): Why receive timestamp? RefractoryPeriod should always be based on // real time right now. if (isInRefractoryPeriod(timestampNs, key)) { @@ -225,7 +226,7 @@ void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, const MetricDime if (!mSubscriptions.empty()) { ALOGI("An anomaly (%lld) %s has occurred! Informing subscribers.", mAlert.id(), key.toString().c_str()); - informSubscribers(key); + informSubscribers(key, metricId, metricValue); } else { ALOGI("An anomaly has occurred! (But no subscriber for that alert.)"); } @@ -238,11 +239,11 @@ void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, const MetricDime } void AnomalyTracker::detectAndDeclareAnomaly(const int64_t& timestampNs, - const int64_t& currBucketNum, + const int64_t& currBucketNum, int64_t metricId, const MetricDimensionKey& key, const int64_t& currentBucketValue) { if (detectAnomaly(currBucketNum, key, currentBucketValue)) { - declareAnomaly(timestampNs, key); + declareAnomaly(timestampNs, metricId, key, currentBucketValue); } } @@ -255,8 +256,9 @@ bool AnomalyTracker::isInRefractoryPeriod(const int64_t& timestampNs, return false; } -void AnomalyTracker::informSubscribers(const MetricDimensionKey& key) { - triggerSubscribers(mAlert.id(), key, mConfigKey, mSubscriptions); +void AnomalyTracker::informSubscribers(const MetricDimensionKey& key, int64_t metric_id, + int64_t metricValue) { + triggerSubscribers(mAlert.id(), metric_id, key, metricValue, mConfigKey, mSubscriptions); } } // namespace statsd diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h index 927e2df1b53a..e9414735b82b 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.h +++ b/cmds/statsd/src/anomaly/AnomalyTracker.h @@ -67,14 +67,16 @@ public: const int64_t& currentBucketValue); // Informs incidentd about the detected alert. - void declareAnomaly(const int64_t& timestampNs, const MetricDimensionKey& key); + void declareAnomaly(const int64_t& timestampNs, int64_t metricId, const MetricDimensionKey& key, + int64_t metricValue); // Detects if, based on past buckets plus the new currentBucketValue (which generally // represents the partially-filled current bucket), an anomaly has happened, and if so, // declares an anomaly and informs relevant subscribers. // Also advances to currBucketNum-1. void detectAndDeclareAnomaly(const int64_t& timestampNs, const int64_t& currBucketNum, - const MetricDimensionKey& key, const int64_t& currentBucketValue); + int64_t metricId, const MetricDimensionKey& key, + const int64_t& currentBucketValue); // Init the AlarmMonitor which is shared across anomaly trackers. virtual void setAlarmMonitor(const sp<AlarmMonitor>& alarmMonitor) { @@ -176,7 +178,7 @@ protected: virtual void resetStorage(); // Informs the subscribers (incidentd, perfetto, broadcasts, etc) that an anomaly has occurred. - void informSubscribers(const MetricDimensionKey& key); + void informSubscribers(const MetricDimensionKey& key, int64_t metricId, int64_t metricValue); FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets); FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets); diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp index 3acfd171848e..2b56810170e5 100644 --- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp @@ -65,7 +65,9 @@ void DurationAnomalyTracker::stopAlarm(const MetricDimensionKey& dimensionKey, // If the alarm is set in the past but hasn't fired yet (due to lag), catch it now. if (itr->second != nullptr && timestampNs >= (int64_t)NS_PER_SEC * itr->second->timestampSec) { - declareAnomaly(timestampNs, dimensionKey); + declareAnomaly(timestampNs, mAlert.metric_id(), dimensionKey, + mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) - + itr->second->timestampSec); } if (mAlarmMonitor != nullptr) { mAlarmMonitor->remove(itr->second); @@ -100,7 +102,9 @@ void DurationAnomalyTracker::informAlarmsFired(const int64_t& timestampNs, // Now declare each of these alarms to have fired. for (const auto& kv : matchedAlarms) { - declareAnomaly(timestampNs, kv.first); + declareAnomaly( + timestampNs, mAlert.metric_id(), kv.first, + mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) - kv.second->timestampSec); mAlarms.erase(kv.first); firedAlarms.erase(kv.second); // No one else can also own it, so we're done with it. } diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp index 6b46b8b3b900..548a6869436d 100644 --- a/cmds/statsd/src/anomaly/subscriber_util.cpp +++ b/cmds/statsd/src/anomaly/subscriber_util.cpp @@ -30,9 +30,8 @@ namespace android { namespace os { namespace statsd { -void triggerSubscribers(const int64_t rule_id, - const MetricDimensionKey& dimensionKey, - const ConfigKey& configKey, +void triggerSubscribers(int64_t ruleId, int64_t metricId, const MetricDimensionKey& dimensionKey, + int64_t metricValue, const ConfigKey& configKey, const std::vector<Subscription>& subscriptions) { VLOG("informSubscribers called."); if (subscriptions.empty()) { @@ -50,13 +49,14 @@ void triggerSubscribers(const int64_t rule_id, } switch (subscription.subscriber_information_case()) { case Subscription::SubscriberInformationCase::kIncidentdDetails: - if (!GenerateIncidentReport(subscription.incidentd_details(), rule_id, configKey)) { + if (!GenerateIncidentReport(subscription.incidentd_details(), ruleId, metricId, + dimensionKey, metricValue, configKey)) { ALOGW("Failed to generate incident report."); } break; case Subscription::SubscriberInformationCase::kPerfettoDetails: if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details(), - subscription.id(), rule_id, configKey)) { + subscription.id(), ruleId, configKey)) { ALOGW("Failed to generate perfetto traces."); } break; @@ -66,7 +66,7 @@ void triggerSubscribers(const int64_t rule_id, break; case Subscription::SubscriberInformationCase::kPerfprofdDetails: if (!CollectPerfprofdTraceAndUploadToDropbox(subscription.perfprofd_details(), - rule_id, configKey)) { + ruleId, configKey)) { ALOGW("Failed to generate perfprofd traces."); } break; @@ -76,7 +76,6 @@ void triggerSubscribers(const int64_t rule_id, } } - } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/anomaly/subscriber_util.h b/cmds/statsd/src/anomaly/subscriber_util.h index dba8981a72aa..1df3c8991f94 100644 --- a/cmds/statsd/src/anomaly/subscriber_util.h +++ b/cmds/statsd/src/anomaly/subscriber_util.h @@ -24,10 +24,9 @@ namespace android { namespace os { namespace statsd { -void triggerSubscribers(const int64_t rule_id, - const MetricDimensionKey& dimensionKey, - const ConfigKey& configKey, - const std::vector<Subscription>& subscriptions); +void triggerSubscribers(const int64_t ruleId, const int64_t metricId, + const MetricDimensionKey& dimensionKey, int64_t metricValue, + const ConfigKey& configKey, const std::vector<Subscription>& subscriptions); } // namespace statsd } // namespace os diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 873b772d06ea..d7ab6e5f89bb 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -245,6 +245,7 @@ message Atom { AssistGestureStageReported assist_gesture_stage_reported = 174; AssistGestureFeedbackReported assist_gesture_feedback_reported = 175; AssistGestureProgressReported assist_gesture_progress_reported = 176; + TouchGestureClassified touch_gesture_classified = 177; } // Pulled events will start at field 10000. @@ -302,6 +303,8 @@ message Atom { RoleHolder role_holder = 10049; DangerousPermissionState dangerous_permission_state = 10050; TrainInfo train_info = 10051; + TimeZoneDataInfo time_zone_data_info = 10052; + SDCardInfo sdcard_info = 10053; } // DO NOT USE field numbers above 100,000 in AOSP. @@ -2407,6 +2410,37 @@ message TouchEventReported { } /** + * Logs gesture classification and timing information for touch events. + * + * Logged from: + * frameworks/base/core/java/android/view/GestureDetector.java + * frameworks/base/core/java/android/view/View.java + */ +message TouchGestureClassified { + // The source of the classification (e.g. Java class name). + optional string source = 1; + + enum Classification { + UNKNOWN_CLASSIFICATION = 0; + SINGLE_TAP = 1; + DOUBLE_TAP = 2; + LONG_PRESS = 3; + DEEP_PRESS = 4; + SCROLL = 5; + } + // The classification of the gesture. + optional Classification classification = 2; + + // The interval from the start of a touch event stream until the + // classification was made. + optional int32 latency_millis = 3; + + // The distance from the location of the first touch event to the + // location of the touch event when the classification was made. + optional float displacement_px = 4; +} + +/** * Logs that a setting was updated. * Logged from: * frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -3230,6 +3264,28 @@ message BatteryCycleCount { optional int32 cycle_count = 1; } +/** + * Logs that an SD card is mounted and information about it, its type (public or private) and the + * size in bytes. + * Pulled from: + * StatsCompanionService + */ + +message SDCardInfo { + + enum Type { + UNKNOWN = 0; + TYPE_PUBLIC = 1; + TYPE_PRIVATE = 2; + OTHERS = 3; + } + + // Type of the SD card: TYPE_PUBLIC if portable and TYPE_PRIVATE if internal. + optional Type type = 1; + // Total size of the sd card in bytes. + optional int64 size_bytes = 2; +} + /* * Logs when a connection becomes available and lost. * Logged in StatsCompanionService.java @@ -5564,3 +5620,11 @@ message AssistGestureProgressReported { // [0,100] progress for the assist gesture. optional int32 progress = 1; } + +/* + * Information about the time zone data on a device. + */ +message TimeZoneDataInfo { + // A version identifier for the data set on device. e.g. "2018i" + optional string tzdb_version = 1; +} diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index ed72b2914a34..1513834b6724 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -29,10 +29,11 @@ #include "../statscompanion_util.h" #include "PowerStatsPuller.h" #include "ResourceHealthManagerPuller.h" -#include "StatsCompanionServicePuller.h" #include "StatsCallbackPuller.h" +#include "StatsCompanionServicePuller.h" #include "StatsPullerManager.h" #include "SubsystemSleepStatePuller.h" +#include "TrainInfoPuller.h" #include "statslog.h" #include <iostream> @@ -152,7 +153,7 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}}, // temperature {android::util::TEMPERATURE, - {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}}, + {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}}, // binder_calls {android::util::BINDER_CALLS, {.additiveFields = {4, 5, 6, 8, 12}, @@ -231,6 +232,14 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { // PermissionState. {android::util::DANGEROUS_PERMISSION_STATE, {.puller = new StatsCompanionServicePuller(android::util::DANGEROUS_PERMISSION_STATE)}}, + // TrainInfo. + {android::util::TRAIN_INFO, {.puller = new TrainInfoPuller()}}, + // TimeZoneDataInfo. + {android::util::TIME_ZONE_DATA_INFO, + {.puller = new StatsCompanionServicePuller(android::util::TIME_ZONE_DATA_INFO)}}, + // SDCardInfo + {android::util::SDCARD_INFO, + {.puller = new StatsCompanionServicePuller(android::util::SDCARD_INFO)}}, }; StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) { diff --git a/cmds/statsd/src/external/TrainInfoPuller.cpp b/cmds/statsd/src/external/TrainInfoPuller.cpp new file mode 100644 index 000000000000..9d0924297912 --- /dev/null +++ b/cmds/statsd/src/external/TrainInfoPuller.cpp @@ -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. + */ + +#define DEBUG false // STOPSHIP if true +#include "Log.h" + +#include "external/StatsPuller.h" + +#include "TrainInfoPuller.h" +#include "logd/LogEvent.h" +#include "stats_log_util.h" +#include "statslog.h" +#include "storage/StorageManager.h" + +using std::make_shared; +using std::shared_ptr; + +namespace android { +namespace os { +namespace statsd { + +TrainInfoPuller::TrainInfoPuller() : + StatsPuller(android::util::TRAIN_INFO) { +} + +bool TrainInfoPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { + InstallTrainInfo trainInfo; + bool ret = StorageManager::readTrainInfo(trainInfo); + if (!ret) { + ALOGW("Failed to read train info."); + return false; + } + auto event = make_shared<LogEvent>(getWallClockNs(), getElapsedRealtimeNs(), trainInfo); + data->push_back(event); + return true; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/TrainInfoPuller.h b/cmds/statsd/src/external/TrainInfoPuller.h new file mode 100644 index 000000000000..615d02351fd3 --- /dev/null +++ b/cmds/statsd/src/external/TrainInfoPuller.h @@ -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. + */ + +#pragma once + +#include "StatsPuller.h" + +namespace android { +namespace os { +namespace statsd { + +/** + * Reads train info from disk. + */ +class TrainInfoPuller : public StatsPuller { + public: + TrainInfoPuller(); + + private: + bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override; +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index b433c41518cc..d661ee8651be 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -478,6 +478,11 @@ void StatsdStats::noteBucketDropped(int64_t metricId) { getAtomMetricStats(metricId).bucketDropped++; } +void StatsdStats::noteBucketUnknownCondition(int64_t metricId) { + lock_guard<std::mutex> lock(mLock); + getAtomMetricStats(metricId).bucketUnknownCondition++; +} + void StatsdStats::noteConditionChangeInNextBucket(int64_t metricId) { lock_guard<std::mutex> lock(mLock); getAtomMetricStats(metricId).conditionChangeInNextBucket++; @@ -497,7 +502,7 @@ void StatsdStats::noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayN std::min(pullStats.minBucketBoundaryDelayNs, timeDelayNs); } -StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int metricId) { +StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int64_t metricId) { auto atomMetricStatsIter = mAtomMetricStats.find(metricId); if (atomMetricStatsIter != mAtomMetricStats.end()) { return atomMetricStatsIter->second; diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 5275c8f34fd0..e039be2b4395 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -409,6 +409,11 @@ public: void noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayNs); /** + * Number of buckets with unknown condition. + */ + void noteBucketUnknownCondition(int64_t metricId); + + /** * Reset the historical stats. Including all stats in icebox, and the tracked stats about * metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue * to collect stats after reset() has been called. @@ -458,6 +463,7 @@ public: long bucketDropped = 0; int64_t minBucketBoundaryDelayNs = 0; int64_t maxBucketBoundaryDelayNs = 0; + long bucketUnknownCondition = 0; } AtomMetricStats; private: @@ -488,7 +494,7 @@ private: std::map<int, PulledAtomStats> mPulledAtomStats; // Maps metric ID to its stats. The size is capped by the number of metrics. - std::map<int, AtomMetricStats> mAtomMetricStats; + std::map<int64_t, AtomMetricStats> mAtomMetricStats; struct LogLossStats { LogLossStats(int32_t sec, int32_t count, int32_t error) @@ -532,7 +538,7 @@ private: * Get a reference to AtomMetricStats for a metric. If none exists, create it. The reference * will live as long as `this`. */ - StatsdStats::AtomMetricStats& getAtomMetricStats(int metricId); + StatsdStats::AtomMetricStats& getAtomMetricStats(int64_t metricId); FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd); FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd); diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 99ebc965f5a8..dec36b54a1ce 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -361,6 +361,16 @@ LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, } } +LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, + const InstallTrainInfo& trainInfo) { + mLogdTimestampNs = wallClockTimestampNs; + mElapsedTimestampNs = elapsedTimestampNs; + mTagId = android::util::TRAIN_INFO; + mValues.push_back( + FieldValue(Field(mTagId, getSimpleField(1)), Value(trainInfo.trainVersionCode))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainInfo.experimentIds))); +} + LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) : LogEvent(tagId, timestampNs, 0) {} LogEvent::LogEvent(int32_t tagId, int64_t timestampNs, int32_t uid) { diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 6b5fa56d4c8b..111a619760df 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -55,6 +55,11 @@ struct AttributionNodeInternal { int32_t mUid; std::string mTag; }; + +struct InstallTrainInfo { + int64_t trainVersionCode; + std::vector<uint8_t> experimentIds; +}; /** * Wrapper for the log_msg structure. */ @@ -132,6 +137,9 @@ public: explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const VendorAtom& vendorAtom); + explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, + const InstallTrainInfo& installTrainInfo); + ~LogEvent(); /** diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 350745b2c326..e84f88d407d3 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -250,7 +250,7 @@ void CountMetricProducer::dropDataLocked(const int64_t dropTimeNs) { void CountMetricProducer::onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) { VLOG("Metric %lld onConditionChanged", (long long)mMetricId); - mCondition = conditionMet; + mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; } bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { @@ -303,7 +303,7 @@ void CountMetricProducer::onMatchedLogEventInternalLocked( if (prev != mCurrentFullCounters->end()) { countWholeBucket += prev->second; } - tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, + tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey, countWholeBucket); } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 6c1c47bbc093..da6b97cc4e59 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -433,7 +433,7 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondit void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) { VLOG("Metric %lld onConditionChanged", (long long)mMetricId); - mCondition = conditionMet; + mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; flushIfNeededLocked(eventTime); for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { for (auto& pair : whatIt.second) { @@ -767,12 +767,13 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, !mSameConditionDimensionsInTracker, !mHasLinksToAllConditionDimensionsInTracker, &dimensionKeysInCondition); - condition = (conditionState == ConditionState::kTrue); + condition = conditionState == ConditionState::kTrue; if (mDimensionsInCondition.empty() && condition) { dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY); } } else { - condition = mCondition; + // TODO: The unknown condition state is not handled here, we should fix it. + condition = mCondition == ConditionState::kTrue; if (condition) { dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY); } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 1b830a3f69f5..ec561b527025 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -137,6 +137,7 @@ private: FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition); FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition); + FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState); FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade); FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket); FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade); diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index 7e695a69988f..3b4af6533e34 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -132,7 +132,7 @@ void EventMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, void EventMetricProducer::onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) { VLOG("Metric %lld onConditionChanged", (long long)mMetricId); - mCondition = conditionMet; + mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; } void EventMetricProducer::onMatchedLogEventInternalLocked( diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index d56a355f15d6..63017936b1db 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -320,15 +320,15 @@ void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) { // When the metric wants to do random sampling and there is already one gauge atom for the // current bucket, do not do it again. case GaugeMetric::RANDOM_ONE_SAMPLE: { - triggerPuller = mCondition && mCurrentSlicedBucket->empty(); + triggerPuller = mCondition == ConditionState::kTrue && mCurrentSlicedBucket->empty(); break; } case GaugeMetric::CONDITION_CHANGE_TO_TRUE: { - triggerPuller = mCondition; + triggerPuller = mCondition == ConditionState::kTrue; break; } case GaugeMetric::FIRST_N_SAMPLES: { - triggerPuller = mCondition; + triggerPuller = mCondition == ConditionState::kTrue; break; } default: @@ -364,7 +364,7 @@ void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet, const int64_t eventTimeNs) { VLOG("GaugeMetric %lld onConditionChanged", (long long)mMetricId); flushIfNeededLocked(eventTimeNs); - mCondition = conditionMet; + mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; if (mIsPulled && mTriggerAtomId == -1) { pullAndMatchEventsLocked(eventTimeNs); } // else: Push mode. No need to proactively pull the gauge data. @@ -377,7 +377,7 @@ void GaugeMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition flushIfNeededLocked(eventTimeNs); // If the condition is sliced, mCondition is true if any of the dimensions is true. And we will // pull for every dimension. - mCondition = overallCondition; + mCondition = overallCondition ? ConditionState::kTrue : ConditionState::kFalse; if (mIsPulled && mTriggerAtomId == -1) { pullAndMatchEventsLocked(eventTimeNs); } // else: Push mode. No need to proactively pull the gauge data. @@ -485,8 +485,8 @@ void GaugeMetricProducer::onMatchedLogEventInternalLocked( gaugeVal = value.long_value; } for (auto& tracker : mAnomalyTrackers) { - tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, - gaugeVal); + tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, + eventKey, gaugeVal); } } } diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 65136eedb137..4cf5333947d3 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -45,7 +45,8 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo &dimensionKeysInCondition); condition = (conditionState == ConditionState::kTrue); } else { - condition = mCondition; + // TODO: The unknown condition state is not handled here, we should fix it. + condition = mCondition == ConditionState::kTrue; } if (mDimensionsInCondition.empty() && condition) { diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 849cb76ec392..8ab3b0680276 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -59,7 +59,7 @@ public: mTimeBaseNs(timeBaseNs), mCurrentBucketStartTimeNs(timeBaseNs), mCurrentBucketNum(0), - mCondition(conditionIndex >= 0 ? false : true), + mCondition(conditionIndex >= 0 ? ConditionState::kUnknown : ConditionState::kTrue), mConditionSliced(false), mWizard(wizard), mConditionTrackerIndex(conditionIndex), @@ -315,7 +315,7 @@ protected: int64_t mBucketSizeNs; - bool mCondition; + ConditionState mCondition; bool mConditionSliced; diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 851ae9962d09..3cf378d7d7ce 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -154,7 +154,7 @@ ValueMetricProducer::ValueMetricProducer( // Adjust start for partial bucket mCurrentBucketStartTimeNs = startTimeNs; // Kicks off the puller immediately if condition is true and diff based. - if (mIsPulled && mCondition && mUseDiff) { + if (mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) { pullAndMatchEventsLocked(startTimeNs); } VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), @@ -341,17 +341,21 @@ void ValueMetricProducer::onConditionChangedLocked(const bool condition, flushIfNeededLocked(eventTimeNs); // Pull on condition changes. - if (mIsPulled && (mCondition != condition)) { + bool conditionChanged = mCondition != condition; + bool unknownToFalse = mCondition == ConditionState::kUnknown + && condition == ConditionState::kFalse; + // We do not need to pull when we go from unknown to false. + if (mIsPulled && conditionChanged && !unknownToFalse) { pullAndMatchEventsLocked(eventTimeNs); } // when condition change from true to false, clear diff base but don't // reset other counters as we may accumulate more value in the bucket. - if (mUseDiff && mCondition && !condition) { + if (mUseDiff && mCondition == ConditionState::kTrue && condition == ConditionState::kFalse) { resetBase(); } - mCondition = condition; + mCondition = condition ? ConditionState::kTrue : ConditionState::kFalse; } void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) { @@ -372,7 +376,7 @@ int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTime void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData, bool pullSuccess, int64_t originalPullTimeNs) { std::lock_guard<std::mutex> lock(mMutex); - if (mCondition) { + if (mCondition == ConditionState::kTrue) { if (!pullSuccess) { // If the pull failed, we won't be able to compute a diff. invalidateCurrentBucket(); @@ -693,8 +697,8 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn wholeBucketVal += prev->second; } for (auto& tracker : mAnomalyTrackers) { - tracker->detectAndDeclareAnomaly( - eventTimeNs, mCurrentBucketNum, eventKey, wholeBucketVal); + tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey, + wholeBucketVal); } } } @@ -725,6 +729,10 @@ void ValueMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) { } void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs) { + if (mCondition == ConditionState::kUnknown) { + StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId); + } + VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs, (int)mCurrentSlicedBucket.size()); int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs(); diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h index ccb1d4359e89..081e61ed21fa 100644 --- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h @@ -155,8 +155,8 @@ protected: const int64_t& currentBucketValue) { for (auto& anomalyTracker : mAnomalyTrackers) { if (anomalyTracker != nullptr) { - anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mEventKey, - currentBucketValue); + anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mTrackerId, + mEventKey, currentBucketValue); } } } diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp index 5cf012638dce..d4b57dd68134 100644 --- a/cmds/statsd/src/packages/UidMap.cpp +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -73,6 +73,11 @@ UidMap::UidMap() : mBytesUsed(0) {} UidMap::~UidMap() {} +sp<UidMap> UidMap::getInstance() { + static sp<UidMap> sInstance = new UidMap(); + return sInstance; +} + bool UidMap::hasApp(int uid, const string& packageName) const { lock_guard<mutex> lock(mMutex); @@ -336,6 +341,61 @@ size_t UidMap::getBytesUsed() const { return mBytesUsed; } +void UidMap::writeUidMapSnapshot(int64_t timestamp, bool includeVersionStrings, + bool includeInstaller, const std::set<int32_t>& interestingUids, + std::set<string>* str_set, ProtoOutputStream* proto) { + lock_guard<mutex> lock(mMutex); + + writeUidMapSnapshotLocked(timestamp, includeVersionStrings, includeInstaller, interestingUids, + str_set, proto); +} + +void UidMap::writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings, + bool includeInstaller, + const std::set<int32_t>& interestingUids, + std::set<string>* str_set, ProtoOutputStream* proto) { + proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP, (long long)timestamp); + for (const auto& kv : mMap) { + if (!interestingUids.empty() && + interestingUids.find(kv.first.first) == interestingUids.end()) { + continue; + } + uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_SNAPSHOT_PACKAGE_INFO); + if (str_set != nullptr) { + str_set->insert(kv.first.second); + proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH, + (long long)Hash64(kv.first.second)); + if (includeVersionStrings) { + str_set->insert(kv.second.versionString); + proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH, + (long long)Hash64(kv.second.versionString)); + } + if (includeInstaller) { + str_set->insert(kv.second.installer); + proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH, + (long long)Hash64(kv.second.installer)); + } + } else { + proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second); + if (includeVersionStrings) { + proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING, + kv.second.versionString); + } + if (includeInstaller) { + proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER, + kv.second.installer); + } + } + + proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION, + (long long)kv.second.versionCode); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, kv.first.first); + proto->write(FIELD_TYPE_BOOL | FIELD_ID_SNAPSHOT_PACKAGE_DELETED, kv.second.deleted); + proto->end(token); + } +} + void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set<string>* str_set, bool includeVersionStrings, bool includeInstaller, ProtoOutputStream* proto) { @@ -381,43 +441,9 @@ void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::s // Write snapshot from current uid map state. uint64_t snapshotsToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SNAPSHOTS); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP, (long long)timestamp); - for (const auto& kv : mMap) { - uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_SNAPSHOT_PACKAGE_INFO); - - if (str_set != nullptr) { - str_set->insert(kv.first.second); - proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH, - (long long)Hash64(kv.first.second)); - if (includeVersionStrings) { - str_set->insert(kv.second.versionString); - proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH, - (long long)Hash64(kv.second.versionString)); - } - if (includeInstaller) { - str_set->insert(kv.second.installer); - proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH, - (long long)Hash64(kv.second.installer)); - } - } else { - proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second); - if (includeVersionStrings) { - proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING, - kv.second.versionString); - } - if (includeInstaller) { - proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER, - kv.second.installer); - } - } - - proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION, - (long long)kv.second.versionCode); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, kv.first.first); - proto->write(FIELD_TYPE_BOOL | FIELD_ID_SNAPSHOT_PACKAGE_DELETED, kv.second.deleted); - proto->end(token); - } + writeUidMapSnapshotLocked(timestamp, includeVersionStrings, includeInstaller, + std::set<int32_t>() /*empty uid set means including every uid*/, + str_set, proto); proto->end(snapshotsToken); int64_t prevMin = getMinimumTimestampNs(); diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h index 75ff507ef09a..a7c5fb27375c 100644 --- a/cmds/statsd/src/packages/UidMap.h +++ b/cmds/statsd/src/packages/UidMap.h @@ -91,6 +91,8 @@ public: UidMap(); ~UidMap(); static const std::map<std::string, uint32_t> sAidToUidMapping; + + static sp<UidMap> getInstance(); /* * All three inputs must be the same size, and the jth element in each array refers to the same * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j]. @@ -152,12 +154,25 @@ public: std::set<int32_t> getAppUid(const string& package) const; + // Write current PackageInfoSnapshot to ProtoOutputStream. + // interestingUids: If not empty, only write the package info for these uids. If empty, write + // package info for all uids. + // str_set: if not null, add new string to the set and write str_hash to proto + // if null, write string to proto. + void writeUidMapSnapshot(int64_t timestamp, bool includeVersionStrings, bool includeInstaller, + const std::set<int32_t>& interestingUids, std::set<string>* str_set, + ProtoOutputStream* proto); + private: std::set<string> getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const; string normalizeAppName(const string& appName) const; void getListenerListCopyLocked(std::vector<wp<PackageInfoListener>>* output); + void writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings, + bool includeInstaller, const std::set<int32_t>& interestingUids, + std::set<string>* str_set, ProtoOutputStream* proto); + mutable mutex mMutex; mutable mutex mIsolatedMutex; diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index f7428a50b3da..623d8bc06e38 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -425,6 +425,7 @@ message StatsdStatsReport { optional int64 bucket_dropped = 8; optional int64 min_bucket_boundary_delay_ns = 9; optional int64 max_bucket_boundary_delay_ns = 10; + optional int64 bucket_unknown_condition = 11; } repeated AtomMetricStats atom_metric_stats = 17; @@ -456,3 +457,17 @@ message StatsdStatsReport { } repeated LogLossStats detected_log_loss = 16; } + +message AlertTriggerDetails { + message MetricValue { + optional int64 metric_id = 1; + optional DimensionsValue dimension_in_what = 2; + optional DimensionsValue dimension_in_condition = 3; + optional int64 value = 4; + } + oneof value { + MetricValue trigger_metric = 1; + EventMetricData trigger_event = 2; + } + optional UidMapping.PackageInfoSnapshot package_info = 3; +} diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp index 01043a2143f0..ca645e186614 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -84,6 +84,7 @@ const int FIELD_ID_INVALIDATED_BUCKET = 7; const int FIELD_ID_BUCKET_DROPPED = 8; const int FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS = 9; const int FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS = 10; +const int FIELD_ID_BUCKET_UNKNOWN_CONDITION = 11; namespace { @@ -489,11 +490,11 @@ void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats> protoOutput->end(token); } -void writeAtomMetricStatsToStream(const std::pair<int, StatsdStats::AtomMetricStats> &pair, +void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair, util::ProtoOutputStream *protoOutput) { uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_METRIC_STATS | FIELD_COUNT_REPEATED); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, (int32_t)pair.first); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, (long long)pair.first); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_HARD_DIMENSION_LIMIT_REACHED, (long long)pair.second.hardDimensionLimitReached); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_LATE_LOG_EVENT_SKIPPED, @@ -512,6 +513,8 @@ void writeAtomMetricStatsToStream(const std::pair<int, StatsdStats::AtomMetricSt (long long)pair.second.minBucketBoundaryDelayNs); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS, (long long)pair.second.maxBucketBoundaryDelayNs); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_UNKNOWN_CONDITION, + (long long)pair.second.bucketUnknownCondition); protoOutput->end(token); } diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h index dcea0e653e22..59d4865f8977 100644 --- a/cmds/statsd/src/stats_log_util.h +++ b/cmds/statsd/src/stats_log_util.h @@ -74,7 +74,7 @@ void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats> util::ProtoOutputStream* protoOutput); // Helper function to write AtomMetricStats to ProtoOutputStream -void writeAtomMetricStatsToStream(const std::pair<int, StatsdStats::AtomMetricStats> &pair, +void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair, util::ProtoOutputStream *protoOutput); template<class T> diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp index fe465cefd0f4..165e57c73743 100644 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ b/cmds/statsd/src/storage/StorageManager.cpp @@ -127,7 +127,7 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, return true; } -bool StorageManager::readTrainInfo(TrainInfo& trainInfo) { +bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) { std::lock_guard<std::mutex> lock(sTrainInfoMutex); unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir); diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h index d9eca9f5cdc2..d6df8674e59b 100644 --- a/cmds/statsd/src/storage/StorageManager.h +++ b/cmds/statsd/src/storage/StorageManager.h @@ -49,7 +49,7 @@ public: /** * Reads train info. */ - static bool readTrainInfo(TrainInfo& trainInfo); + static bool readTrainInfo(InstallTrainInfo& trainInfo); /** * Reads the file content to the buffer. diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp index 42cac0cc4122..0ed2d75802da 100644 --- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp +++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp @@ -16,7 +16,10 @@ #define DEBUG false #include "Log.h" +#include "FieldValue.h" #include "IncidentdReporter.h" +#include "packages/UidMap.h" +#include "stats_log_util.h" #include <android/os/IIncidentManager.h> #include <android/os/IncidentReportArgs.h> @@ -43,8 +46,18 @@ const int FIELD_ID_CONFIG_KEY = 3; const int FIELD_ID_CONFIG_KEY_UID = 1; const int FIELD_ID_CONFIG_KEY_ID = 2; +const int FIELD_ID_TRIGGER_DETAILS = 4; +const int FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC = 1; +const int FIELD_ID_METRIC_VALUE_METRIC_ID = 1; +const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT = 2; +const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_CONDITION = 3; +const int FIELD_ID_METRIC_VALUE_VALUE = 4; + +const int FIELD_ID_PACKAGE_INFO = 3; + namespace { -void getProtoData(const int64_t& rule_id, const ConfigKey& configKey, vector<uint8_t>* protoData) { +void getProtoData(const int64_t& rule_id, int64_t metricId, const MetricDimensionKey& dimensionKey, + int64_t metricValue, const ConfigKey& configKey, vector<uint8_t>* protoData) { ProtoOutputStream headerProto; headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_ID, (long long)rule_id); uint64_t token = @@ -53,6 +66,58 @@ void getProtoData(const int64_t& rule_id, const ConfigKey& configKey, vector<uin headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_KEY_ID, (long long)configKey.GetId()); headerProto.end(token); + token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS); + + // MetricValue trigger_metric = 1; + uint64_t metricToken = + headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC); + // message MetricValue { + // optional int64 metric_id = 1; + headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_METRIC_ID, (long long)metricId); + // optional DimensionsValue dimension_in_what = 2; + uint64_t dimToken = + headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT); + writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), nullptr, &headerProto); + headerProto.end(dimToken); + + // optional DimensionsValue dimension_in_condition = 3; + dimToken = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_METRIC_VALUE_DIMENSION_IN_CONDITION); + writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), nullptr, &headerProto); + headerProto.end(dimToken); + + // optional int64 value = 4; + headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_VALUE, (long long)metricValue); + + // } + headerProto.end(metricToken); + + // write relevant uid package info + std::set<int32_t> uids; + + for (const auto& dim : dimensionKey.getDimensionKeyInWhat().getValues()) { + int uid = getUidIfExists(dim); + // any uid <= 2000 are predefined AID_* + if (uid > 2000) { + uids.insert(uid); + } + } + + for (const auto& dim : dimensionKey.getDimensionKeyInCondition().getValues()) { + int uid = getUidIfExists(dim); + if (uid > 2000) { + uids.insert(uid); + } + } + + if (!uids.empty()) { + uint64_t token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_PACKAGE_INFO); + UidMap::getInstance()->writeUidMapSnapshot(getElapsedRealtimeNs(), true, true, uids, + nullptr /*string set*/, &headerProto); + headerProto.end(token); + } + + headerProto.end(token); + protoData->resize(headerProto.size()); size_t pos = 0; auto iter = headerProto.data(); @@ -65,7 +130,8 @@ void getProtoData(const int64_t& rule_id, const ConfigKey& configKey, vector<uin } } // namespace -bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id, +bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int64_t metricId, + const MetricDimensionKey& dimensionKey, int64_t metricValue, const ConfigKey& configKey) { if (config.section_size() == 0) { VLOG("The alert %lld contains zero section in config(%d,%lld)", (unsigned long long)rule_id, @@ -76,7 +142,7 @@ bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_ IncidentReportArgs incidentReport; vector<uint8_t> protoData; - getProtoData(rule_id, configKey, &protoData); + getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey, &protoData); incidentReport.addHeader(protoData); for (int i = 0; i < config.section_size(); i++) { diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.h b/cmds/statsd/src/subscriber/IncidentdReporter.h index 1b83fe23de8f..e78a4d98dcd8 100644 --- a/cmds/statsd/src/subscriber/IncidentdReporter.h +++ b/cmds/statsd/src/subscriber/IncidentdReporter.h @@ -16,6 +16,7 @@ #pragma once +#include "HashableDimensionKey.h" #include "config/ConfigKey.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert, IncidentdDetails @@ -26,7 +27,8 @@ namespace statsd { /** * Calls incidentd to trigger an incident report and put in dropbox for uploading. */ -bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id, +bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int64_t metricId, + const MetricDimensionKey& dimensionKey, int64_t metricValue, const ConfigKey& configKey); } // namespace statsd diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index 5f3aae3ab93a..4579ca6008ef 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -297,7 +297,8 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { // Setup a simple config, no activation StatsdConfig config1; - config1.set_id(12341); + int64_t cfgId1 = 12341; + config1.set_id(cfgId1); config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); *config1.add_atom_matcher() = wakelockAcquireMatcher; @@ -314,14 +315,12 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { countMetric2->set_what(wakelockAcquireMatcher.id()); countMetric2->set_bucket(FIVE_MINUTES); - ConfigKey cfgKey1(uid, 12341); - long timeBase1 = 1; - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); + ConfigKey cfgKey1(uid, cfgId1); // Add another config, with two metrics, one with activation StatsdConfig config2; - config2.set_id(12342); + int64_t cfgId2 = 12342; + config2.set_id(cfgId2); config2.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. *config2.add_atom_matcher() = wakelockAcquireMatcher; @@ -344,11 +343,12 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { metric3ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); metric3ActivationTrigger->set_ttl_seconds(100); - ConfigKey cfgKey2(uid, 12342); + ConfigKey cfgKey2(uid, cfgId2); // Add another config, with two metrics, both with activations StatsdConfig config3; - config3.set_id(12342); + int64_t cfgId3 = 12343; + config3.set_id(cfgId3); config3.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. *config3.add_atom_matcher() = wakelockAcquireMatcher; @@ -376,14 +376,37 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { metric6ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); metric6ActivationTrigger->set_ttl_seconds(200); - ConfigKey cfgKey3(uid, 12343); + ConfigKey cfgKey3(uid, cfgId3); - processor->OnConfigUpdated(2, cfgKey2, config2); - processor->OnConfigUpdated(3, cfgKey3, config3); + sp<UidMap> m = new UidMap(); + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> subscriberAlarmMonitor; + vector<int64_t> activeConfigsBroadcast; - EXPECT_EQ(3, processor->mMetricsManagers.size()); - auto it = processor->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor->mMetricsManagers.end()); + long timeBase1 = 1; + int broadcastCount = 0; + StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, + timeBase1, [](const ConfigKey& key) { return true; }, + [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, + const vector<int64_t>& activeConfigs) { + broadcastCount++; + EXPECT_EQ(broadcastUid, uid); + activeConfigsBroadcast.clear(); + activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), + activeConfigs.begin(), activeConfigs.end()); + return true; + }); + + processor.OnConfigUpdated(1, cfgKey1, config1); + processor.OnConfigUpdated(2, cfgKey2, config2); + processor.OnConfigUpdated(3, cfgKey3, config3); + + EXPECT_EQ(3, processor.mMetricsManagers.size()); + + // Expect the first config and both metrics in it to be active. + auto it = processor.mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor.mMetricsManagers.end()); auto& metricsManager1 = it->second; EXPECT_TRUE(metricsManager1->isActive()); @@ -407,8 +430,9 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto& metricProducer2 = *metricIt; EXPECT_TRUE(metricProducer2->isActive()); - it = processor->mMetricsManagers.find(cfgKey2); - EXPECT_TRUE(it != processor->mMetricsManagers.end()); + // Expect config 2 to be active. Metric 3 shouldn't be active, metric 4 should be active. + it = processor.mMetricsManagers.find(cfgKey2); + EXPECT_TRUE(it != processor.mMetricsManagers.end()); auto& metricsManager2 = it->second; EXPECT_TRUE(metricsManager2->isActive()); @@ -432,8 +456,9 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto& metricProducer4 = *metricIt; EXPECT_TRUE(metricProducer4->isActive()); - it = processor->mMetricsManagers.find(cfgKey3); - EXPECT_TRUE(it != processor->mMetricsManagers.end()); + // Expect the third config and both metrics in it to be inactive. + it = processor.mMetricsManagers.find(cfgKey3); + EXPECT_TRUE(it != processor.mMetricsManagers.end()); auto& metricsManager3 = it->second; EXPECT_FALSE(metricsManager3->isActive()); @@ -457,10 +482,30 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto& metricProducer6 = *metricIt; EXPECT_FALSE(metricProducer6->isActive()); + // No broadcast for active configs should have happened yet. + EXPECT_EQ(broadcastCount, 0); + + // Activate all 3 metrics that were not active. std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); + // Assert that all 3 configs are active. + EXPECT_TRUE(metricsManager1->isActive()); + EXPECT_TRUE(metricsManager2->isActive()); + EXPECT_TRUE(metricsManager3->isActive()); + + // A broadcast should have happened, and all 3 configs should be active in the broadcast. + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(activeConfigsBroadcast.size(), 3); + EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId1) + != activeConfigsBroadcast.end()); + EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId2) + != activeConfigsBroadcast.end()); + EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId3) + != activeConfigsBroadcast.end()); + + // When we shut down, metrics 3 & 5 have 100ns remaining, metric 6 has 100s + 100ns. int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; EXPECT_TRUE(metricProducer3->isActive()); int64_t ttl3 = metricProducer3->getRemainingTtlNs(shutDownTime); @@ -472,8 +517,9 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { int64_t ttl6 = metricProducer6->getRemainingTtlNs(shutDownTime); EXPECT_EQ(100 + 100 * NS_PER_SEC, ttl6); - processor->WriteMetricsActivationToDisk(timeBase1 + 100 * NS_PER_SEC); + processor.WriteMetricsActivationToDisk(shutDownTime); + // Create a second StatsLogProcessor and push the same 3 configs. long timeBase2 = 1000; sp<StatsLogProcessor> processor2 = CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); @@ -481,6 +527,8 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { processor2->OnConfigUpdated(timeBase2, cfgKey3, config3); EXPECT_EQ(3, processor2->mMetricsManagers.size()); + + // First config and both metrics are active. it = processor2->mMetricsManagers.find(cfgKey1); EXPECT_TRUE(it != processor2->mMetricsManagers.end()); auto& metricsManager1001 = it->second; @@ -506,6 +554,7 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto& metricProducer1002 = *metricIt; EXPECT_TRUE(metricProducer1002->isActive()); + // Second config is active. Metric 3 is inactive, metric 4 is active. it = processor2->mMetricsManagers.find(cfgKey2); EXPECT_TRUE(it != processor2->mMetricsManagers.end()); auto& metricsManager1002 = it->second; @@ -531,6 +580,7 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto& metricProducer1004 = *metricIt; EXPECT_TRUE(metricProducer1004->isActive()); + // Config 3 is inactive. both metrics are inactive. it = processor2->mMetricsManagers.find(cfgKey3); EXPECT_TRUE(it != processor2->mMetricsManagers.end()); auto& metricsManager1003 = it->second; @@ -557,6 +607,7 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto& metricProducer1006 = *metricIt; EXPECT_FALSE(metricProducer1006->isActive()); + // Assert that all 3 metrics with activation are inactive and that the ttls were properly set. EXPECT_FALSE(metricProducer1003->isActive()); const auto& activation1003 = metricProducer1003->mEventActivationMap.begin()->second; EXPECT_EQ(100 * NS_PER_SEC, activation1003.ttl_ns); @@ -572,12 +623,16 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { processor2->LoadMetricsActivationFromDisk(); + // After loading activations from disk, assert that all 3 metrics are active. EXPECT_TRUE(metricProducer1003->isActive()); EXPECT_EQ(timeBase2 + ttl3 - activation1003.ttl_ns, activation1003.activation_ns); EXPECT_TRUE(metricProducer1005->isActive()); EXPECT_EQ(timeBase2 + ttl5 - activation1005.ttl_ns, activation1005.activation_ns); EXPECT_TRUE(metricProducer1006->isActive()); EXPECT_EQ(timeBase2 + ttl6 - activation1006.ttl_ns, activation1003.activation_ns); + + // Make sure no more broadcasts have happened. + EXPECT_EQ(broadcastCount, 1); } TEST(StatsLogProcessorTest, TestActivationOnBoot) { diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp index 960fbdab1c15..c10703c36b98 100644 --- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp +++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp @@ -90,7 +90,8 @@ void detectAndDeclareAnomalies(AnomalyTracker& tracker, const std::shared_ptr<DimToValMap>& bucket, const int64_t& eventTimestamp) { for (const auto& kv : *bucket) { - tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, kv.first, kv.second); + tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, 0 /*metric_id*/, kv.first, + kv.second); } } diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp index 29e86f3f9456..85d8a5613a8b 100644 --- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp @@ -66,17 +66,42 @@ TEST(MetricActivationE2eTest, TestCountMetric) { auto config = CreateStatsdConfig(); int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; - - ConfigKey cfgKey; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - EXPECT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - sp<MetricProducer> metricProducer = - processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + sp<UidMap> m = new UidMap(); + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> subscriberAlarmMonitor; + vector<int64_t> activeConfigsBroadcast; + + long timeBase1 = 1; + int broadcastCount = 0; + StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, + bucketStartTimeNs, [](const ConfigKey& key) { return true; }, + [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, + const vector<int64_t>& activeConfigs) { + broadcastCount++; + EXPECT_EQ(broadcastUid, uid); + activeConfigsBroadcast.clear(); + activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), + activeConfigs.begin(), activeConfigs.end()); + return true; + }); + + processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); + + EXPECT_EQ(processor.mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; auto& eventActivationMap = metricProducer->mEventActivationMap; + EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); // Two activations: one is triggered by battery saver mode (tracker index 0), the other is // triggered by screen on event (tracker index 2). @@ -93,13 +118,19 @@ TEST(MetricActivationE2eTest, TestCountMetric) { std::unique_ptr<LogEvent> event; event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); + 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()); + EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive); EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC); @@ -109,12 +140,13 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // First processed event. event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); // Activated by screen on event. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 20); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); + EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive); EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); @@ -126,7 +158,8 @@ 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()); + EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive); EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); @@ -134,15 +167,21 @@ TEST(MetricActivationE2eTest, TestCountMetric) { EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive); EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + 20); EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC); + // No new broadcast since the config should still be active. + EXPECT_EQ(broadcastCount, 1); // 3rd processed event. event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); // All activations expired. event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); + EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); + // New broadcast since the config is no longer active. + EXPECT_EQ(broadcastCount, 2); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive); EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC); @@ -153,8 +192,12 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // Re-activate. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); + EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive); EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC); @@ -163,11 +206,11 @@ TEST(MetricActivationE2eTest, TestCountMetric) { EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC); event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); - processor->OnLogEvent(event.get()); + processor.OnLogEvent(event.get()); ConfigMetricsReportList reports; vector<uint8_t> buffer; - processor->onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, + processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, ADB_DUMP, &buffer); EXPECT_TRUE(buffer.size() > 0); EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index b54096441d3f..b294cad1802c 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -117,6 +117,7 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { DurationMetricProducer durationProducer( kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); + durationProducer.mCondition = ConditionState::kFalse; EXPECT_FALSE(durationProducer.mCondition); EXPECT_FALSE(durationProducer.isConditionSliced()); @@ -140,6 +141,51 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { EXPECT_EQ(1LL, buckets2[0].mDuration); } +TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) { + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_SUM); + + int tagId = 1; + LogEvent event1(tagId, bucketStartTimeNs + 1); + event1.init(); + LogEvent event2(tagId, bucketStartTimeNs + 2); + event2.init(); + LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1); + event3.init(); + LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3); + event4.init(); + + FieldMatcher dimensions; + DurationMetricProducer durationProducer( + kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); + + EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition); + EXPECT_FALSE(durationProducer.isConditionSliced()); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event1); + durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event3); + durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2); + durationProducer.onMatchedLogEvent(2 /* stop index*/, event4); + durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); + EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); + const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(1UL, buckets2.size()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs); + EXPECT_EQ(1LL, buckets2[0].mDuration); +} + TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) { /** * The duration starts from the first bucket, through the two partial buckets (10-70sec), diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index 572b1991f426..7e7ffeda8053 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -2176,7 +2176,7 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = true; + valueProducer.mCondition = ConditionState::kTrue; vector<shared_ptr<LogEvent>> allData; valueProducer.onDataPulled(allData, /** succeed */ false, bucketStartTimeNs); @@ -2226,7 +2226,7 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = false; + valueProducer.mCondition = ConditionState::kFalse; // Max delay is set to 0 so pull will exceed max delay. valueProducer.onConditionChanged(true, bucketStartTimeNs + 1); @@ -2257,7 +2257,7 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) { eventMatcherWizard, tagId, bucket2StartTimeNs, bucket2StartTimeNs, pullerManager); - valueProducer.mCondition = false; + valueProducer.mCondition = ConditionState::kFalse; // Event should be skipped since it is from previous bucket. // Pull should not be called. @@ -2299,7 +2299,7 @@ TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = false; + valueProducer.mCondition = ConditionState::kFalse; valueProducer.mHasGlobalBase = false; valueProducer.onConditionChanged(true, bucketStartTimeNs + 1); @@ -2351,7 +2351,7 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = true; + valueProducer.mCondition = ConditionState::kTrue; // Bucket start. vector<shared_ptr<LogEvent>> allData; @@ -2429,7 +2429,7 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenGuardRailHit) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = false; + valueProducer.mCondition = ConditionState::kFalse; valueProducer.onConditionChanged(true, bucket2StartTimeNs + 2); EXPECT_EQ(true, valueProducer.mCurrentBucketIsInvalid); EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size()); @@ -2481,7 +2481,7 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = true; + valueProducer.mCondition = ConditionState::kTrue; // Bucket start. vector<shared_ptr<LogEvent>> allData; @@ -2564,7 +2564,7 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed) { eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.mCondition = true; + valueProducer.mCondition = ConditionState::kTrue; // Bucket start. vector<shared_ptr<LogEvent>> allData; diff --git a/cmds/statsd/tests/storage/StorageManager_test.cpp b/cmds/statsd/tests/storage/StorageManager_test.cpp index ce957a3ec771..f66de0518f80 100644 --- a/cmds/statsd/tests/storage/StorageManager_test.cpp +++ b/cmds/statsd/tests/storage/StorageManager_test.cpp @@ -30,14 +30,14 @@ using std::vector; using testing::Contains; TEST(StorageManagerTest, TrainInfoReadWriteTest) { - TrainInfo trainInfo; + InstallTrainInfo trainInfo; trainInfo.trainVersionCode = 12345; const char* expIds = "test_ids"; trainInfo.experimentIds.assign(expIds, expIds + strlen(expIds)); StorageManager::writeTrainInfo(trainInfo.trainVersionCode, trainInfo.experimentIds); - TrainInfo result; + InstallTrainInfo result; StorageManager::readTrainInfo(result); EXPECT_EQ(trainInfo.trainVersionCode, result.trainVersionCode); EXPECT_EQ(trainInfo.experimentIds.size(), result.experimentIds.size()); diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt index 6fe6ee0b5193..dc2ed4cc0d69 100644 --- a/config/hiddenapi-greylist.txt +++ b/config/hiddenapi-greylist.txt @@ -542,8 +542,6 @@ Landroid/net/IConnectivityManager;->getTetherableWifiRegexs()[Ljava/lang/String; Landroid/net/IConnectivityManager;->getTetheredIfaces()[Ljava/lang/String; Landroid/net/IConnectivityManager;->getTetheringErroredIfaces()[Ljava/lang/String; Landroid/net/IConnectivityManager;->startLegacyVpn(Lcom/android/internal/net/VpnProfile;)V -Landroid/net/INetd$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetd; -Landroid/net/INetd;->interfaceAddAddress(Ljava/lang/String;Ljava/lang/String;I)V Landroid/net/INetworkManagementEventObserver$Stub;-><init>()V Landroid/net/INetworkPolicyListener$Stub;-><init>()V Landroid/net/INetworkPolicyManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkPolicyManager; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 5f778da44a1c..e55c96443198 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -71,6 +71,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Parcelable; import android.os.PersistableBundle; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager.ServiceNotFoundException; import android.os.StrictMode; @@ -2297,7 +2298,7 @@ public class Activity extends ContextThemeWrapper public final void requestShowKeyboardShortcuts() { Intent intent = new Intent(Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS); intent.setPackage(KEYBOARD_SHORTCUTS_RECEIVER_PKG_NAME); - sendBroadcastAsUser(intent, UserHandle.SYSTEM); + sendBroadcastAsUser(intent, Process.myUserHandle()); } /** @@ -2306,7 +2307,7 @@ public class Activity extends ContextThemeWrapper public final void dismissKeyboardShortcutsHelper() { Intent intent = new Intent(Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS); intent.setPackage(KEYBOARD_SHORTCUTS_RECEIVER_PKG_NAME); - sendBroadcastAsUser(intent, UserHandle.SYSTEM); + sendBroadcastAsUser(intent, Process.myUserHandle()); } @Override diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index ca3c72627e3b..5d4f988c3630 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -55,6 +55,7 @@ import android.os.Bundle; import android.os.Debug; import android.os.Handler; import android.os.IBinder; +import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; import android.os.Process; @@ -68,6 +69,7 @@ import android.util.DisplayMetrics; import android.util.Singleton; import android.util.Size; +import com.android.internal.app.LocalePicker; import com.android.internal.app.procstats.ProcessStats; import com.android.internal.os.RoSystemProperties; import com.android.internal.os.TransferPipe; @@ -84,7 +86,9 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.Locale; /** * <p> @@ -3451,6 +3455,35 @@ public class ActivityManager { } /** + * Sets the current locales of the device. Calling app must have the permission + * {@code android.permission.CHANGE_CONFIGURATION} and + * {@code android.permission.WRITE_SETTINGS}. + * + * @hide + */ + @SystemApi + public void setDeviceLocales(@NonNull LocaleList locales) { + LocalePicker.updateLocales(locales); + } + + /** + * Returns a list of supported locales by this system. It includes all locales that are + * selectable by the user, potentially including locales that the framework does not have + * translated resources for. To get locales that the framework has translated resources for, use + * {@code Resources.getSystem().getAssets().getLocales()} instead. + * + * @hide + */ + @SystemApi + public @NonNull Collection<Locale> getSupportedLocales() { + ArrayList<Locale> locales = new ArrayList<>(); + for (String localeTag : LocalePicker.getSupportedLocales(mContext)) { + locales.add(Locale.forLanguageTag(localeTag)); + } + return locales; + } + + /** * Get the device configuration attributes. */ public ConfigurationInfo getDeviceConfigurationInfo() { diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 64b94a946489..13eec580ec14 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -848,6 +848,7 @@ public class AppOpsManager { /** @hide Has a legacy (non-isolated) view of storage. */ public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage"; /** @hide Interact with accessibility. */ + @SystemApi public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility"; // Warning: If an permission is added here it also has to be added to @@ -4290,12 +4291,35 @@ public class AppOpsManager { /** * Like {@link #noteProxyOp(String, String)} but instead * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}. + * + * <p>This API requires the package with the {@code proxiedPackageName} to belongs to + * {@link Binder#getCallingUid()}. */ public int noteProxyOpNoThrow(String op, String proxiedPackageName) { return noteProxyOpNoThrow(strOpToOp(op), proxiedPackageName); } /** + * Like {@link #noteProxyOp(String, String)} but instead + * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}. + * + * <p>This API requires package with the {@code proxiedPackageName} to belong to + * {@code proxiedUid}. + * + * @param op The op to note + * @param proxiedPackageName The package to note the op for or {@code null} if the op should be + * noted for the "android" package + * @param proxiedUid The uid the package belongs to + * + * @hide + */ + @SystemApi + public int noteProxyOpNoThrow(@NonNull String op, @Nullable String proxiedPackageName, + int proxiedUid) { + return noteProxyOpNoThrow(strOpToOp(op), proxiedPackageName, proxiedUid); + } + + /** * Report that an application has started executing a long-running operation. Note that you * must pass in both the uid and name of the application to be checked; this function will * verify that these two match, and if not, return {@link #MODE_IGNORED}. If this call @@ -4494,17 +4518,30 @@ public class AppOpsManager { * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}. * @hide */ - public int noteProxyOpNoThrow(int op, String proxiedPackageName) { + public int noteProxyOpNoThrow(int op, String proxiedPackageName, int proxiedUid) { logOperationIfNeeded(op, mContext.getOpPackageName(), proxiedPackageName); try { return mService.noteProxyOperation(op, Process.myUid(), mContext.getOpPackageName(), - Binder.getCallingUid(), proxiedPackageName); + proxiedUid, proxiedPackageName); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** + * Like {@link #noteProxyOp(int, String)} but instead + * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}. + * + * <p>This API requires the package with {@code proxiedPackageName} to belongs to + * {@link Binder#getCallingUid()}. + * + * @hide + */ + public int noteProxyOpNoThrow(int op, String proxiedPackageName) { + return noteProxyOpNoThrow(op, proxiedPackageName, Binder.getCallingUid()); + } + + /** * Like {@link #noteOp} but instead of throwing a {@link SecurityException} it * returns {@link #MODE_ERRORED}. * @hide diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 1a728c12e138..6908ca27480c 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2101,7 +2101,8 @@ class ContextImpl extends Context { } private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName, - int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) { + int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo, + String[] overlayDirs) { final String[] splitResDirs; final ClassLoader classLoader; try { @@ -2113,7 +2114,7 @@ class ContextImpl extends Context { return ResourcesManager.getInstance().getResources(activityToken, pi.getResDir(), splitResDirs, - pi.getOverlayDirs(), + overlayDirs, pi.getApplicationInfo().sharedLibraryFiles, displayId, overrideConfig, @@ -2131,9 +2132,11 @@ class ContextImpl extends Context { new UserHandle(UserHandle.getUserId(application.uid)), flags, null, null); final int displayId = getDisplayId(); - + // overlayDirs is retrieved directly from ApplicationInfo since ActivityThread may have + // a LoadedApk containing Resources with stale overlays for a remote application. + final String[] overlayDirs = application.resourceDirs; c.setResources(createResources(mActivityToken, pi, null, displayId, null, - getDisplayAdjustments(displayId).getCompatibilityInfo())); + getDisplayAdjustments(displayId).getCompatibilityInfo(), overlayDirs)); if (c.mResources != null) { return c; } @@ -2168,7 +2171,7 @@ class ContextImpl extends Context { final int displayId = getDisplayId(); c.setResources(createResources(mActivityToken, pi, null, displayId, null, - getDisplayAdjustments(displayId).getCompatibilityInfo())); + getDisplayAdjustments(displayId).getCompatibilityInfo(), pi.getOverlayDirs())); if (c.mResources != null) { return c; } @@ -2218,7 +2221,8 @@ class ContextImpl extends Context { final int displayId = getDisplayId(); context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId, - overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo())); + overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(), + mPackageInfo.getOverlayDirs())); return context; } @@ -2233,7 +2237,8 @@ class ContextImpl extends Context { final int displayId = display.getDisplayId(); context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId, - null, getDisplayAdjustments(displayId).getCompatibilityInfo())); + null, getDisplayAdjustments(displayId).getCompatibilityInfo(), + mPackageInfo.getOverlayDirs())); context.mDisplay = display; return context; } @@ -2416,7 +2421,7 @@ class ContextImpl extends Context { ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null, null, null, 0, null, null); context.setResources(createResources(null, packageInfo, null, displayId, null, - packageInfo.getCompatibilityInfo())); + packageInfo.getCompatibilityInfo(), packageInfo.getOverlayDirs())); context.updateDisplay(displayId); return context; } diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index 87bf5ed7db65..f116e133e338 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -27,6 +27,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; +import android.content.pm.ServiceInfo.ForegroundServiceType; import android.content.res.Configuration; import android.os.Build; import android.os.IBinder; @@ -735,7 +736,7 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * @see {@link android.content.pm.ServiceInfo} for the set of FOREGROUND_SERVICE_TYPE flags. */ public final void startForeground(int id, @NonNull Notification notification, - int foregroundServiceType) { + @ForegroundServiceType int foregroundServiceType) { try { mActivityManager.setServiceForeground( new ComponentName(this, mClassName), mToken, id, diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 1878d8407738..077652cacc2d 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -42,12 +42,10 @@ import java.lang.annotation.RetentionPolicy; public class StatusBarManager { /** @hide */ - @SystemApi public static final int DISABLE_EXPAND = View.STATUS_BAR_DISABLE_EXPAND; /** @hide */ public static final int DISABLE_NOTIFICATION_ICONS = View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS; /** @hide */ - @SystemApi public static final int DISABLE_NOTIFICATION_ALERTS = View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS; @@ -59,17 +57,14 @@ public class StatusBarManager { /** @hide */ public static final int DISABLE_SYSTEM_INFO = View.STATUS_BAR_DISABLE_SYSTEM_INFO; /** @hide */ - @SystemApi public static final int DISABLE_HOME = View.STATUS_BAR_DISABLE_HOME; /** @hide */ - @SystemApi public static final int DISABLE_RECENT = View.STATUS_BAR_DISABLE_RECENT; /** @hide */ public static final int DISABLE_BACK = View.STATUS_BAR_DISABLE_BACK; /** @hide */ public static final int DISABLE_CLOCK = View.STATUS_BAR_DISABLE_CLOCK; /** @hide */ - @SystemApi public static final int DISABLE_SEARCH = View.STATUS_BAR_DISABLE_SEARCH; /** @hide */ @@ -78,7 +73,6 @@ public class StatusBarManager { View.STATUS_BAR_DISABLE_HOME | View.STATUS_BAR_DISABLE_RECENT; /** @hide */ - @SystemApi public static final int DISABLE_NONE = 0x00000000; /** @hide */ @@ -122,7 +116,6 @@ public class StatusBarManager { public static final int DISABLE2_ROTATE_SUGGESTIONS = 1 << 4; /** @hide */ - @SystemApi public static final int DISABLE2_NONE = 0x00000000; /** @hide */ @@ -387,14 +380,14 @@ public class StatusBarManager { } /** - * Get the currently applied StatusBar disable flags + * Get this app's currently requested disabled components * - * @return a pair of Integers in the form of (disable, disable2) + * @return a new DisableInfo * * @hide */ @SystemApi - public Pair<Integer, Integer> getDisableFlags() { + public DisableInfo getDisableInfo() { try { final int userId = Binder.getCallingUserHandle().getIdentifier(); final IStatusBarService svc = getService(); @@ -403,7 +396,7 @@ public class StatusBarManager { flags = svc.getDisableFlags(mToken, userId); } - return new Pair<Integer, Integer>(flags[0], flags[1]); + return new DisableInfo(flags[0], flags[1]); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } @@ -416,4 +409,180 @@ public class StatusBarManager { if (state == WINDOW_STATE_SHOWING) return "WINDOW_STATE_SHOWING"; return "WINDOW_STATE_UNKNOWN"; } + + /** + * DisableInfo describes this app's requested state of the StatusBar with regards to which + * components are enabled/disabled + * + * @hide + */ + @SystemApi + public static final class DisableInfo { + + private boolean mStatusBarExpansion; + private boolean mNavigateHome; + private boolean mNotificationPeeking; + private boolean mRecents; + private boolean mSearch; + + /** @hide */ + public DisableInfo(int flags1, int flags2) { + mStatusBarExpansion = (flags1 & DISABLE_EXPAND) != 0; + mNavigateHome = (flags1 & DISABLE_HOME) != 0; + mNotificationPeeking = (flags1 & DISABLE_NOTIFICATION_ALERTS) != 0; + mRecents = (flags1 & DISABLE_RECENT) != 0; + mSearch = (flags1 & DISABLE_SEARCH) != 0; + } + + /** @hide */ + public DisableInfo() {} + + /** + * @return {@code true} if expanding the notification shade is disabled + * + * @hide + */ + @SystemApi + public boolean isStatusBarExpansionDisabled() { + return mStatusBarExpansion; + } + + /** * @hide */ + public void setStatusBarExpansionDisabled(boolean disabled) { + mStatusBarExpansion = disabled; + } + + /** + * @return {@code true} if navigation home is disabled + * + * @hide + */ + @SystemApi + public boolean isNavigateToHomeDisabled() { + return mNavigateHome; + } + + /** * @hide */ + public void setNagivationHomeDisabled(boolean disabled) { + mNavigateHome = disabled; + } + + /** + * @return {@code true} if notification peeking (heads-up notification) is disabled + * + * @hide + */ + @SystemApi + public boolean isNotificationPeekingDisabled() { + return mNotificationPeeking; + } + + /** @hide */ + public void setNotificationPeekingDisabled(boolean disabled) { + mNotificationPeeking = disabled; + } + + /** + * @return {@code true} if mRecents/overview is disabled + * + * @hide + */ + @SystemApi + public boolean isRecentsDisabled() { + return mRecents; + } + + /** @hide */ + public void setRecentsDisabled(boolean disabled) { + mRecents = disabled; + } + + /** + * @return {@code true} if mSearch is disabled + * + * @hide + */ + @SystemApi + public boolean isSearchDisabled() { + return mSearch; + } + + /** @hide */ + public void setSearchDisabled(boolean disabled) { + mSearch = disabled; + } + + /** + * @return {@code true} if no components are disabled (default state) + * + * @hide + */ + @SystemApi + public boolean areNoComponentsDisabled() { + return !mStatusBarExpansion && !mNavigateHome && !mNotificationPeeking && !mRecents + && !mSearch; + } + + /** @hide */ + public void setEnableAll() { + mStatusBarExpansion = false; + mNavigateHome = false; + mNotificationPeeking = false; + mRecents = false; + mSearch = false; + } + + /** + * @return {@code true} if all status bar components are disabled + * + * @hide + */ + public boolean areAllComponentsDisabled() { + return mStatusBarExpansion && mNavigateHome && mNotificationPeeking + && mRecents && mSearch; + } + + /** @hide */ + public void setDisableAll() { + mStatusBarExpansion = true; + mNavigateHome = true; + mNotificationPeeking = true; + mRecents = true; + mSearch = true; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("DisableInfo: "); + sb.append(" mStatusBarExpansion=").append(mStatusBarExpansion ? "disabled" : "enabled"); + sb.append(" mNavigateHome=").append(mNavigateHome ? "disabled" : "enabled"); + sb.append(" mNotificationPeeking=") + .append(mNotificationPeeking ? "disabled" : "enabled"); + sb.append(" mRecents=").append(mRecents ? "disabled" : "enabled"); + sb.append(" mSearch=").append(mSearch ? "disabled" : "enabled"); + + return sb.toString(); + + } + + /** + * Convert a DisableInfo to equivalent flags + * @return a pair of equivalent disable flags + * + * @hide + */ + public Pair<Integer, Integer> toFlags() { + int disable1 = DISABLE_NONE; + int disable2 = DISABLE2_NONE; + + if (mStatusBarExpansion) disable1 |= DISABLE_EXPAND; + if (mNavigateHome) disable1 |= DISABLE_HOME; + if (mNotificationPeeking) disable1 |= DISABLE_NOTIFICATION_ALERTS; + if (mRecents) disable1 |= DISABLE_RECENT; + if (mSearch) disable1 |= DISABLE_SEARCH; + + return new Pair<Integer, Integer>(disable1, disable2); + } + } } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 807b7f2ac349..c12a92f44fa2 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -101,13 +101,11 @@ import android.net.IConnectivityManager; import android.net.IEthernetManager; import android.net.IIpMemoryStore; import android.net.IIpSecService; -import android.net.INetd; import android.net.INetworkPolicyManager; import android.net.IpMemoryStore; import android.net.IpSecManager; import android.net.NetworkPolicyManager; import android.net.NetworkScoreManager; -import android.net.NetworkStack; import android.net.NetworkWatchlistManager; import android.net.lowpan.ILowpanManager; import android.net.lowpan.LowpanManager; @@ -330,21 +328,13 @@ final class SystemServiceRegistry { return new ConnectivityManager(context, service); }}); - registerService(Context.NETD_SERVICE, INetd.class, new StaticServiceFetcher<INetd>() { + registerService(Context.NETD_SERVICE, IBinder.class, new StaticServiceFetcher<IBinder>() { @Override - public INetd createService() throws ServiceNotFoundException { - return INetd.Stub.asInterface( - ServiceManager.getServiceOrThrow(Context.NETD_SERVICE)); + public IBinder createService() throws ServiceNotFoundException { + return ServiceManager.getServiceOrThrow(Context.NETD_SERVICE); } }); - registerService(Context.NETWORK_STACK_SERVICE, NetworkStack.class, - new StaticServiceFetcher<NetworkStack>() { - @Override - public NetworkStack createService() { - return new NetworkStack(); - }}); - registerService(Context.IP_MEMORY_STORE_SERVICE, IpMemoryStore.class, new CachedServiceFetcher<IpMemoryStore>() { @Override @@ -1139,7 +1129,11 @@ final class SystemServiceRegistry { IBinder b = ServiceManager .getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE); IContentCaptureManager service = IContentCaptureManager.Stub.asInterface(b); - return new ContentCaptureManager(outerContext, service); + if (service != null) { + // When feature is disabled, we return a null manager to apps so the + // performance impact is practically zero + return new ContentCaptureManager(outerContext, service); + } } return null; }}); diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index a554882123f1..46316e1a254b 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -229,7 +229,11 @@ public class UiModeManager { * <strong>Note:</strong> On API 22 and below, changes to the night mode * are only effective when the {@link Configuration#UI_MODE_TYPE_CAR car} * or {@link Configuration#UI_MODE_TYPE_DESK desk} mode is enabled on a - * device. Starting in API 23, changes to night mode are always effective. + * device. On API 23 through API 28, changes to night mode are always effective. + * <p> + * Starting in API 29, when the device is in car mode and this method is called, night mode + * will change, but the new setting is not persisted and the previously persisted setting + * will be restored when the device exits car mode. * <p> * Changes to night mode take effect globally and will result in a configuration change * (and potentially an Activity lifecycle event) being applied to all running apps. diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java index 94a2a3eaae7f..97efa01e982b 100644 --- a/core/java/android/app/usage/UsageStats.java +++ b/core/java/android/app/usage/UsageStats.java @@ -465,8 +465,6 @@ public final class UsageStats implements Parcelable { mActivities.put(instanceId, eventType); break; case ACTIVITY_STOPPED: - mActivities.put(instanceId, eventType); - break; case ACTIVITY_DESTROYED: // remove activity from the map. mActivities.delete(instanceId); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 957a48494892..25bfba26256f 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -49,7 +49,6 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; -import android.net.NetworkStack; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -3647,11 +3646,10 @@ public abstract class Context { public static final String NETD_SERVICE = "netd"; /** - * Use with {@link #getSystemService(String)} to retrieve a - * {@link NetworkStack} for communicating with the network stack + * Use with {@link android.os.ServiceManager.getService()} to retrieve a + * {@link NetworkStackClient} IBinder for communicating with the network stack * @hide - * @see #getSystemService(String) - * @see NetworkStack + * @see NetworkStackClient */ public static final String NETWORK_STACK_SERVICE = "network_stack"; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index d781a96420c7..a5e7e95f0874 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1790,6 +1790,35 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.MANAGE_APP_PERMISSIONS"; /** + * Activity action: Launch UI to manage a specific permissions of an app. + * <p> + * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose permission + * will be managed by the launched UI. + * </p> + * <p> + * Input: {@link #EXTRA_PERMISSION_NAME} specifies the (individual) permission + * that should be managed by the launched UI. + * </p> + * <p> + * <li> {@link #EXTRA_USER} specifies the UserHandle of the user that owns the app. + * </p> + * <p> + * Output: Nothing. + * </p> + * + * @see #EXTRA_PACKAGE_NAME + * @see #EXTRA_PERMISSION_NAME + * @see #EXTRA_USER + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_MANAGE_APP_PERMISSION = + "android.intent.action.MANAGE_APP_PERMISSION"; + + /** * Activity action: Launch UI to manage permissions. * <p> * Input: Nothing. @@ -2080,6 +2109,22 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_REVIEW_APP_PERMISSION_USAGE = "android.intent.action.REVIEW_APP_PERMISSION_USAGE"; + /** + * Activity action: Launch UI to review running accessibility services. + * <p> + * Input: Nothing. + * </p> + * <p> + * Output: Nothing. + * </p> + * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REVIEW_ACCESSIBILITY_SERVICES = + "android.intent.action.REVIEW_ACCESSIBILITY_SERVICES"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent broadcast actions (see action variable). diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java index 9f5c877e7081..6fe6e991fb1e 100644 --- a/core/java/android/content/PermissionChecker.java +++ b/core/java/android/content/PermissionChecker.java @@ -108,8 +108,7 @@ public final class PermissionChecker { packageName = packageNames[0]; } - if (appOpsManager.noteProxyOpNoThrow(op, packageName) - != AppOpsManager.MODE_ALLOWED) { + if (appOpsManager.noteProxyOpNoThrow(op, packageName, uid) != AppOpsManager.MODE_ALLOWED) { return PERMISSION_DENIED_APP_OP; } @@ -120,6 +119,9 @@ public final class PermissionChecker { * Checks whether your app has a given permission and whether the app op * that corresponds to this permission is allowed. * + * <p>This API assumes the the {@link Binder#getCallingUid()} is the same as + * {@link Process#myUid()}. + * * @param context Context for accessing resources. * @param permission The permission to check. * @return The permission check result which is either {@link #PERMISSION_GRANTED} diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 36ffb0ea94d0..14e77258c65e 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -295,8 +295,6 @@ interface IPackageManager { void restoreDefaultApps(in byte[] backup, int userId); byte[] getIntentFilterVerificationBackup(int userId); void restoreIntentFilterVerification(in byte[] backup, int userId); - byte[] getPermissionGrantBackup(int userId); - void restorePermissionGrants(in byte[] backup, int userId); /** * Report the set of 'Home' activity candidates, plus (if any) which of them diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 3ea78df6ae39..00419212544a 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2943,6 +2943,7 @@ public abstract class PackageManager { * @hide */ @SystemApi + @TestApi public static final int FLAG_PERMISSION_USER_SET = 1 << 0; /** @@ -2953,6 +2954,7 @@ public abstract class PackageManager { * @hide */ @SystemApi + @TestApi public static final int FLAG_PERMISSION_USER_FIXED = 1 << 1; /** @@ -2976,6 +2978,7 @@ public abstract class PackageManager { * @hide */ @SystemApi + @TestApi public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 1 << 3; /** @@ -3005,6 +3008,7 @@ public abstract class PackageManager { * @hide */ @SystemApi + @TestApi public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 1 << 6; /** @@ -3014,6 +3018,7 @@ public abstract class PackageManager { * * @hide */ + @TestApi public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 1 << 7; /** @@ -3795,6 +3800,7 @@ public abstract class PackageManager { * @hide */ @SystemApi + @TestApi @RequiresPermission(anyOf = { android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS @@ -3815,6 +3821,7 @@ public abstract class PackageManager { * @hide */ @SystemApi + @TestApi @RequiresPermission(anyOf = { android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index cfe35b061151..270e3879a71f 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -163,6 +163,30 @@ public abstract class PackageManagerInternal { } /** + * Provider for default home + */ + public interface DefaultHomeProvider { + + /** + * Get the package name of the default home. + * + * @param userId the user id + * + * @return the package name of the default home, or {@code null} if none + */ + @Nullable + String getDefaultHome(@UserIdInt int userId); + + /** + * Set the package name of the default home. + * + * @param packageName package name of the default home, or {@code null} to remove + * @param userId the user id + */ + void setDefaultHomeAsync(@Nullable String packageName, @UserIdInt int userId); + } + + /** * Sets the location provider packages provider. * @param provider The packages provider. */ @@ -886,4 +910,11 @@ public abstract class PackageManagerInternal { * @param provider the provider */ public abstract void setDefaultBrowserProvider(@NonNull DefaultBrowserProvider provider); + + /** + * Sets the default home provider. + * + * @param provider the provider + */ + public abstract void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 0abd5eaaf2aa..0f67262bf901 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -4848,7 +4848,7 @@ public class PackageParser { } /** - * Sets every the max aspect ratio of every child activity that doesn't already have an aspect + * Sets every the min aspect ratio of every child activity that doesn't already have an aspect * ratio set. */ private void setMinAspectRatio(Package owner) { diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java index 60475de351cf..4a2f800552f6 100644 --- a/core/java/android/content/pm/ServiceInfo.java +++ b/core/java/android/content/pm/ServiceInfo.java @@ -151,6 +151,7 @@ public class ServiceInfo extends ComponentInfo * @hide */ @IntDef(flag = true, prefix = { "FOREGROUND_SERVICE_TYPE_" }, value = { + FOREGROUND_SERVICE_TYPE_MANIFEST, FOREGROUND_SERVICE_TYPE_NONE, FOREGROUND_SERVICE_TYPE_DATA_SYNC, FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, @@ -180,10 +181,10 @@ public class ServiceInfo extends ComponentInfo } /** - * Return the current foreground service type. - * @return the current foreground service type. + * Return foreground service type specified in the manifest.. + * @return foreground service type specified in the manifest. */ - public int getForegroundServiceType() { + public @ForegroundServiceType int getForegroundServiceType() { return mForegroundServiceType; } diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java index 5722e7b25fbf..ea489c456f3d 100644 --- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java +++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java @@ -77,6 +77,14 @@ public class SQLiteQueryBuilder { } /** + * Get if the query is marked as DISTINCT, as last configured by + * {@link #setDistinct(boolean)}. + */ + public boolean getDistinct() { + return mDistinct; + } + + /** * Returns the list of tables being queried * * @return the list of tables being queried @@ -167,6 +175,14 @@ public class SQLiteQueryBuilder { } /** + * Gets the projection map for the query, as last configured by + * {@link #setProjectionMap(Map)}. + */ + public Map<String, String> getProjectionMap() { + return mProjectionMap; + } + + /** * Sets a projection greylist of columns that will be allowed through, even * when {@link #setStrict(boolean)} is enabled. This provides a way for * abusive custom columns like {@code COUNT(*)} to continue working. @@ -178,6 +194,16 @@ public class SQLiteQueryBuilder { } /** + * Gets the projection greylist for the query, as last configured by + * {@link #setProjectionGreylist(List)}. + * + * @hide + */ + public List<Pattern> getProjectionGreylist() { + return mProjectionGreylist; + } + + /** * Sets the cursor factory to be used for the query. You can use * one factory for all queries on a database but it is normally * easier to specify the factory when doing this query. @@ -189,6 +215,14 @@ public class SQLiteQueryBuilder { } /** + * Sets the cursor factory to be used for the query, as last configured by + * {@link #setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory)}. + */ + public SQLiteDatabase.CursorFactory getCursorFactory() { + return mFactory; + } + + /** * When set, the selection is verified against malicious arguments. * When using this class to create a statement using * {@link #buildQueryString(boolean, String, String[], String, String, String, String, String)}, @@ -214,6 +248,14 @@ public class SQLiteQueryBuilder { } /** + * Get if the query is marked as strict, as last configured by + * {@link #setStrict(boolean)}. + */ + public boolean getStrict() { + return mStrict; + } + + /** * Build an SQL query string from the given clauses. * * @param distinct true if you want each row to be unique, false otherwise. diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index d257c0377265..a696eeb6bcc7 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -156,21 +156,21 @@ public class BiometricManager { } /** - * Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password) + * Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password) * * @param token an opaque token returned by password confirmation. * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) - public void resetTimeout(byte[] token) { + public void resetLockout(byte[] token) { if (mService != null) { try { - mService.resetTimeout(token); + mService.resetLockout(token); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } else { - Slog.w(TAG, "resetTimeout(): Service not connected"); + Slog.w(TAG, "resetLockout(): Service not connected"); } } diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl index a20e2bf3d067..4971911eb87c 100644 --- a/core/java/android/hardware/biometrics/IBiometricService.aidl +++ b/core/java/android/hardware/biometrics/IBiometricService.aidl @@ -49,8 +49,8 @@ interface IBiometricService { // Client lifecycle is still managed in <Biometric>Service. void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId); - // Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password) - void resetTimeout(in byte [] token); + // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password) + void resetLockout(in byte [] token); // TODO(b/123378871): Remove when moved. // CDCA needs to send results to BiometricService if it was invoked using BiometricPrompt's diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index efe24e50272a..55b340f267a6 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -107,6 +107,11 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan mHandler.obtainMessage(MSG_REMOVED, remaining, 0, new Face(null, faceId, deviceId)).sendToTarget(); } + + @Override + public void onEnumerated(long deviceId, int faceId, int remaining) { + // TODO: Finish. Low priority since it's not used. + } }; /** @@ -474,25 +479,6 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } /** - * Reset the lockout timer when asked to do so by keyguard. - * - * @param token an opaque token returned by password confirmation. - * @hide - */ - @RequiresPermission(MANAGE_BIOMETRIC) - public void resetTimeout(byte[] token) { - if (mService != null) { - try { - mService.resetTimeout(token); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } else { - Log.w(TAG, "resetTimeout(): Service not connected!"); - } - } - - /** * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index f67760a8fafc..9609e99275c9 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -86,8 +86,8 @@ interface IFaceService { // Gets the authenticator ID for face long getAuthenticatorId(String opPackageName); - // Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password) - void resetTimeout(in byte [] cryptoToken); + // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password) + void resetLockout(in byte [] token); // Add a callback which gets notified when the face lockout period expired. void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback); diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl index b88574b74c36..cec9fd8381f9 100644 --- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl +++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl @@ -28,4 +28,5 @@ oneway interface IFaceServiceReceiver { void onAuthenticationFailed(long deviceId); void onError(long deviceId, int error, int vendorCode); void onRemoved(long deviceId, int faceId, int remaining); + void onEnumerated(long deviceId, int faceId, int remaining); } diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 80d404d03c75..d0622c88b4ca 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -18,7 +18,6 @@ package android.hardware.fingerprint; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.MANAGE_FINGERPRINT; -import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT; import static android.Manifest.permission.USE_BIOMETRIC; import static android.Manifest.permission.USE_FINGERPRINT; @@ -724,26 +723,6 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } /** - * Reset the lockout timer when asked to do so by keyguard. - * - * @param token an opaque token returned by password confirmation. - * - * @hide - */ - @RequiresPermission(RESET_FINGERPRINT_LOCKOUT) - public void resetTimeout(byte[] token) { - if (mService != null) { - try { - mService.resetTimeout(token); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } else { - Slog.w(TAG, "resetTimeout(): Service not connected!"); - } - } - - /** * @hide */ public void addLockoutResetCallback(final LockoutResetCallback callback) { diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 2aca55aacf7a..6d195ae47449 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -678,11 +678,20 @@ public class ConnectivityManager { @Deprecated public static final int TYPE_VPN = 17; + /** + * A network that is exclusively meant to be used for testing + * + * @deprecated Use {@link NetworkCapabilities} instead. + * @hide + */ + @Deprecated + public static final int TYPE_TEST = 18; // TODO: Remove this once NetworkTypes are unused. + /** {@hide} */ - public static final int MAX_RADIO_TYPE = TYPE_VPN; + public static final int MAX_RADIO_TYPE = TYPE_TEST; /** {@hide} */ - public static final int MAX_NETWORK_TYPE = TYPE_VPN; + public static final int MAX_NETWORK_TYPE = TYPE_TEST; private static final int MIN_NETWORK_TYPE = TYPE_MOBILE; @@ -3927,15 +3936,16 @@ public class ConnectivityManager { * * <p>This endpoint is exclusively for use by the NetworkStack and is protected by the * corresponding permission. + * @param network Network on which the captive portal was detected. * @param appExtras Extras to include in the app start intent. * @hide */ @SystemApi @TestApi @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) - public void startCaptivePortalApp(Bundle appExtras) { + public void startCaptivePortalApp(Network network, Bundle appExtras) { try { - mService.startCaptivePortalAppInternal(appExtras); + mService.startCaptivePortalAppInternal(network, appExtras); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 872671fa5697..87c62d2f10a7 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -168,7 +168,7 @@ interface IConnectivityManager void setAcceptUnvalidated(in Network network, boolean accept, boolean always); void setAvoidUnvalidated(in Network network); void startCaptivePortalApp(in Network network); - void startCaptivePortalAppInternal(in Bundle appExtras); + void startCaptivePortalAppInternal(in Network network, in Bundle appExtras); boolean getAvoidBadWifi(); int getMultipathPreference(in Network Network); diff --git a/core/java/android/net/INetworkMonitor.aidl b/core/java/android/net/INetworkMonitor.aidl index 41f969acad6f..c94cdde15aa8 100644 --- a/core/java/android/net/INetworkMonitor.aidl +++ b/core/java/android/net/INetworkMonitor.aidl @@ -34,6 +34,7 @@ oneway interface INetworkMonitor { void start(); void launchCaptivePortalApp(); + void notifyCaptivePortalAppFinished(int response); void forceReevaluation(int uid); void notifyPrivateDnsChanged(in PrivateDnsConfigParcel config); void notifyDnsResponse(int returnCode); diff --git a/core/java/android/net/INetworkMonitorCallbacks.aidl b/core/java/android/net/INetworkMonitorCallbacks.aidl index 514658549651..2c61511feb72 100644 --- a/core/java/android/net/INetworkMonitorCallbacks.aidl +++ b/core/java/android/net/INetworkMonitorCallbacks.aidl @@ -26,5 +26,4 @@ oneway interface INetworkMonitorCallbacks { void notifyPrivateDnsConfigResolved(in PrivateDnsConfigParcel config); void showProvisioningNotification(String action, String packageName); void hideProvisioningNotification(); - void logCaptivePortalLoginEvent(int eventId, String packageName); }
\ No newline at end of file diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java index 62cf7d7ceb25..b9d49c14f6c6 100644 --- a/core/java/android/net/InterfaceConfiguration.java +++ b/core/java/android/net/InterfaceConfiguration.java @@ -36,8 +36,9 @@ public class InterfaceConfiguration implements Parcelable { private LinkAddress mAddr; private HashSet<String> mFlags = Sets.newHashSet(); - private static final String FLAG_UP = INetd.IF_STATE_UP; - private static final String FLAG_DOWN = INetd.IF_STATE_DOWN; + // Must be kept in sync with constant in INetd.aidl + private static final String FLAG_UP = "up"; + private static final String FLAG_DOWN = "down"; private static final String[] EMPTY_STRING_ARRAY = new String[0]; diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 7e9bda14b199..1d2d81dc4fbe 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -597,6 +597,7 @@ public final class NetworkCapabilities implements Parcelable { TRANSPORT_VPN, TRANSPORT_WIFI_AWARE, TRANSPORT_LOWPAN, + TRANSPORT_TEST, }) public @interface Transport { } @@ -635,10 +636,18 @@ public final class NetworkCapabilities implements Parcelable { */ public static final int TRANSPORT_LOWPAN = 6; + /** + * Indicates this network uses a Test-only virtual interface as a transport. + * + * @hide + */ + @TestApi + public static final int TRANSPORT_TEST = 7; + /** @hide */ public static final int MIN_TRANSPORT = TRANSPORT_CELLULAR; /** @hide */ - public static final int MAX_TRANSPORT = TRANSPORT_LOWPAN; + public static final int MAX_TRANSPORT = TRANSPORT_TEST; /** @hide */ public static boolean isValidTransport(@Transport int transportType) { @@ -652,7 +661,8 @@ public final class NetworkCapabilities implements Parcelable { "ETHERNET", "VPN", "WIFI_AWARE", - "LOWPAN" + "LOWPAN", + "TEST" }; /** diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 5ab34e9aa6e8..bf272625e713 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -92,16 +92,6 @@ public class NetworkPolicyManager { public static final int MASK_ALL_NETWORKS = 0b11110000; public static final int FIREWALL_RULE_DEFAULT = 0; - public static final int FIREWALL_RULE_ALLOW = INetd.FIREWALL_RULE_ALLOW; - public static final int FIREWALL_RULE_DENY = INetd.FIREWALL_RULE_DENY; - - public static final int FIREWALL_TYPE_WHITELIST = INetd.FIREWALL_WHITELIST; - public static final int FIREWALL_TYPE_BLACKLIST = INetd.FIREWALL_BLACKLIST; - - public static final int FIREWALL_CHAIN_NONE = INetd.FIREWALL_CHAIN_NONE; - public static final int FIREWALL_CHAIN_DOZABLE = INetd.FIREWALL_CHAIN_DOZABLE; - public static final int FIREWALL_CHAIN_STANDBY = INetd.FIREWALL_CHAIN_STANDBY; - public static final int FIREWALL_CHAIN_POWERSAVE = INetd.FIREWALL_CHAIN_POWERSAVE; public static final String FIREWALL_CHAIN_NAME_NONE = "none"; public static final String FIREWALL_CHAIN_NAME_DOZABLE = "dozable"; diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java index ca49438390e9..dbb894f92f55 100644 --- a/core/java/android/net/NetworkStack.java +++ b/core/java/android/net/NetworkStack.java @@ -15,46 +15,17 @@ */ package android.net; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH; -import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; - -import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.SystemService; import android.annotation.TestApi; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.content.pm.PackageManager; -import android.net.dhcp.DhcpServingParamsParcel; -import android.net.dhcp.IDhcpServerCallbacks; -import android.net.ip.IIpClientCallbacks; -import android.os.Binder; -import android.os.IBinder; -import android.os.Process; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.UserHandle; -import android.util.Slog; - -import com.android.internal.annotations.GuardedBy; - -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; /** - * Service used to communicate with the network stack, which is running in a separate module. + * + * Constants for client code communicating with the network stack service. * @hide */ -@SystemService(Context.NETWORK_STACK_SERVICE) @SystemApi @TestApi public class NetworkStack { - private static final String TAG = NetworkStack.class.getSimpleName(); - /** * Permission granted only to the NetworkStack APK, defined in NetworkStackStub with signature * protection level. @@ -65,235 +36,5 @@ public class NetworkStack { public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK"; - private static final int NETWORKSTACK_TIMEOUT_MS = 10_000; - - @NonNull - @GuardedBy("mPendingNetStackRequests") - private final ArrayList<NetworkStackCallback> mPendingNetStackRequests = new ArrayList<>(); - @Nullable - @GuardedBy("mPendingNetStackRequests") - private INetworkStackConnector mConnector; - - private volatile boolean mNetworkStackStartRequested = false; - - private interface NetworkStackCallback { - void onNetworkStackConnected(INetworkStackConnector connector); - } - - /** @hide */ - public NetworkStack() { } - - /** - * Create a DHCP server according to the specified parameters. - * - * <p>The server will be returned asynchronously through the provided callbacks. - * @hide - */ - public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params, - final IDhcpServerCallbacks cb) { - requestConnector(connector -> { - try { - connector.makeDhcpServer(ifName, params, cb); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } - }); - } - - /** - * Create an IpClient on the specified interface. - * - * <p>The IpClient will be returned asynchronously through the provided callbacks. - * @hide - */ - public void makeIpClient(String ifName, IIpClientCallbacks cb) { - requestConnector(connector -> { - try { - connector.makeIpClient(ifName, cb); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } - }); - } - - /** - * Create a NetworkMonitor. - * - * <p>The INetworkMonitor will be returned asynchronously through the provided callbacks. - * @hide - */ - public void makeNetworkMonitor( - NetworkParcelable network, String name, INetworkMonitorCallbacks cb) { - requestConnector(connector -> { - try { - connector.makeNetworkMonitor(network, name, cb); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } - }); - } - - private class NetworkStackConnection implements ServiceConnection { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - registerNetworkStackService(service); - } - - @Override - public void onServiceDisconnected(ComponentName name) { - // TODO: crash/reboot the system ? - Slog.wtf(TAG, "Lost network stack connector"); - } - }; - - private void registerNetworkStackService(@NonNull IBinder service) { - final INetworkStackConnector connector = INetworkStackConnector.Stub.asInterface(service); - - ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */, - DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL); - - final ArrayList<NetworkStackCallback> requests; - synchronized (mPendingNetStackRequests) { - requests = new ArrayList<>(mPendingNetStackRequests); - mPendingNetStackRequests.clear(); - mConnector = connector; - } - - for (NetworkStackCallback r : requests) { - r.onNetworkStackConnected(connector); - } - } - - /** - * Start the network stack. Should be called only once on device startup. - * - * <p>This method will start the network stack either in the network stack process, or inside - * the system server on devices that do not support the network stack module. The network stack - * connector will then be delivered asynchronously to clients that requested it before it was - * started. - * @hide - */ - public void start(Context context) { - mNetworkStackStartRequested = true; - // Try to bind in-process if the library is available - IBinder connector = null; - try { - final Class service = Class.forName( - "com.android.server.NetworkStackService", - true /* initialize */, - context.getClassLoader()); - connector = (IBinder) service.getMethod("makeConnector", Context.class) - .invoke(null, context); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService"); - // TODO: crash/reboot system here ? - return; - } catch (ClassNotFoundException e) { - // Normal behavior if stack is provided by the app: fall through - } - - // In-process network stack. Add the service to the service manager here. - if (connector != null) { - registerNetworkStackService(connector); - return; - } - // Start the network stack process. The service will be added to the service manager in - // NetworkStackConnection.onServiceConnected(). - final Intent intent = new Intent(INetworkStackConnector.class.getName()); - final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0); - intent.setComponent(comp); - - if (comp == null) { - Slog.wtf(TAG, "Could not resolve the network stack with " + intent); - // TODO: crash/reboot system server ? - return; - } - - final PackageManager pm = context.getPackageManager(); - int uid = -1; - try { - uid = pm.getPackageUid(comp.getPackageName(), UserHandle.USER_SYSTEM); - } catch (PackageManager.NameNotFoundException e) { - Slog.wtf("Network stack package not found", e); - // Fall through - } - - if (uid != Process.NETWORK_STACK_UID) { - throw new SecurityException("Invalid network stack UID: " + uid); - } - - final int hasPermission = - pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName()); - if (hasPermission != PERMISSION_GRANTED) { - throw new SecurityException( - "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK); - } - - if (!context.bindServiceAsUser(intent, new NetworkStackConnection(), - Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) { - Slog.wtf(TAG, - "Could not bind to network stack in-process, or in app with " + intent); - // TODO: crash/reboot system server if no network stack after a timeout ? - } - } - - /** - * For non-system server clients, get the connector registered by the system server. - */ - private INetworkStackConnector getRemoteConnector() { - // Block until the NetworkStack connector is registered in ServiceManager. - // <p>This is only useful for non-system processes that do not have a way to be notified of - // registration completion. Adding a callback system would be too heavy weight considering - // that the connector is registered on boot, so it is unlikely that a client would request - // it before it is registered. - // TODO: consider blocking boot on registration and simplify much of the logic in this class - IBinder connector; - try { - final long before = System.currentTimeMillis(); - while ((connector = ServiceManager.getService(Context.NETWORK_STACK_SERVICE)) == null) { - Thread.sleep(20); - if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) { - Slog.e(TAG, "Timeout waiting for NetworkStack connector"); - return null; - } - } - } catch (InterruptedException e) { - Slog.e(TAG, "Error waiting for NetworkStack connector", e); - return null; - } - - return INetworkStackConnector.Stub.asInterface(connector); - } - - private void requestConnector(@NonNull NetworkStackCallback request) { - // TODO: PID check. - final int caller = Binder.getCallingUid(); - if (caller != Process.SYSTEM_UID && !UserHandle.isSameApp(caller, Process.BLUETOOTH_UID)) { - // Don't even attempt to obtain the connector and give a nice error message - throw new SecurityException( - "Only the system server should try to bind to the network stack."); - } - - if (!mNetworkStackStartRequested) { - // The network stack is not being started in this process, e.g. this process is not - // the system server. Get a remote connector registered by the system server. - final INetworkStackConnector connector = getRemoteConnector(); - synchronized (mPendingNetStackRequests) { - mConnector = connector; - } - request.onNetworkStackConnected(connector); - return; - } - - final INetworkStackConnector connector; - synchronized (mPendingNetStackRequests) { - connector = mConnector; - if (connector == null) { - mPendingNetStackRequests.add(request); - return; - } - } - - request.onNetworkStackConnected(connector); - } + private NetworkStack() {} } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index ab6dd7c07b42..b7e65b9151b9 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -46,6 +46,7 @@ import com.android.internal.os.BatteryStatsHelper; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -262,6 +263,7 @@ public abstract class BatteryStats implements Parcelable { private static final long BYTES_PER_KB = 1024; private static final long BYTES_PER_MB = 1048576; // 1024^2 private static final long BYTES_PER_GB = 1073741824; //1024^3 + public static final double MILLISECONDS_IN_HOUR = 3600 * 1000; private static final String VERSION_DATA = "vers"; private static final String UID_DATA = "uid"; @@ -482,6 +484,13 @@ public abstract class BatteryStats implements Parcelable { * yield a value of 0 if the device doesn't support power calculations. */ public abstract LongCounter getPowerCounter(); + + /** + * @return a non-null {@link LongCounter} representing total power monitored on the rails + * in mAms (miliamps-milliseconds). The counter may always yield a value of 0 if the device + * doesn't support power rail monitoring. + */ + public abstract LongCounter getMonitoredRailChargeConsumedMaMs(); } /** @@ -1526,6 +1535,9 @@ public abstract class BatteryStats implements Parcelable { // The charge of the battery in micro-Ampere-hours. public int batteryChargeUAh; + public double modemRailChargeMah; + public double wifiRailChargeMah; + // Constants from SCREEN_BRIGHTNESS_* public static final int STATE_BRIGHTNESS_SHIFT = 0; public static final int STATE_BRIGHTNESS_MASK = 0x7; @@ -1738,6 +1750,8 @@ public abstract class BatteryStats implements Parcelable { | ((((int)batteryVoltage)<<16)&0xffff0000); dest.writeInt(bat); dest.writeInt(batteryChargeUAh); + dest.writeDouble(modemRailChargeMah); + dest.writeDouble(wifiRailChargeMah); dest.writeInt(states); dest.writeInt(states2); if (wakelockTag != null) { @@ -1767,6 +1781,8 @@ public abstract class BatteryStats implements Parcelable { batteryTemperature = (short)(bat2&0xffff); batteryVoltage = (char)((bat2>>16)&0xffff); batteryChargeUAh = src.readInt(); + modemRailChargeMah = src.readDouble(); + wifiRailChargeMah = src.readDouble(); states = src.readInt(); states2 = src.readInt(); if ((bat&0x10000000) != 0) { @@ -1807,6 +1823,8 @@ public abstract class BatteryStats implements Parcelable { batteryTemperature = 0; batteryVoltage = 0; batteryChargeUAh = 0; + modemRailChargeMah = 0; + wifiRailChargeMah = 0; states = 0; states2 = 0; wakelockTag = null; @@ -1835,6 +1853,8 @@ public abstract class BatteryStats implements Parcelable { batteryTemperature = o.batteryTemperature; batteryVoltage = o.batteryVoltage; batteryChargeUAh = o.batteryChargeUAh; + modemRailChargeMah = o.modemRailChargeMah; + wifiRailChargeMah = o.wifiRailChargeMah; states = o.states; states2 = o.states2; if (o.wakelockTag != null) { @@ -1867,6 +1887,8 @@ public abstract class BatteryStats implements Parcelable { && batteryTemperature == o.batteryTemperature && batteryVoltage == o.batteryVoltage && batteryChargeUAh == o.batteryChargeUAh + && modemRailChargeMah == o.modemRailChargeMah + && wifiRailChargeMah == o.wifiRailChargeMah && states == o.states && states2 == o.states2 && currentTime == o.currentTime; @@ -3311,7 +3333,8 @@ public abstract class BatteryStats implements Parcelable { if (counter.getIdleTimeCounter().getCountLocked(which) != 0 || counter.getRxTimeCounter().getCountLocked(which) != 0 - || counter.getPowerCounter().getCountLocked(which) != 0) { + || counter.getPowerCounter().getCountLocked(which) != 0 + || counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which) != 0) { return true; } @@ -3345,7 +3368,10 @@ public abstract class BatteryStats implements Parcelable { pw.print(","); pw.print(counter.getRxTimeCounter().getCountLocked(which)); pw.print(","); - pw.print(counter.getPowerCounter().getCountLocked(which) / (1000 * 60 * 60)); + pw.print(counter.getPowerCounter().getCountLocked(which) / (MILLISECONDS_IN_HOUR)); + pw.print(","); + pw.print(counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which) + / (MILLISECONDS_IN_HOUR)); for (LongCounter c : counter.getTxTimeCounters()) { pw.print(","); pw.print(c.getCountLocked(which)); @@ -3370,7 +3396,10 @@ public abstract class BatteryStats implements Parcelable { proto.write(ControllerActivityProto.RX_DURATION_MS, counter.getRxTimeCounter().getCountLocked(which)); proto.write(ControllerActivityProto.POWER_MAH, - counter.getPowerCounter().getCountLocked(which) / (1000 * 60 * 60)); + counter.getPowerCounter().getCountLocked(which) / (MILLISECONDS_IN_HOUR)); + proto.write(ControllerActivityProto.MONITORED_RAIL_CHARGE_MAH, + counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which) + / (MILLISECONDS_IN_HOUR)); long tToken; LongCounter[] txCounters = counter.getTxTimeCounters(); @@ -3400,6 +3429,8 @@ public abstract class BatteryStats implements Parcelable { final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which); final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which); final long powerDrainMaMs = counter.getPowerCounter().getCountLocked(which); + final long monitoredRailChargeConsumedMaMs = + counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which); // Battery real time final long totalControllerActivityTimeMs = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000; @@ -3522,10 +3553,22 @@ public abstract class BatteryStats implements Parcelable { sb.append(" "); sb.append(controllerName); sb.append(" Battery drain: ").append( - BatteryStatsHelper.makemAh(powerDrainMaMs / (double) (1000*60*60))); + BatteryStatsHelper.makemAh(powerDrainMaMs / MILLISECONDS_IN_HOUR)); sb.append("mAh"); pw.println(sb.toString()); } + + if (monitoredRailChargeConsumedMaMs > 0) { + sb.setLength(0); + sb.append(prefix); + sb.append(" "); + sb.append(controllerName); + sb.append(" Monitored rail energy drain: ").append( + new DecimalFormat("#.##").format( + monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR)); + sb.append(" mAh"); + pw.println(sb.toString()); + } } /** @@ -6103,6 +6146,8 @@ public abstract class BatteryStats implements Parcelable { int oldTemp = -1; int oldVolt = -1; int oldChargeMAh = -1; + double oldModemRailChargeMah = -1; + double oldWifiRailChargeMah = -1; long lastTime = -1; void reset() { @@ -6114,6 +6159,8 @@ public abstract class BatteryStats implements Parcelable { oldTemp = -1; oldVolt = -1; oldChargeMAh = -1; + oldModemRailChargeMah = -1; + oldWifiRailChargeMah = -1; } public void printNextItem(PrintWriter pw, HistoryItem rec, long baseTime, boolean checkin, @@ -6299,6 +6346,16 @@ public abstract class BatteryStats implements Parcelable { item.append(checkin ? ",Bcc=" : " charge="); item.append(oldChargeMAh); } + if (oldModemRailChargeMah != rec.modemRailChargeMah) { + oldModemRailChargeMah = rec.modemRailChargeMah; + item.append(checkin ? ",Mrc=" : " modemRailChargemAh="); + item.append(new DecimalFormat("#.##").format(oldModemRailChargeMah)); + } + if (oldWifiRailChargeMah != rec.wifiRailChargeMah) { + oldWifiRailChargeMah = rec.wifiRailChargeMah; + item.append(checkin ? ",Wrc=" : " wifiRailChargemAh="); + item.append(new DecimalFormat("#.##").format(oldWifiRailChargeMah)); + } printBitDescriptions(item, oldState, rec.states, rec.wakelockTag, HISTORY_STATE_DESCRIPTIONS, !checkin); printBitDescriptions(item, oldState2, rec.states2, null, diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index d12ace8424f0..ec6da24dc2b1 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -61,9 +61,11 @@ public class GraphicsEnvironment { private static final String SYSTEM_DRIVER_VERSION_NAME = ""; private static final long SYSTEM_DRIVER_VERSION_CODE = 0; private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0"; + private static final String PROPERTY_GFX_DRIVER_BUILD_DATE = "ro.gfx.driver.build_date"; private static final String ANGLE_RULES_FILE = "a4a_rules.json"; private static final String ANGLE_TEMP_RULES = "debug.angle.rules"; private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID"; + private static final String GAME_DRIVER_WHITELIST_ALL = "*"; private ClassLoader mClassLoader; private String mLayerPath; @@ -78,8 +80,9 @@ public class GraphicsEnvironment { setupGpuLayers(context, coreSettings, pm, packageName); setupAngle(context, coreSettings, pm, packageName); if (!chooseDriver(context, coreSettings, pm, packageName)) { + final String driverBuildDate = SystemProperties.get(PROPERTY_GFX_DRIVER_BUILD_DATE); setGpuStats(SYSTEM_DRIVER_NAME, SYSTEM_DRIVER_VERSION_NAME, SYSTEM_DRIVER_VERSION_CODE, - packageName); + driverBuildDate == null ? "" : driverBuildDate, packageName); } } @@ -573,8 +576,8 @@ public class GraphicsEnvironment { final PackageInfo driverPackageInfo; try { - driverPackageInfo = - pm.getPackageInfo(driverPackageName, PackageManager.MATCH_SYSTEM_ONLY); + driverPackageInfo = pm.getPackageInfo(driverPackageName, + PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA); } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "driver package '" + driverPackageName + "' not installed"); return false; @@ -623,9 +626,10 @@ public class GraphicsEnvironment { final boolean isOptIn = getGlobalSettingsString(null, coreSettings, Settings.Global.GAME_DRIVER_OPT_IN_APPS).contains(packageName); - if (!isOptIn - && !getGlobalSettingsString(null, coreSettings, - Settings.Global.GAME_DRIVER_WHITELIST).contains(packageName)) { + final List<String> whitelist = getGlobalSettingsString(null, coreSettings, + Settings.Global.GAME_DRIVER_WHITELIST); + if (!isOptIn && whitelist.indexOf(GAME_DRIVER_WHITELIST_ALL) != 0 + && !whitelist.contains(packageName)) { if (DEBUG) { Log.w(TAG, packageName + " is not on the whitelist."); } @@ -653,9 +657,6 @@ public class GraphicsEnvironment { return false; } - setGpuStats(driverPackageName, driverPackageInfo.versionName, driverAppInfo.longVersionCode, - packageName); - final StringBuilder sb = new StringBuilder(); sb.append(driverAppInfo.nativeLibraryDir) .append(File.pathSeparator); @@ -667,6 +668,12 @@ public class GraphicsEnvironment { if (DEBUG) Log.v(TAG, "gfx driver package libs: " + paths); setDriverPath(paths); + final String driverBuildDate = driverAppInfo.metaData == null + ? "" + : driverAppInfo.metaData.getString("driver_build_date"); + setGpuStats(driverPackageName, driverPackageInfo.versionName, driverAppInfo.longVersionCode, + driverBuildDate == null ? "" : driverBuildDate, packageName); + return true; } @@ -708,7 +715,7 @@ public class GraphicsEnvironment { private static native void setDebugLayersGLES(String layers); private static native void setDriverPath(String path); private static native void setGpuStats(String driverPackageName, String driverVersionName, - long driverVersionCode, String appPackageName); + long driverVersionCode, String driverBuildDate, String appPackageName); private static native void setAngleInfo(String path, String appPackage, String devOptIn, FileDescriptor rulesFd, long rulesOffset, long rulesLength); private static native boolean getShouldUseAngle(String packageName); diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java index 87e1b7d21f53..1420e2f5ce10 100644 --- a/core/java/android/os/LocaleList.java +++ b/core/java/android/os/LocaleList.java @@ -20,6 +20,7 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Size; +import android.annotation.SystemApi; import android.content.LocaleProto; import android.icu.util.ULocale; import android.util.proto.ProtoOutputStream; @@ -324,6 +325,15 @@ public final class LocaleList implements Parcelable { return LOCALE_EN_XA.equals(locale) || LOCALE_AR_XB.equals(locale); } + /** + * Returns true if locale is a pseudo-locale, false otherwise. + * {@hide} + */ + @SystemApi + public static boolean isPseudoLocale(@Nullable ULocale locale) { + return isPseudoLocale(locale != null ? locale.toLocale() : null); + } + @IntRange(from=0, to=1) private static int matchScore(Locale supported, Locale desired) { if (supported.equals(desired)) { diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 2ecf9d1159f0..cfe2d28f1024 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1784,7 +1784,7 @@ public final class PowerManager { * * see {@link #registerThermalStatusCallback} */ - public void unregisterThermalStatusCallback(ThermalStatusCallback callback) { + public void unregisterThermalStatusCallback(@NonNull ThermalStatusCallback callback) { Preconditions.checkNotNull(callback, "callback cannnot be null"); synchronized (this) { if (mThermalService == null) { diff --git a/core/java/android/os/connectivity/WifiBatteryStats.java b/core/java/android/os/connectivity/WifiBatteryStats.java index e5341eeeb17b..3639c71ae3b5 100644 --- a/core/java/android/os/connectivity/WifiBatteryStats.java +++ b/core/java/android/os/connectivity/WifiBatteryStats.java @@ -44,6 +44,7 @@ public final class WifiBatteryStats implements Parcelable { private long[] mTimeInStateMs; private long[] mTimeInSupplicantStateMs; private long[] mTimeInRxSignalStrengthLevelMs; + private long mMonitoredRailChargeConsumedMaMs; public static final Parcelable.Creator<WifiBatteryStats> CREATOR = new Parcelable.Creator<WifiBatteryStats>() { @@ -77,6 +78,7 @@ public final class WifiBatteryStats implements Parcelable { out.writeLongArray(mTimeInStateMs); out.writeLongArray(mTimeInRxSignalStrengthLevelMs); out.writeLongArray(mTimeInSupplicantStateMs); + out.writeLong(mMonitoredRailChargeConsumedMaMs); } public void readFromParcel(Parcel in) { @@ -96,6 +98,7 @@ public final class WifiBatteryStats implements Parcelable { in.readLongArray(mTimeInStateMs); in.readLongArray(mTimeInRxSignalStrengthLevelMs); in.readLongArray(mTimeInSupplicantStateMs); + mMonitoredRailChargeConsumedMaMs = in.readLong(); } public long getLoggingDurationMs() { @@ -162,6 +165,10 @@ public final class WifiBatteryStats implements Parcelable { return mTimeInSupplicantStateMs; } + public long getMonitoredRailChargeConsumedMaMs() { + return mMonitoredRailChargeConsumedMaMs; + } + public void setLoggingDurationMs(long t) { mLoggingDurationMs = t; return; @@ -245,6 +252,11 @@ public final class WifiBatteryStats implements Parcelable { return; } + public void setMonitoredRailChargeConsumedMaMs(long monitoredRailEnergyConsumedMaMs) { + mMonitoredRailChargeConsumedMaMs = monitoredRailEnergyConsumedMaMs; + return; + } + public int describeContents() { return 0; } @@ -274,6 +286,7 @@ public final class WifiBatteryStats implements Parcelable { Arrays.fill(mTimeInRxSignalStrengthLevelMs, 0); mTimeInSupplicantStateMs = new long[BatteryStats.NUM_WIFI_SUPPL_STATES]; Arrays.fill(mTimeInSupplicantStateMs, 0); + mMonitoredRailChargeConsumedMaMs = 0; return; } }
\ No newline at end of file diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java index 89967c325dd2..d62bc6c5a872 100644 --- a/core/java/android/permission/PermissionControllerManager.java +++ b/core/java/android/permission/PermissionControllerManager.java @@ -51,6 +51,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.util.ArrayMap; import android.util.Log; +import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService; @@ -85,9 +86,11 @@ public final class PermissionControllerManager { private static final Object sLock = new Object(); - /** App global remote service used by all {@link PermissionControllerManager managers} */ + /** + * Global remote services (per user) used by all {@link PermissionControllerManager managers} + */ @GuardedBy("sLock") - private static RemoteService sRemoteService; + private static SparseArray<RemoteService> sRemoteServices = new SparseArray<>(1); /** * The key for retrieving the result from the returned bundle. @@ -203,6 +206,7 @@ public final class PermissionControllerManager { } private final @NonNull Context mContext; + private final @NonNull RemoteService mRemoteService; /** * Create a new {@link PermissionControllerManager}. @@ -213,14 +217,18 @@ public final class PermissionControllerManager { */ public PermissionControllerManager(@NonNull Context context) { synchronized (sLock) { - if (sRemoteService == null) { + RemoteService remoteService = sRemoteServices.get(context.getUserId(), null); + if (remoteService == null) { Intent intent = new Intent(SERVICE_INTERFACE); intent.setPackage(context.getPackageManager().getPermissionControllerPackageName()); ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0); - sRemoteService = new RemoteService(context.getApplicationContext(), - serviceInfo.getComponentInfo().getComponentName()); + remoteService = new RemoteService(context.getApplicationContext(), + serviceInfo.getComponentInfo().getComponentName(), context.getUser()); + sRemoteServices.put(context.getUserId(), remoteService); } + + mRemoteService = remoteService; } mContext = context; @@ -255,7 +263,7 @@ public final class PermissionControllerManager { + " required"); } - sRemoteService.scheduleRequest(new PendingRevokeRuntimePermissionRequest(sRemoteService, + mRemoteService.scheduleRequest(new PendingRevokeRuntimePermissionRequest(mRemoteService, request, doDryRun, reason, mContext.getPackageName(), executor, callback)); } @@ -276,7 +284,7 @@ public final class PermissionControllerManager { checkNotNull(executor); checkNotNull(callback); - sRemoteService.scheduleRequest(new PendingGetRuntimePermissionBackup(sRemoteService, + mRemoteService.scheduleRequest(new PendingGetRuntimePermissionBackup(mRemoteService, user, executor, callback)); } @@ -294,8 +302,8 @@ public final class PermissionControllerManager { checkNotNull(backup); checkNotNull(user); - sRemoteService.scheduleAsyncRequest( - new PendingRestoreRuntimePermissionBackup(sRemoteService, backup, user)); + mRemoteService.scheduleAsyncRequest( + new PendingRestoreRuntimePermissionBackup(mRemoteService, backup, user)); } /** @@ -318,8 +326,8 @@ public final class PermissionControllerManager { checkNotNull(executor); checkNotNull(callback); - sRemoteService.scheduleRequest( - new PendingRestoreDelayedRuntimePermissionBackup(sRemoteService, packageName, + mRemoteService.scheduleRequest( + new PendingRestoreDelayedRuntimePermissionBackup(mRemoteService, packageName, user, executor, callback)); } @@ -338,8 +346,8 @@ public final class PermissionControllerManager { checkNotNull(packageName); checkNotNull(callback); - sRemoteService.scheduleRequest(new PendingGetAppPermissionRequest(sRemoteService, - packageName, callback, handler == null ? sRemoteService.getHandler() : handler)); + mRemoteService.scheduleRequest(new PendingGetAppPermissionRequest(mRemoteService, + packageName, callback, handler == null ? mRemoteService.getHandler() : handler)); } /** @@ -356,7 +364,7 @@ public final class PermissionControllerManager { checkNotNull(packageName); checkNotNull(permissionName); - sRemoteService.scheduleAsyncRequest(new PendingRevokeAppPermissionRequest(packageName, + mRemoteService.scheduleAsyncRequest(new PendingRevokeAppPermissionRequest(packageName, permissionName)); } @@ -379,9 +387,9 @@ public final class PermissionControllerManager { checkFlagsArgument(flags, COUNT_WHEN_SYSTEM | COUNT_ONLY_WHEN_GRANTED); checkNotNull(callback); - sRemoteService.scheduleRequest(new PendingCountPermissionAppsRequest(sRemoteService, + mRemoteService.scheduleRequest(new PendingCountPermissionAppsRequest(mRemoteService, permissionNames, flags, callback, - handler == null ? sRemoteService.getHandler() : handler)); + handler == null ? mRemoteService.getHandler() : handler)); } /** @@ -402,7 +410,7 @@ public final class PermissionControllerManager { checkNotNull(executor); checkNotNull(callback); - sRemoteService.scheduleRequest(new PendingGetPermissionUsagesRequest(sRemoteService, + mRemoteService.scheduleRequest(new PendingGetPermissionUsagesRequest(mRemoteService, countSystem, numMillis, executor, callback)); } @@ -424,8 +432,8 @@ public final class PermissionControllerManager { checkNotNull(executor); checkNotNull(callback); - sRemoteService.scheduleRequest(new PendingIsApplicationQualifiedForRoleRequest( - sRemoteService, roleName, packageName, executor, callback)); + mRemoteService.scheduleRequest(new PendingIsApplicationQualifiedForRoleRequest( + mRemoteService, roleName, packageName, executor, callback)); } /** @@ -441,11 +449,12 @@ public final class PermissionControllerManager { * * @param context A context to use * @param componentName The component of the service to connect to + * @param user User the remote service should be connected as */ - RemoteService(@NonNull Context context, @NonNull ComponentName componentName) { - super(context, SERVICE_INTERFACE, componentName, UserHandle.myUserId(), - service -> Log.e(TAG, "RuntimePermPresenterService " + service + " died"), - false, false, 1); + RemoteService(@NonNull Context context, @NonNull ComponentName componentName, + @NonNull UserHandle user) { + super(context, SERVICE_INTERFACE, componentName, user.getIdentifier(), + service -> Log.e(TAG, "RemoteService " + service + " died"), false, false, 1); } /** @@ -624,7 +633,7 @@ public final class PermissionControllerManager { * * <p>Needs to be called when canceling this task as it might be hung. */ - void interruptRead() { + void interruptWrite() { IoUtils.closeQuietly(mLocalPipe); } @@ -806,18 +815,19 @@ public final class PermissionControllerManager { @Override public void run(@NonNull IPermissionController service) { + mBackupSender.execute(mBackup); + ParcelFileDescriptor remotePipe = mBackupSender.getRemotePipe(); try { service.restoreRuntimePermissionBackup(mUser, remotePipe); } catch (RemoteException e) { Log.e(TAG, "Error sending runtime permission backup", e); mBackupSender.cancel(false); + mBackupSender.interruptWrite(); } finally { // Remote pipe end is duped by binder call. Local copy is not needed anymore IoUtils.closeQuietly(remotePipe); } - - mBackupSender.execute(mBackup); } } diff --git a/core/java/android/permission/PermissionManagerInternal.java b/core/java/android/permission/PermissionManagerInternal.java new file mode 100644 index 000000000000..92dbab33a2f0 --- /dev/null +++ b/core/java/android/permission/PermissionManagerInternal.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.permission; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.UserHandle; + +/** + * Internal interfaces to be used by other components within the system server. + * + * <p>Only for use within the system server. + * + * @hide + */ +public abstract class PermissionManagerInternal { + /** + * Get the state of the runtime permissions as xml file. + * + * @param user The user the data should be extracted for + * + * @return The state as a xml file + */ + public abstract @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user); + + /** + * Restore a permission state previously backed up via {@link #backupRuntimePermissions}. + * + * <p>If not all state can be restored, the un-restoreable state will be delayed and can be + * re-tried via {@link #restoreDelayedRuntimePermissions}. + * + * @param backup The state as an xml file + * @param user The user the data should be restored for + */ + public abstract void restoreRuntimePermissions(@NonNull byte[] backup, + @NonNull UserHandle user); + + /** + * Try to apply permission backup of a package that was previously not applied. + * + * @param packageName The package that is newly installed + * @param user The user the package is installed for + * + * @see #restoreRuntimePermissions + */ + public abstract void restoreDelayedRuntimePermissions(@NonNull String packageName, + @NonNull UserHandle user); +} diff --git a/core/java/android/provider/BaseColumns.java b/core/java/android/provider/BaseColumns.java index f594c19d8eca..00c9e72df880 100644 --- a/core/java/android/provider/BaseColumns.java +++ b/core/java/android/provider/BaseColumns.java @@ -16,17 +16,18 @@ package android.provider; -public interface BaseColumns -{ +import android.database.Cursor; + +public interface BaseColumns { /** * The unique ID for a row. - * <P>Type: INTEGER (long)</P> */ + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String _ID = "_id"; /** * The count of rows in a directory. - * <P>Type: INTEGER</P> */ + // @Column(Cursor.FIELD_TYPE_INTEGER) public static final String _COUNT = "_count"; } diff --git a/core/java/android/provider/Column.java b/core/java/android/provider/Column.java new file mode 100644 index 000000000000..1364fb86cbd6 --- /dev/null +++ b/core/java/android/provider/Column.java @@ -0,0 +1,50 @@ +/* + * 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.provider; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Denotes that a field is a {@link ContentProvider} column. It can be used as a + * key for {@link ContentValues} when inserting or updating data, or as a + * projection when querying. + * + * @hide + */ +@Documented +@Retention(RUNTIME) +@Target({FIELD}) +public @interface Column { + /** + * The {@link Cursor#getType(int)} of the data stored in this column. + */ + int value(); + + /** + * This column is read-only and cannot be defined during insert or updates. + */ + boolean readOnly() default false; +} diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 104b61dc410d..7eb03007ee6a 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -316,10 +316,17 @@ public final class DeviceConfig { public interface Rollback { String NAMESPACE = "rollback"; + String BOOT_NAMESPACE = "rollback_boot"; + /** * Timeout in milliseconds for enabling package rollback. */ String ENABLE_ROLLBACK_TIMEOUT = "enable_rollback_timeout"; + + /** + * The lifetime duration of rollback packages in millis + */ + String ROLLBACK_LIFETIME_IN_MILLIS = "rollback_lifetime_in_millis"; } /** diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 0b3842080a75..1b10bef76067 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -857,8 +857,6 @@ public final class MediaStore { * this path. Instead of trying to open this path directly, apps should * use {@link ContentResolver#openFileDescriptor(Uri, String)} to gain * access. - * <p> - * Type: TEXT * * @deprecated Apps may not have filesystem permissions to directly * access this path. Instead of trying to open this path @@ -869,6 +867,7 @@ public final class MediaStore { * {@link android.os.Build.VERSION_CODES#Q} or higher. */ @Deprecated + @Column(Cursor.FIELD_TYPE_STRING) public static final String DATA = "_data"; /** @@ -883,44 +882,44 @@ public final class MediaStore { * If you require the hash of a specific item, you can call * {@link ContentResolver#canonicalize(Uri)}, which will block until the * hash is calculated. - * <p> - * Type: BLOB + * * @removed */ @Deprecated + @Column(value = Cursor.FIELD_TYPE_BLOB, readOnly = true) public static final String HASH = "_hash"; /** * The size of the file in bytes - * <P>Type: INTEGER (long)</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String SIZE = "_size"; /** * The display name of the file - * <P>Type: TEXT</P> */ + @Column(Cursor.FIELD_TYPE_STRING) public static final String DISPLAY_NAME = "_display_name"; /** * The title of the content - * <P>Type: TEXT</P> */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String TITLE = "title"; /** * The time the file was added to the media provider * Units are seconds since 1970. - * <P>Type: INTEGER (long)</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String DATE_ADDED = "date_added"; /** * The time the file was last modified * Units are seconds since 1970. * NOTE: This is for internal use by the media scanner. Do not modify this field. - * <P>Type: INTEGER (long)</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String DATE_MODIFIED = "date_modified"; /** @@ -938,9 +937,8 @@ public final class MediaStore { * {@code format} of {@code audio/ogg} would be ignored. * <p> * This is a read-only column that is automatically computed. - * <p> - * Type: TEXT */ + @Column(Cursor.FIELD_TYPE_STRING) public static final String MIME_TYPE = "mime_type"; /** @@ -948,35 +946,34 @@ public final class MediaStore { * 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. - * <P>Type: INTEGER</P> * @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 - * <P>Type: INTEGER (boolean)</P> * @hide */ @UnsupportedAppUsage + @Deprecated + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String IS_DRM = "is_drm"; /** * Flag indicating if a media item is pending, and still being inserted * by its owner. - * <p> - * Type: BOOLEAN * * @see MediaColumns#IS_PENDING * @see MediaStore#setIncludePending(Uri) * @see MediaStore#createPending(Context, PendingParams) */ + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String IS_PENDING = "is_pending"; /** * Flag indicating if a media item is trashed. - * <p> - * Type: BOOLEAN * * @see MediaColumns#IS_TRASHED * @see MediaStore#setIncludeTrashed(Uri) @@ -985,57 +982,54 @@ public final class MediaStore { * @removed */ @Deprecated + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String IS_TRASHED = "is_trashed"; /** * The time the file should be considered expired. Units are seconds * since 1970. Typically only meaningful in the context of * {@link #IS_PENDING} or {@link #IS_TRASHED}. - * <p> - * Type: INTEGER * @removed */ @Deprecated + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String DATE_EXPIRES = "date_expires"; /** * The width of the image/video in pixels. */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String WIDTH = "width"; /** * The height of the image/video in pixels. */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String HEIGHT = "height"; /** * Package name that contributed this media. The value may be * {@code NULL} if ownership cannot be reliably determined. - * <p> - * This is a read-only column that is automatically computed. - * <p> - * Type: TEXT */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String OWNER_PACKAGE_NAME = "owner_package_name"; /** * The primary directory name this media exists under. The value may be * {@code NULL} if the media doesn't have a primary directory name. - * <p> - * Type: TEXT * * @see PendingParams#setPrimaryDirectory(String) */ + @Column(Cursor.FIELD_TYPE_STRING) 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. - * <p> - * Type: TEXT * * @see PendingParams#setSecondaryDirectory(String) */ + @Column(Cursor.FIELD_TYPE_STRING) public static final String SECONDARY_DIRECTORY = "secondary_directory"; /** @@ -1046,11 +1040,8 @@ public final class MediaStore { * <p> * Each "document ID" is created once for each new resource. Different * renditions of that resource are expected to have different IDs. - * <p> - * This is a read-only column that is automatically computed. - * <p> - * Type: TEXT */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String DOCUMENT_ID = "document_id"; /** @@ -1061,11 +1052,8 @@ public final class MediaStore { * <p> * This "instance ID" changes with each save operation of a specific * "document ID". - * <p> - * This is a read-only column that is automatically computed. - * <p> - * Type: TEXT */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String INSTANCE_ID = "instance_id"; /** @@ -1077,11 +1065,8 @@ public final class MediaStore { * For example, when you save a PSD document as a JPEG, then convert the * JPEG to GIF format, the "original document ID" of both the JPEG and * GIF files is the "document ID" of the original PSD file. - * <p> - * This is a read-only column that is automatically computed. - * <p> - * Type: TEXT */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String ORIGINAL_DOCUMENT_ID = "original_document_id"; } @@ -1172,43 +1157,46 @@ public final class MediaStore { public interface FileColumns extends MediaColumns { /** * The MTP storage ID of the file - * <P>Type: INTEGER</P> * @hide */ @UnsupportedAppUsage + @Deprecated + // @Column(Cursor.FIELD_TYPE_INTEGER) public static final String STORAGE_ID = "storage_id"; /** * The MTP format code of the file - * <P>Type: INTEGER</P> * @hide */ @UnsupportedAppUsage + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String FORMAT = "format"; /** * The index of the parent directory of the file - * <P>Type: INTEGER</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String PARENT = "parent"; /** * The MIME type of the file * <P>Type: TEXT</P> */ + @Column(Cursor.FIELD_TYPE_STRING) public static final String MIME_TYPE = "mime_type"; /** * The title of the content * <P>Type: TEXT</P> */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String TITLE = "title"; /** * The media type (audio, video, image or playlist) * of the file, or 0 for not a media file - * <P>Type: TEXT</P> */ + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String MEDIA_TYPE = "media_type"; /** @@ -1241,6 +1229,7 @@ public final class MediaStore { * Column indicating if the file is part of Downloads collection. * @hide */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String IS_DOWNLOAD = "is_download"; } } @@ -1260,23 +1249,20 @@ public final class MediaStore { public interface DownloadColumns extends MediaColumns { /** * Uri indicating where the file has been downloaded from. - * <p> - * Type: TEXT */ + @Column(Cursor.FIELD_TYPE_STRING) String DOWNLOAD_URI = "download_uri"; /** * Uri indicating HTTP referer of {@link #DOWNLOAD_URI}. - * <p> - * Type: TEXT */ + @Column(Cursor.FIELD_TYPE_STRING) String REFERER_URI = "referer_uri"; /** * The description of the download. - * <p> - * Type: Text */ + @Column(Cursor.FIELD_TYPE_STRING) String DESCRIPTION = "description"; } @@ -1442,29 +1428,28 @@ public final class MediaStore { public interface ImageColumns extends MediaColumns { /** * The description of the image - * <P>Type: TEXT</P> */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String DESCRIPTION = "description"; /** * The picasa id of the image - * <P>Type: TEXT</P> * * @deprecated this value was only relevant for images hosted on * Picasa, which are no longer supported. */ @Deprecated + @Column(Cursor.FIELD_TYPE_STRING) public static final String PICASA_ID = "picasa_id"; /** * Whether the video should be published as public or private - * <P>Type: INTEGER</P> */ + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String IS_PRIVATE = "isprivate"; /** * The latitude where the image was captured. - * <P>Type: DOUBLE</P> * * @deprecated location details are no longer indexed for privacy * reasons, and this value is now always {@code null}. @@ -1472,11 +1457,11 @@ public final class MediaStore { * {@link ExifInterface#getLatLong(float[])}. */ @Deprecated + @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true) public static final String LATITUDE = "latitude"; /** * The longitude where the image was captured. - * <P>Type: DOUBLE</P> * * @deprecated location details are no longer indexed for privacy * reasons, and this value is now always {@code null}. @@ -1484,40 +1469,40 @@ public final class MediaStore { * {@link ExifInterface#getLatLong(float[])}. */ @Deprecated + @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true) public static final String LONGITUDE = "longitude"; /** * The date & time that the image was taken in units * of milliseconds since jan 1, 1970. - * <P>Type: INTEGER</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String DATE_TAKEN = "datetaken"; /** * The orientation for the image expressed as degrees. * Only degrees 0, 90, 180, 270 will work. - * <P>Type: INTEGER</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String ORIENTATION = "orientation"; /** * The mini thumb id. - * <P>Type: INTEGER</P> * * @deprecated all thumbnails should be obtained via * {@link MediaStore.Images.Thumbnails#getThumbnail}, as this * value is no longer supported. */ @Deprecated + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String MINI_THUMB_MAGIC = "mini_thumb_magic"; /** * 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. - * <p> - * Type: INTEGER */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String BUCKET_ID = "bucket_id"; /** @@ -1525,9 +1510,8 @@ public final class MediaStore { * useful to present the user a first-level clustering of related * media items. This is a read-only column that is automatically * computed. - * <p> - * Type: TEXT */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String BUCKET_DISPLAY_NAME = "bucket_display_name"; /** @@ -1541,9 +1525,8 @@ public final class MediaStore { * {@code IMG1024.BURST001.JPG} and {@code IMG1024.BURST002.JPG} * will have the same {@link #GROUP_ID} because the first portion of * their filenames is identical. - * <p> - * Type: INTEGER */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String GROUP_ID = "group_id"; } @@ -1848,8 +1831,6 @@ public final class MediaStore { * apps should use * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain * access. - * <p> - * Type: TEXT * * @deprecated Apps may not have filesystem permissions to directly * access this path. Instead of trying to open this path @@ -1860,39 +1841,45 @@ public final class MediaStore { * {@link android.os.Build.VERSION_CODES#Q} or higher. */ @Deprecated + @Column(Cursor.FIELD_TYPE_STRING) public static final String DATA = "_data"; /** * The original image for the thumbnal - * <P>Type: INTEGER (ID from Images table)</P> */ + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String IMAGE_ID = "image_id"; /** * The kind of the thumbnail - * <P>Type: INTEGER (One of the values below)</P> */ + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String KIND = "kind"; public static final int MINI_KIND = ThumbnailConstants.MINI_KIND; public static final int FULL_SCREEN_KIND = ThumbnailConstants.FULL_SCREEN_KIND; public static final int MICRO_KIND = ThumbnailConstants.MICRO_KIND; + /** * The blob raw data of thumbnail - * <P>Type: DATA STREAM</P> + * + * @deprecated this column never existed internally, and could never + * have returned valid data. */ + @Deprecated + @Column(Cursor.FIELD_TYPE_BLOB) public static final String THUMB_DATA = "thumb_data"; /** * The width of the thumbnal - * <P>Type: INTEGER (long)</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String WIDTH = "width"; /** * The height of the thumbnail - * <P>Type: INTEGER (long)</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String HEIGHT = "height"; } } @@ -1909,79 +1896,80 @@ public final class MediaStore { /** * A non human readable key calculated from the TITLE, used for * searching, sorting and grouping - * <P>Type: TEXT</P> */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String TITLE_KEY = "title_key"; /** * The duration of the audio file, in ms - * <P>Type: INTEGER (long)</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String DURATION = "duration"; /** * The position, in ms, playback was at when playback for this file * was last stopped. - * <P>Type: INTEGER (long)</P> */ + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String BOOKMARK = "bookmark"; /** * The id of the artist who created the audio file, if any - * <P>Type: INTEGER (long)</P> */ + @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 - * <P>Type: TEXT</P> */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String ARTIST = "artist"; /** * The artist credited for the album that contains the audio file - * <P>Type: TEXT</P> * @hide */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String ALBUM_ARTIST = "album_artist"; /** * Whether the song is part of a compilation - * <P>Type: TEXT</P> * @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 - * <P>Type: TEXT</P> */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String ARTIST_KEY = "artist_key"; /** * The composer of the audio file, if any - * <P>Type: TEXT</P> */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String COMPOSER = "composer"; /** * The id of the album the audio file is from, if any - * <P>Type: INTEGER (long)</P> */ + @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 - * <P>Type: TEXT</P> */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String ALBUM = "album"; /** * A non human readable key calculated from the ALBUM, used for * searching, sorting and grouping - * <P>Type: TEXT</P> */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String ALBUM_KEY = "album_key"; /** @@ -1990,63 +1978,63 @@ public final class MediaStore { * disc number. For multi-disc sets, this number will * be 1xxx for tracks on the first disc, 2xxx for tracks * on the second disc, etc. - * <P>Type: INTEGER</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String TRACK = "track"; /** * The year the audio file was recorded, if any - * <P>Type: INTEGER</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String YEAR = "year"; /** * Non-zero if the audio file is music - * <P>Type: INTEGER (boolean)</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String IS_MUSIC = "is_music"; /** * Non-zero if the audio file is a podcast - * <P>Type: INTEGER (boolean)</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String IS_PODCAST = "is_podcast"; /** * Non-zero if the audio file may be a ringtone - * <P>Type: INTEGER (boolean)</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String IS_RINGTONE = "is_ringtone"; /** * Non-zero if the audio file may be an alarm - * <P>Type: INTEGER (boolean)</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String IS_ALARM = "is_alarm"; /** * Non-zero if the audio file may be a notification sound - * <P>Type: INTEGER (boolean)</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String IS_NOTIFICATION = "is_notification"; /** * Non-zero if the audio file is an audiobook - * <P>Type: INTEGER (boolean)</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String IS_AUDIOBOOK = "is_audiobook"; /** * The genre of the audio file, if any - * <P>Type: TEXT</P> * Does not exist in the database - only used by the media scanner for inserts. * @hide */ + @Deprecated + // @Column(Cursor.FIELD_TYPE_STRING) public static final String GENRE = "genre"; /** * The resource URI of a localized title, if any - * <P>Type: TEXT</P> * Conforms to this pattern: * Scheme: {@link ContentResolver.SCHEME_ANDROID_RESOURCE} * Authority: Package Name of ringtone title provider @@ -2054,6 +2042,7 @@ public final class MediaStore { * Second Path Segment: Resource ID of title * @hide */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String TITLE_RESOURCE_URI = "title_resource_uri"; } @@ -2142,6 +2131,7 @@ public final class MediaStore { * @deprecated Apps may not have filesystem permissions to directly * access this path. */ + @Deprecated public static @Nullable Uri getContentUriForPath(@NonNull String path) { return getContentUri(getVolumeName(new File(path))); } @@ -2202,8 +2192,8 @@ public final class MediaStore { public interface GenresColumns { /** * The name of the genre - * <P>Type: TEXT</P> */ + @Column(Cursor.FIELD_TYPE_STRING) public static final String NAME = "name"; } @@ -2287,14 +2277,14 @@ public final class MediaStore { /** * The ID of the audio file - * <P>Type: INTEGER (long)</P> */ + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String AUDIO_ID = "audio_id"; /** * The ID of the genre - * <P>Type: INTEGER (long)</P> */ + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String GENRE_ID = "genre_id"; } } @@ -2305,8 +2295,8 @@ public final class MediaStore { public interface PlaylistsColumns { /** * The name of the playlist - * <P>Type: TEXT</P> */ + @Column(Cursor.FIELD_TYPE_STRING) public static final String NAME = "name"; /** @@ -2317,8 +2307,6 @@ public final class MediaStore { * apps should use * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain * access. - * <p> - * Type: TEXT * * @deprecated Apps may not have filesystem permissions to directly * access this path. Instead of trying to open this path @@ -2329,21 +2317,22 @@ public final class MediaStore { * {@link android.os.Build.VERSION_CODES#Q} or higher. */ @Deprecated + @Column(Cursor.FIELD_TYPE_STRING) public static final String DATA = "_data"; /** * The time the file was added to the media provider * Units are seconds since 1970. - * <P>Type: INTEGER (long)</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String DATE_ADDED = "date_added"; /** * The time the file was last modified * Units are seconds since 1970. * NOTE: This is for internal use by the media scanner. Do not modify this field. - * <P>Type: INTEGER (long)</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String DATE_MODIFIED = "date_modified"; } @@ -2426,6 +2415,7 @@ public final class MediaStore { /** * The ID within the playlist. */ + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String _ID = "_id"; /** @@ -2436,20 +2426,20 @@ public final class MediaStore { /** * The ID of the audio file - * <P>Type: INTEGER (long)</P> */ + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String AUDIO_ID = "audio_id"; /** * The ID of the playlist - * <P>Type: INTEGER (long)</P> */ + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String PLAYLIST_ID = "playlist_id"; /** * The order of the songs in the playlist - * <P>Type: INTEGER (long)></P> */ + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String PLAY_ORDER = "play_order"; /** @@ -2465,25 +2455,27 @@ public final class MediaStore { public interface ArtistColumns { /** * The artist who created the audio file, if any - * <P>Type: TEXT</P> */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String ARTIST = "artist"; /** * A non human readable key calculated from the ARTIST, used for * searching, sorting and grouping - * <P>Type: TEXT</P> */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String ARTIST_KEY = "artist_key"; /** * The number of albums in the database for this artist */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String NUMBER_OF_ALBUMS = "number_of_albums"; /** * The number of albums in the database for this artist */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String NUMBER_OF_TRACKS = "number_of_tracks"; } @@ -2551,34 +2543,34 @@ public final class MediaStore { /** * The id for the album - * <P>Type: INTEGER</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String ALBUM_ID = "album_id"; /** * The album on which the audio file appears, if any - * <P>Type: TEXT</P> */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String ALBUM = "album"; /** * The artist whose songs appear on this album - * <P>Type: TEXT</P> */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String ARTIST = "artist"; /** * The number of songs on this album - * <P>Type: INTEGER</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String NUMBER_OF_SONGS = "numsongs"; /** * This column is available when getting album info via artist, * and indicates the number of songs on the album by the given * artist. - * <P>Type: INTEGER</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String NUMBER_OF_SONGS_FOR_ARTIST = "numsongs_by_artist"; /** @@ -2586,8 +2578,8 @@ public final class MediaStore { * on this album were released. This will often * be the same as {@link #LAST_YEAR}, but for compilation albums * they might differ. - * <P>Type: INTEGER</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String FIRST_YEAR = "minyear"; /** @@ -2595,20 +2587,19 @@ public final class MediaStore { * on this album were released. This will often * be the same as {@link #FIRST_YEAR}, but for compilation albums * they might differ. - * <P>Type: INTEGER</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String LAST_YEAR = "maxyear"; /** * A non human readable key calculated from the ALBUM, used for * searching, sorting and grouping - * <P>Type: TEXT</P> */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String ALBUM_KEY = "album_key"; /** * Cached album art. - * <P>Type: TEXT</P> * * @deprecated Apps may not have filesystem permissions to directly * access this path. Instead of trying to open this path @@ -2619,6 +2610,7 @@ public final class MediaStore { * {@link android.os.Build.VERSION_CODES#Q} or higher. */ @Deprecated + @Column(Cursor.FIELD_TYPE_STRING) public static final String ALBUM_ART = "album_art"; } @@ -2676,6 +2668,43 @@ public final class MediaStore { // Not instantiable. private Radio() { } } + + /** + * This class provides utility methods to obtain thumbnails for various + * {@link Audio} items. + * + * @deprecated Callers should migrate to using + * {@link ContentResolver#loadThumbnail}, since it offers + * richer control over requested thumbnail sizes and + * cancellation behavior. + * @hide + */ + @Deprecated + public static class Thumbnails implements BaseColumns { + /** + * Path to the thumbnail file on disk. + * <p> + * Note that apps may not have filesystem permissions to directly + * access this path. Instead of trying to open this path directly, + * apps should use + * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain + * access. + * + * @deprecated Apps may not have filesystem permissions to directly + * access this path. Instead of trying to open this path + * directly, apps should use + * {@link ContentResolver#loadThumbnail} + * to gain access. This value will always be + * {@code NULL} for apps targeting + * {@link android.os.Build.VERSION_CODES#Q} or higher. + */ + @Deprecated + @Column(Cursor.FIELD_TYPE_STRING) + public static final String DATA = "_data"; + + @Column(Cursor.FIELD_TYPE_INTEGER) + public static final String ALBUM_ID = "album_id"; + } } public static final class Video { @@ -2693,61 +2722,60 @@ public final class MediaStore { /** * The duration of the video file, in ms - * <P>Type: INTEGER (long)</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String DURATION = "duration"; /** * The artist who created the video file, if any - * <P>Type: TEXT</P> */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String ARTIST = "artist"; /** * The album the video file is from, if any - * <P>Type: TEXT</P> */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String ALBUM = "album"; /** * The resolution of the video file, formatted as "XxY" - * <P>Type: TEXT</P> */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String RESOLUTION = "resolution"; /** * The description of the video recording - * <P>Type: TEXT</P> */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String DESCRIPTION = "description"; /** * Whether the video should be published as public or private - * <P>Type: INTEGER</P> */ + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String IS_PRIVATE = "isprivate"; /** * The user-added tags associated with a video - * <P>Type: TEXT</P> */ + @Column(Cursor.FIELD_TYPE_STRING) public static final String TAGS = "tags"; /** * The YouTube category of the video - * <P>Type: TEXT</P> */ + @Column(Cursor.FIELD_TYPE_STRING) public static final String CATEGORY = "category"; /** * The language of the video - * <P>Type: TEXT</P> */ + @Column(Cursor.FIELD_TYPE_STRING) public static final String LANGUAGE = "language"; /** * The latitude where the video was captured. - * <P>Type: DOUBLE</P> * * @deprecated location details are no longer indexed for privacy * reasons, and this value is now always {@code null}. @@ -2755,11 +2783,11 @@ public final class MediaStore { * {@link ExifInterface#getLatLong(float[])}. */ @Deprecated + @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true) public static final String LATITUDE = "latitude"; /** * The longitude where the video was captured. - * <P>Type: DOUBLE</P> * * @deprecated location details are no longer indexed for privacy * reasons, and this value is now always {@code null}. @@ -2767,33 +2795,33 @@ public final class MediaStore { * {@link ExifInterface#getLatLong(float[])}. */ @Deprecated + @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true) public static final String LONGITUDE = "longitude"; /** * The date & time that the video was taken in units * of milliseconds since jan 1, 1970. - * <P>Type: INTEGER</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String DATE_TAKEN = "datetaken"; /** * The mini thumb id. - * <P>Type: INTEGER</P> * * @deprecated all thumbnails should be obtained via * {@link MediaStore.Images.Thumbnails#getThumbnail}, as this * value is no longer supported. */ @Deprecated + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String MINI_THUMB_MAGIC = "mini_thumb_magic"; /** * 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. - * <p> - * Type: INTEGER */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String BUCKET_ID = "bucket_id"; /** @@ -2801,9 +2829,8 @@ public final class MediaStore { * useful to present the user a first-level clustering of related * media items. This is a read-only column that is automatically * computed. - * <p> - * Type: TEXT */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String BUCKET_DISPLAY_NAME = "bucket_display_name"; /** @@ -2817,9 +2844,8 @@ public final class MediaStore { * {@code IMG1024.BURST001.JPG} and {@code IMG1024.BURST002.JPG} * will have the same {@link #GROUP_ID} because the first portion of * their filenames is identical. - * <p> - * Type: INTEGER */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String GROUP_ID = "group_id"; /** @@ -2827,29 +2853,29 @@ public final class MediaStore { * video should start playing at the next time it is opened. If the value is null or * out of the range 0..DURATION-1 then the video should start playing from the * beginning. - * <P>Type: INTEGER</P> */ + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String BOOKMARK = "bookmark"; /** * The standard of color aspects - * <P>Type: INTEGER</P> * @hide */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String COLOR_STANDARD = "color_standard"; /** * The transfer of color aspects - * <P>Type: INTEGER</P> * @hide */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String COLOR_TRANSFER = "color_transfer"; /** * The range of color aspects - * <P>Type: INTEGER</P> * @hide */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String COLOR_RANGE = "color_range"; } @@ -3016,8 +3042,6 @@ public final class MediaStore { /** * Path to the thumbnail file on disk. - * <p> - * Type: TEXT * * @deprecated Apps may not have filesystem permissions to directly * access this path. Instead of trying to open this path @@ -3028,18 +3052,19 @@ public final class MediaStore { * {@link android.os.Build.VERSION_CODES#Q} or higher. */ @Deprecated + @Column(Cursor.FIELD_TYPE_STRING) public static final String DATA = "_data"; /** * The original image for the thumbnal - * <P>Type: INTEGER (ID from Video table)</P> */ + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String VIDEO_ID = "video_id"; /** * The kind of the thumbnail - * <P>Type: INTEGER (One of the values below)</P> */ + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String KIND = "kind"; public static final int MINI_KIND = ThumbnailConstants.MINI_KIND; @@ -3048,14 +3073,14 @@ public final class MediaStore { /** * The width of the thumbnal - * <P>Type: INTEGER (long)</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String WIDTH = "width"; /** * The height of the thumbnail - * <P>Type: INTEGER (long)</P> */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String HEIGHT = "height"; } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 452d09dc2e6e..d925d7ee2676 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1657,6 +1657,30 @@ public final class Settings { public static final String ACTION_STORAGE_VOLUME_ACCESS_SETTINGS = "android.settings.STORAGE_VOLUME_ACCESS_SETTINGS"; + + /** + * Activity Action: Show screen that let user select enable (or disable) Content Capture. + * <p> + * Input: Nothing. + * + * <p> + * Output: {@link android.app.Activity#RESULT_OK} if user enabled Content Capture, + * {@link android.app.Activity#RESULT_CANCELED} if user disabled it, cancelled, or if the caller + * is not the Content Capture service associated with the user. + * + * <p> + * <b>NOTE: </b> Caller should call + * {@link android.view.contentcapture.ContentCaptureManager#isContentCaptureFeatureEnabled()} + * first to check whether the feature is already enabled. + * + * @hide + */ + @SystemApi + @TestApi + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = + "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE"; + // End of Intent actions for Settings /** diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java index 81d066d3d656..8695da237f97 100644 --- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java +++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java @@ -61,8 +61,6 @@ import java.util.List; */ @SystemApi @TestApi -// TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service -// in the same package as the test, and that module is compiled with SDK=test_current public abstract class AugmentedAutofillService extends Service { private static final String TAG = AugmentedAutofillService.class.getSimpleName(); diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java index f2a7a35b2825..b989dd9cd4eb 100644 --- a/core/java/android/service/autofill/augmented/FillCallback.java +++ b/core/java/android/service/autofill/augmented/FillCallback.java @@ -31,8 +31,6 @@ import android.util.Log; */ @SystemApi @TestApi -//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service -//in the same package as the test, and that module is compiled with SDK=test_current public final class FillCallback { private static final String TAG = FillCallback.class.getSimpleName(); diff --git a/core/java/android/service/autofill/augmented/FillController.java b/core/java/android/service/autofill/augmented/FillController.java index d7bc893f884a..67f23d599e1d 100644 --- a/core/java/android/service/autofill/augmented/FillController.java +++ b/core/java/android/service/autofill/augmented/FillController.java @@ -38,10 +38,8 @@ import java.util.List; */ @SystemApi @TestApi -//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service -//in the same package as the test, and that module is compiled with SDK=test_current public final class FillController { - private static final String TAG = "FillController"; + private static final String TAG = FillController.class.getSimpleName(); private final AutofillProxy mProxy; diff --git a/core/java/android/service/autofill/augmented/FillRequest.java b/core/java/android/service/autofill/augmented/FillRequest.java index af9905f480df..9a97bb203f5a 100644 --- a/core/java/android/service/autofill/augmented/FillRequest.java +++ b/core/java/android/service/autofill/augmented/FillRequest.java @@ -31,8 +31,6 @@ import android.view.autofill.AutofillValue; @SystemApi // TODO(b/123100811): pass a requestId and/or sessionId? @TestApi -// TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service -// in the same package as the test, and that module is compiled with SDK=test_current public final class FillRequest { final AutofillProxy mProxy; diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java index f1e904a7d8bc..2ac406c5b08c 100644 --- a/core/java/android/service/autofill/augmented/FillResponse.java +++ b/core/java/android/service/autofill/augmented/FillResponse.java @@ -30,8 +30,6 @@ import java.util.List; */ @SystemApi @TestApi -//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service -//in the same package as the test, and that module is compiled with SDK=test_current public final class FillResponse { private final FillWindow mFillWindow; @@ -53,8 +51,6 @@ public final class FillResponse { */ @SystemApi @TestApi - //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service - //in the same package as the test, and that module is compiled with SDK=test_current public static final class Builder { private FillWindow mFillWindow; diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java index 40e3a1219501..6e06754e7b8a 100644 --- a/core/java/android/service/autofill/augmented/FillWindow.java +++ b/core/java/android/service/autofill/augmented/FillWindow.java @@ -63,10 +63,8 @@ import java.io.PrintWriter; */ @SystemApi @TestApi -//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service -//in the same package as the test, and that module is compiled with SDK=test_current public final class FillWindow implements AutoCloseable { - private static final String TAG = "FillWindow"; + private static final String TAG = FillWindow.class.getSimpleName(); private final Object mLock = new Object(); private final CloseGuard mCloseGuard = CloseGuard.get(); diff --git a/core/java/android/service/autofill/augmented/PresentationParams.java b/core/java/android/service/autofill/augmented/PresentationParams.java index 1fb9032c9af5..334487dd6ab4 100644 --- a/core/java/android/service/autofill/augmented/PresentationParams.java +++ b/core/java/android/service/autofill/augmented/PresentationParams.java @@ -15,32 +15,19 @@ */ package android.service.autofill.augmented; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; import android.graphics.Rect; import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy; -import android.util.DebugUtils; import android.view.View; import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; /** * Abstraction of a "Smart Suggestion" component responsible to embed the autofill UI provided by - * the intelligence service. - * - * <p>The Smart Suggestion can embed the autofill UI in 3 distinct places: - * - * <ul> - * <li>A small area associated with suggestions (like a small strip in the top of the IME), - * returned by {@link #getSuggestionArea()} - * <li>The full area (like the full IME window), returned by {@link #getFullArea()} - * <li>A subset of the aforementioned areas, returned by {@link Area#getSubArea(Rect)} - * </ul> + * the augmented autofill service. * * <p>The Smart Suggestion is represented by a {@link Area} object that contains the * dimensions the smart suggestion window, so the service can use it to calculate the size of the @@ -50,54 +37,8 @@ import java.lang.annotation.RetentionPolicy; */ @SystemApi @TestApi -//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service -//in the same package as the test, and that module is compiled with SDK=test_current public abstract class PresentationParams { - /** - * Flag indicating the Smart Suggestion is hosted in the top of its container. - */ - public static final int FLAG_HINT_GRAVITY_TOP = 0x1; - - /** - * Flag indicating the Smart Suggestion is hosted in the bottom of its container. - */ - public static final int FLAG_HINT_GRAVITY_BOTTOM = 0x2; - - /** - * Flag indicating the Smart Suggestion is hosted in the left of its container. - */ - public static final int FLAG_HINT_GRAVITY_LEFT = 0x4; - - /** - * Flag indicating the Smart Suggestion is hosted in the right of its container. - */ - public static final int FLAG_HINT_GRAVITY_RIGHT = 0x8; - - /** - * Flag indicating the Smart Suggestion is hosted by the IME. - */ - public static final int FLAG_HOST_IME = 0x10; - - /** - * Flag indicating the Smart Suggestion is hosted by the Android System as a floating popup - * window. - */ - public static final int FLAG_HOST_SYSTEM = 0x20; - - /** @hide */ - @IntDef(flag = true, prefix = { "FLAG_" }, value = { - FLAG_HINT_GRAVITY_TOP, - FLAG_HINT_GRAVITY_BOTTOM, - FLAG_HINT_GRAVITY_LEFT, - FLAG_HINT_GRAVITY_RIGHT, - FLAG_HOST_IME, - FLAG_HOST_SYSTEM - }) - @Retention(RetentionPolicy.SOURCE) - @interface Flags {} - - // /** @hide */ PresentationParams() {} @@ -112,40 +53,7 @@ public abstract class PresentationParams { return null; } - /** - * Gets the full area for the of the Smart Suggestion provider. - * - * @return full dimensions, or {@code null} if the Smart Suggestion provider does not support - * embeding the UI on its full area. - */ - @Nullable - public Area getFullArea() { - return null; - } - - /** - * Gets flags associated with the Smart Suggestion. - * - * @return any combination of {@link #FLAG_HINT_GRAVITY_TOP}, - * {@link #FLAG_HINT_GRAVITY_BOTTOM}, {@link #FLAG_HINT_GRAVITY_LEFT}, - * {@link #FLAG_HINT_GRAVITY_RIGHT}, {@link #FLAG_HOST_IME}, or - * {@link #FLAG_HOST_SYSTEM}, - */ - public @Flags int getFlags() { - return 0; - } - - /** @hide */ - void dump(@NonNull String prefix, @NonNull PrintWriter pw) { - final int flags = getFlags(); - if (flags > 0) { - pw.print(prefix); pw.print("flags: "); pw.println(flagsToString(flags)); - } - } - - private static String flagsToString(int flags) { - return DebugUtils.flagsToString(PresentationParams.class, "FLAG_", flags); - } + abstract void dump(String prefix, PrintWriter pw); /** * Area associated with a {@link PresentationParams Smart Suggestions} provider. @@ -154,8 +62,6 @@ public abstract class PresentationParams { */ @SystemApi @TestApi - //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service - //in the same package as the test, and that module is compiled with SDK=test_current public abstract static class Area { /** @hide */ @@ -176,24 +82,6 @@ public abstract class PresentationParams { return mBounds; } - /** - * Gets a subarea limited by given boundaries. - * - * @param bounds boundaries relative to this Area. - * - * @return new subarea, or {@code null} if the Smart Suggestion host does not support such - * subaarea. - * - * @throws IllegalArgumentException if the {@code bounds} is not fully-contained inside this - * full Area. - * - */ - @Nullable - public Area getSubArea(@NonNull Rect bounds) { - // TODO(b/123100712): implement / check boundaries / throw IAE / add unit test - return null; - } - @Override public String toString() { return mBounds.toString(); @@ -220,13 +108,7 @@ public abstract class PresentationParams { } @Override - public int getFlags() { - return FLAG_HOST_SYSTEM | FLAG_HINT_GRAVITY_BOTTOM; - } - - @Override void dump(@NonNull String prefix, @NonNull PrintWriter pw) { - super.dump(prefix, pw); pw.print(prefix); pw.print("area: "); pw.println(mSuggestionArea); } } diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index c98f09e13d97..b60fbc593b1c 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -34,6 +34,7 @@ import android.os.Looper; import android.os.RemoteException; import android.service.autofill.AutofillService; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import android.util.Slog; import android.view.contentcapture.ContentCaptureContext; @@ -49,7 +50,9 @@ import com.android.internal.os.IResultReceiver; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.List; +import java.util.Set; /** * A service used to capture the content of the screen to provide contextual data in other areas of @@ -164,6 +167,19 @@ public abstract class ContentCaptureService extends Service { } /** + * @deprecated use {@link #setContentCaptureWhitelist(Set, Set)} instead + */ + @Deprecated + public final void setContentCaptureWhitelist(@Nullable List<String> packages, + @Nullable List<ComponentName> activities) { + setContentCaptureWhitelist(toSet(packages), toSet(activities)); + } + + private <T> ArraySet<T> toSet(@Nullable List<T> set) { + return set == null ? null : new ArraySet<T>(set); + } + + /** * Explicitly limits content capture to the given packages and activities. * * <p>To reset the whitelist, call it passing {@code null} to both arguments. @@ -171,24 +187,29 @@ public abstract class ContentCaptureService extends Service { * <p>Useful when the service wants to restrict content capture to a category of apps, like * chat apps. For example, if the service wants to support view captures on all activities of * app {@code ChatApp1} and just activities {@code act1} and {@code act2} of {@code ChatApp2}, - * it would call: {@code setContentCaptureWhitelist(Arrays.asList("ChatApp1"), - * Arrays.asList(new ComponentName("ChatApp2", "act1"), + * it would call: {@code setContentCaptureWhitelist(Sets.newArraySet("ChatApp1"), + * Sets.newArraySet(new ComponentName("ChatApp2", "act1"), * new ComponentName("ChatApp2", "act2")));} */ - public final void setContentCaptureWhitelist(@Nullable List<String> packages, - @Nullable List<ComponentName> activities) { + public final void setContentCaptureWhitelist(@Nullable Set<String> packages, + @Nullable Set<ComponentName> activities) { final IContentCaptureServiceCallback callback = mCallback; if (callback == null) { Log.w(TAG, "setContentCaptureWhitelist(): no server callback"); return; } + try { - callback.setContentCaptureWhitelist(packages, activities); + callback.setContentCaptureWhitelist(toList(packages), toList(activities)); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } + private <T> ArrayList<T> toList(@Nullable Set<T> set) { + return set == null ? null : new ArrayList<T>(set); + } + /** * Called when the Android system connects to service. * @@ -339,7 +360,7 @@ public abstract class ContentCaptureService extends Service { } switch (event.getType()) { case ContentCaptureEvent.TYPE_SESSION_STARTED: - final ContentCaptureContext clientContext = event.getClientContext(); + final ContentCaptureContext clientContext = event.getContentCaptureContext(); clientContext.setParentSessionId(event.getParentSessionId()); mSessionUids.put(sessionIdString, uid); onCreateContentCaptureSession(clientContext, sessionId); @@ -383,8 +404,8 @@ public abstract class ContentCaptureService extends Service { } final Integer rightUid = mSessionUids.get(sessionId); if (rightUid == null) { - if (DEBUG) { - Log.d(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId + if (VERBOSE) { + Log.v(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId + ": " + mSessionUids); } // Just ignore, as the session could have been finished already diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java index 93189b310485..2961426278dc 100644 --- a/core/java/android/service/notification/Adjustment.java +++ b/core/java/android/service/notification/Adjustment.java @@ -15,6 +15,7 @@ */ package android.service.notification; +import android.annotation.SystemApi; import android.app.Notification; import android.os.Bundle; import android.os.Parcel; @@ -36,13 +37,13 @@ public final class Adjustment implements Parcelable { * See {@link android.app.Notification.Builder#addPerson(String)}. * @hide */ + @SystemApi public static final String KEY_PEOPLE = "key_people"; /** * Parcelable {@code ArrayList} of {@link SnoozeCriterion}. These criteria may be visible to * users. If a user chooses to snooze a notification until one of these criterion, the * assistant will be notified via * {@link NotificationAssistantService#onNotificationSnoozedUntilContext}. - * @hide */ public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria"; /** @@ -111,7 +112,11 @@ public final class Adjustment implements Parcelable { mUser = user; } - private Adjustment(Parcel in) { + /** + * @hide + */ + @SystemApi + protected Adjustment(Parcel in) { if (in.readInt() == 1) { mPackage = in.readString(); } else { diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java index e93b1580bc66..a69724860634 100644 --- a/core/java/android/service/notification/NotificationAssistantService.java +++ b/core/java/android/service/notification/NotificationAssistantService.java @@ -21,6 +21,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SdkConstant; +import android.annotation.SystemApi; import android.app.Notification; import android.app.NotificationChannel; import android.app.admin.DevicePolicyManager; @@ -103,7 +104,6 @@ public abstract class NotificationAssistantService extends NotificationListenerS * * @param sbn the notification to snooze * @param snoozeCriterionId the {@link SnoozeCriterion#getId()} representing a device context. - * @hide */ abstract public void onNotificationSnoozedUntilContext(StatusBarNotification sbn, String snoozeCriterionId); @@ -111,12 +111,13 @@ public abstract class NotificationAssistantService extends NotificationListenerS /** * A notification was posted by an app. Called before post. * + * <p>Note: this method is only called if you don't override + * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel)}.</p> + * * @param sbn the new notification * @return an adjustment or null to take no action, within 100ms. */ - public Adjustment onNotificationEnqueued(StatusBarNotification sbn) { - return null; - } + abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn); /** * A notification was posted by an app. Called before post. @@ -240,12 +241,11 @@ public abstract class NotificationAssistantService extends NotificationListenerS /** * Inform the notification manager about un-snoozing a specific notification. * <p> - * This should only be used for notifications snoozed by this listener using - * {@link #snoozeNotification(String, String)}. Once un-snoozed, you will get a + * This should only be used for notifications snoozed because of a contextual snooze suggestion + * you provided via {@link Adjustment#KEY_SNOOZE_CRITERIA}. Once un-snoozed, you will get a * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the * notification. * @param key The key of the notification to snooze - * @hide */ public final void unsnoozeNotification(String key) { if (!isBound()) return; diff --git a/core/java/android/service/notification/NotificationStats.java b/core/java/android/service/notification/NotificationStats.java index 814b4772a395..ebfabbfda326 100644 --- a/core/java/android/service/notification/NotificationStats.java +++ b/core/java/android/service/notification/NotificationStats.java @@ -16,6 +16,7 @@ package android.service.notification; import android.annotation.IntDef; +import android.annotation.SystemApi; import android.app.RemoteInput; import android.os.Parcel; import android.os.Parcelable; @@ -98,7 +99,11 @@ public final class NotificationStats implements Parcelable { public NotificationStats() { } - private NotificationStats(Parcel in) { + /** + * @hide + */ + @SystemApi + protected NotificationStats(Parcel in) { mSeen = in.readByte() != 0; mExpanded = in.readByte() != 0; mDirectReplied = in.readByte() != 0; diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 551bb8ada044..954dc3943019 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -372,6 +372,13 @@ public class StatusBarNotification implements Parcelable { /** * @hide */ + public void clearPackageContext() { + mContext = null; + } + + /** + * @hide + */ @UnsupportedAppUsage public Context getPackageContext(Context context) { if (mContext == null) { diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index ea2a25d52434..e3e63e539591 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -342,37 +342,17 @@ public class VoiceInteractionService extends Service { } /** - * Requests that the voice state UI indicate the given state. + * Provide hints to be reflected in the system UI. * - * @param state value indicating whether the assistant is listening, fulfilling, etc. + * @param hints Arguments used to show UI. */ - public final void setVoiceState(int state) { - try { - mSystemService.setVoiceState(mInterface, state); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + public final void setUiHints(@NonNull Bundle hints) { + if (hints == null) { + throw new IllegalArgumentException("Hints must be non-null"); } - } - /** - * Displays the given voice transcription contents. - */ - public final void setTranscription(@NonNull String transcription) { - try { - mSystemService.setTranscription(mInterface, transcription); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Hides transcription. - * - * @param immediate if {@code true}, remove before transcription animation completes. - */ - public final void clearTranscription(boolean immediate) { try { - mSystemService.clearTranscription(mInterface, immediate); + mSystemService.setUiHints(mInterface, hints); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 1ca6398e3bc0..67a40153f5b1 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -54,9 +54,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_network_and_internet_v2", "false"); DEFAULT_FLAGS.put("settings_slice_injection", "true"); DEFAULT_FLAGS.put("settings_systemui_theme", "true"); - DEFAULT_FLAGS.put("settings_wifi_dpp", "true"); DEFAULT_FLAGS.put("settings_wifi_mac_randomization", "true"); - DEFAULT_FLAGS.put("settings_wifi_sharing", "true"); DEFAULT_FLAGS.put("settings_mainline_module", "false"); DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false"); DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false"); diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index 62ed901c5e06..f37c9162d98a 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -336,7 +336,7 @@ public final class AccessibilityInteractionController { } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; final View root = findViewByAccessibilityId(accessibilityViewId); - if (root != null) { + if (root != null && isShown(root)) { mPrefetcher.prefetchAccessibilityNodeInfos( root, virtualDescendantId, flags, infos, arguments); } @@ -448,7 +448,7 @@ public final class AccessibilityInteractionController { } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; final View root = findViewByAccessibilityId(accessibilityViewId); - if (root != null) { + if (root != null && isShown(root)) { AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider(); if (provider != null) { infos = provider.findAccessibilityNodeInfosByText(text, @@ -531,7 +531,7 @@ public final class AccessibilityInteractionController { } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; final View root = findViewByAccessibilityId(accessibilityViewId); - if (root != null) { + if (root != null && isShown(root)) { switch (focusType) { case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: { View host = mViewRootImpl.mAccessibilityFocusedHost; @@ -621,7 +621,7 @@ public final class AccessibilityInteractionController { } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; final View root = findViewByAccessibilityId(accessibilityViewId); - if (root != null) { + if (root != null && isShown(root)) { View nextView = root.focusSearch(direction); if (nextView != null) { next = nextView.createAccessibilityNodeInfo(); @@ -676,7 +676,7 @@ public final class AccessibilityInteractionController { } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; final View target = findViewByAccessibilityId(accessibilityViewId); - if (target != null) { + if (target != null && isShown(target)) { if (action == R.id.accessibilityActionClickOnClickableSpan) { // Handle this hidden action separately succeeded = handleClickableSpanActionUiThread( @@ -759,9 +759,7 @@ public final class AccessibilityInteractionController { if (accessibilityId == AccessibilityNodeInfo.ROOT_ITEM_ID) { return mViewRootImpl.mView; } else { - final View foundView = - AccessibilityNodeIdManager.getInstance().findView(accessibilityId); - return isShown(foundView) ? foundView : null; + return AccessibilityNodeIdManager.getInstance().findView(accessibilityId); } } diff --git a/core/java/android/view/ContextThemeWrapper.java b/core/java/android/view/ContextThemeWrapper.java index c77500a9fd70..696e048ffed8 100644 --- a/core/java/android/view/ContextThemeWrapper.java +++ b/core/java/android/view/ContextThemeWrapper.java @@ -16,6 +16,7 @@ package android.view; +import android.annotation.Nullable; import android.annotation.StyleRes; import android.annotation.UnsupportedAppUsage; import android.content.Context; @@ -23,6 +24,7 @@ import android.content.ContextWrapper; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; +import android.os.Build; /** * A context wrapper that allows you to modify or replace the theme of the @@ -31,7 +33,7 @@ import android.content.res.Resources; public class ContextThemeWrapper extends ContextWrapper { @UnsupportedAppUsage private int mThemeResource; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768723) private Resources.Theme mTheme; @UnsupportedAppUsage private LayoutInflater mInflater; @@ -146,6 +148,15 @@ public class ContextThemeWrapper extends ContextWrapper { } } + /** + * Set the configure the current theme. If null is provided then the default Theme is returned + * on the next call to {@link #getTheme()} + * @param theme Theme to consume in the wrapper, a value of null resets the theme to the default + */ + public void setTheme(@Nullable Resources.Theme theme) { + mTheme = theme; + } + /** @hide */ @Override @UnsupportedAppUsage diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index 1dbc46b3e883..6cfc9f22a692 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -25,6 +25,7 @@ import android.graphics.Paint; import android.graphics.RecordingCanvas; import android.graphics.RenderNode; import android.os.Build; +import android.os.Handler; import android.util.SparseIntArray; import com.android.internal.util.VirtualRefBasePtr; @@ -84,6 +85,7 @@ public class RenderNodeAnimator extends Animator { private VirtualRefBasePtr mNativePtr; + private Handler mHandler; private RenderNode mTarget; private View mViewTarget; private int mRenderProperty = -1; @@ -222,6 +224,9 @@ public class RenderNodeAnimator extends Animator { private void moveToRunningState() { mState = STATE_RUNNING; if (mNativePtr != null) { + if (mHandler == null) { + mHandler = new Handler(); + } nStart(mNativePtr.get()); } notifyStartListeners(); @@ -497,7 +502,7 @@ public class RenderNodeAnimator extends Animator { // Called by native @UnsupportedAppUsage private static void callOnFinished(RenderNodeAnimator animator) { - animator.onFinished(); + animator.mHandler.post(animator::onFinished); } @Override diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 47b206ca0dca..20978127f510 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -453,7 +453,7 @@ public final class ThreadedRenderer extends HardwareRenderer { */ void destroyHardwareResources(View view) { destroyResources(view); - destroyHardwareResources(); + clearContent(); } private static void destroyResources(View view) { @@ -735,7 +735,9 @@ public final class ThreadedRenderer extends HardwareRenderer { if (callback != null) { setFrameCallback(callback); } - syncAndDrawFrame(vsync); + createRenderRequest() + .setVsyncTime(vsync) + .syncAndDraw(); } } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ba1b51780291..2b440dc80cfd 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9487,7 +9487,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Gets the session used to notify Content Capture events. * * @return session explicitly set by {@link #setContentCaptureSession(ContentCaptureSession)}, - * inherited by ancestore, default session or {@code null} if content capture is disabled for + * inherited by ancestors, default session or {@code null} if content capture is disabled for * this view. */ @Nullable diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 1a782ee5d3dd..b1fee2d17079 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -3404,21 +3404,25 @@ public final class ViewRootImpl implements ViewParent, .captureFrameCommitCallbacks(); if (mReportNextDraw) { usingAsyncReport = true; - mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> { - // TODO: Use the frame number - pendingDrawFinished(); - if (commitCallbacks != null) { - for (int i = 0; i < commitCallbacks.size(); i++) { - commitCallbacks.get(i).run(); - } - } - }); + final Handler handler = mAttachInfo.mHandler; + mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> + handler.post(() -> { + // TODO: Use the frame number + pendingDrawFinished(); + if (commitCallbacks != null) { + for (int i = 0; i < commitCallbacks.size(); i++) { + commitCallbacks.get(i).run(); + } + } + })); } else if (commitCallbacks != null && commitCallbacks.size() > 0) { - mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> { - for (int i = 0; i < commitCallbacks.size(); i++) { - commitCallbacks.get(i).run(); - } - }); + final Handler handler = mAttachInfo.mHandler; + mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> + handler.post(() -> { + for (int i = 0; i < commitCallbacks.size(); i++) { + commitCallbacks.get(i).run(); + } + })); } } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 5e5c8265f782..a6b40ed55ac5 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -78,6 +78,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Set; //TODO: use java.lang.ref.Cleaner once Android supports Java 9 import sun.misc.Cleaner; @@ -1780,12 +1781,20 @@ public final class AutofillManager { } /** + * @deprecated use {@link #setAugmentedAutofillWhitelist(Set, Set)} instead. + * @hide + */ + @SystemApi + @TestApi + @Deprecated + public void setAugmentedAutofillWhitelist(@Nullable List<String> packages, + @Nullable List<ComponentName> activities) { + // TODO(b/123100824): implement + } + + /** * Explicitly limits augmented autofill to the given packages and activities. * - * <p>When the whitelist is set, it overrides the values passed to - * {@link #setActivityAugmentedAutofillEnabled(ComponentName, boolean)} - * and {@link #setPackageAugmentedAutofillEnabled(String, boolean)}. - * * <p>To reset the whitelist, call it passing {@code null} to both arguments. * * <p>Useful when the service wants to restrict augmented autofill to a category of apps, like @@ -1803,10 +1812,8 @@ public final class AutofillManager { */ @SystemApi @TestApi - //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service - //in the same package as the test, and that module is compiled with SDK=test_current - public void setAugmentedAutofillWhitelist(@Nullable List<String> packages, - @Nullable List<ComponentName> activities) { + public void setAugmentedAutofillWhitelist(@Nullable Set<String> packages, + @Nullable Set<ComponentName> activities) { // TODO(b/123100824): implement } diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java index acb81e086461..13e8a6584218 100644 --- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java @@ -20,10 +20,6 @@ import android.annotation.Nullable; import android.view.autofill.AutofillId; import android.view.contentcapture.ViewNode.ViewStructureImpl; -import com.android.internal.util.Preconditions; - -import java.io.PrintWriter; - /** * A session that is explicitly created by the app (and hence is a descendant of * {@link MainContentCaptureSession}). @@ -35,21 +31,11 @@ final class ChildContentCaptureSession extends ContentCaptureSession { @NonNull private final ContentCaptureSession mParent; - /** - * {@link ContentCaptureContext} set by client, or {@code null} when it's the - * {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the - * context. - * - * @hide - */ - @NonNull - private final ContentCaptureContext mClientContext; - /** @hide */ protected ChildContentCaptureSession(@NonNull ContentCaptureSession parent, @NonNull ContentCaptureContext clientContext) { + super(clientContext); mParent = parent; - mClientContext = Preconditions.checkNotNull(clientContext); } @Override @@ -73,6 +59,11 @@ final class ChildContentCaptureSession extends ContentCaptureSession { } @Override + public void updateContentCaptureContext(@Nullable ContentCaptureContext context) { + getMainCaptureSession().notifyContextUpdated(mId, context); + } + + @Override void onDestroy() { getMainCaptureSession().notifyChildSessionFinished(mParent.mId, mId); } @@ -101,13 +92,4 @@ final class ChildContentCaptureSession extends ContentCaptureSession { boolean isContentCaptureEnabled() { return getMainCaptureSession().isContentCaptureEnabled(); } - - @Override - void dump(String prefix, PrintWriter pw) { - if (mClientContext != null) { - // NOTE: we don't dump clientContent because it could have PII - pw.print(prefix); pw.println("hasClientContext"); - } - super.dump(prefix, pw); - } } diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java index 22254cd94059..9cdbefac3d1d 100644 --- a/core/java/android/view/contentcapture/ContentCaptureEvent.java +++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java @@ -91,13 +91,22 @@ public final class ContentCaptureEvent implements Parcelable { */ public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; + /** + * Called after a call to + * {@link ContentCaptureSession#setContentCaptureContext(ContentCaptureContext)}. + * + * <p>The passed context is available through {@link #getContentCaptureContext()}. + */ + public static final int TYPE_CONTEXT_UPDATED = 6; + /** @hide */ @IntDef(prefix = { "TYPE_" }, value = { TYPE_VIEW_APPEARED, TYPE_VIEW_DISAPPEARED, TYPE_VIEW_TEXT_CHANGED, TYPE_INITIAL_VIEW_TREE_APPEARING, - TYPE_INITIAL_VIEW_TREE_APPEARED + TYPE_INITIAL_VIEW_TREE_APPEARED, + TYPE_CONTEXT_UPDATED }) @Retention(RetentionPolicy.SOURCE) public @interface EventType{} @@ -193,12 +202,13 @@ public final class ContentCaptureEvent implements Parcelable { } /** - * Used by {@link #TYPE_SESSION_STARTED}. + * Gets the {@link ContentCaptureContext} set calls to + * {@link ContentCaptureSession#setContentCaptureContext(ContentCaptureContext)}. * - * @hide + * <p>Only set on {@link #TYPE_CONTEXT_UPDATED} events. */ @Nullable - public ContentCaptureContext getClientContext() { + public ContentCaptureContext getContentCaptureContext() { return mClientContext; } @@ -220,8 +230,8 @@ public final class ContentCaptureEvent implements Parcelable { * Gets the type of the event. * * @return one of {@link #TYPE_VIEW_APPEARED}, {@link #TYPE_VIEW_DISAPPEARED}, - * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_INITIAL_VIEW_TREE_APPEARING}, or - * {@link #TYPE_INITIAL_VIEW_TREE_APPEARED}. + * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_INITIAL_VIEW_TREE_APPEARING}, + * {@link #TYPE_INITIAL_VIEW_TREE_APPEARED}, or {@link #TYPE_CONTEXT_UPDATED}. */ public @EventType int getType() { return mType; @@ -299,6 +309,10 @@ public final class ContentCaptureEvent implements Parcelable { if (mText != null) { pw.print(", text="); pw.println(getSanitizedString(mText)); } + if (mClientContext != null) { + pw.print(", context="); mClientContext.dump(pw); pw.println(); + + } } @Override @@ -325,6 +339,9 @@ public final class ContentCaptureEvent implements Parcelable { if (mText != null) { string.append(", text=").append(getSanitizedString(mText)); } + if (mClientContext != null) { + string.append(", context=").append(mClientContext); + } return string.append(']').toString(); } @@ -345,7 +362,7 @@ public final class ContentCaptureEvent implements Parcelable { if (mType == TYPE_SESSION_STARTED || mType == TYPE_SESSION_FINISHED) { parcel.writeString(mParentSessionId); } - if (mType == TYPE_SESSION_STARTED) { + if (mType == TYPE_SESSION_STARTED || mType == TYPE_CONTEXT_UPDATED) { parcel.writeParcelable(mClientContext, flags); } } @@ -375,7 +392,7 @@ public final class ContentCaptureEvent implements Parcelable { if (type == TYPE_SESSION_STARTED || type == TYPE_SESSION_FINISHED) { event.setParentSessionId(parcel.readString()); } - if (type == TYPE_SESSION_STARTED) { + if (type == TYPE_SESSION_STARTED || type == TYPE_CONTEXT_UPDATED) { event.setClientContext(parcel.readParcelable(null)); } return event; @@ -404,6 +421,8 @@ public final class ContentCaptureEvent implements Parcelable { return "INITIAL_VIEW_HIERARCHY_STARTED"; case TYPE_INITIAL_VIEW_TREE_APPEARED: return "INITIAL_VIEW_HIERARCHY_FINISHED"; + case TYPE_CONTEXT_UPDATED: + return "CONTEXT_UPDATED"; default: return "UKNOWN_TYPE: " + type; } diff --git a/core/java/android/view/contentcapture/ContentCaptureHelper.java b/core/java/android/view/contentcapture/ContentCaptureHelper.java index 508880feb3c3..1cf27fc56a8c 100644 --- a/core/java/android/view/contentcapture/ContentCaptureHelper.java +++ b/core/java/android/view/contentcapture/ContentCaptureHelper.java @@ -15,16 +15,29 @@ */ package android.view.contentcapture; +import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL; +import static android.view.contentcapture.ContentCaptureManager.LOGGING_LEVEL_DEBUG; +import static android.view.contentcapture.ContentCaptureManager.LOGGING_LEVEL_OFF; +import static android.view.contentcapture.ContentCaptureManager.LOGGING_LEVEL_VERBOSE; + +import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Build; +import android.provider.DeviceConfig; +import android.util.Log; +import android.view.contentcapture.ContentCaptureManager.LoggingLevel; /** - * Helpe class for this package. + * Helper class for this package and server's. + * + * @hide */ -final class ContentCaptureHelper { +public final class ContentCaptureHelper { - // TODO(b/121044306): define a way to dynamically set them(for example, using settings?) - static final boolean VERBOSE = false; - static final boolean DEBUG = true; // STOPSHIP if not set to false + private static final String TAG = ContentCaptureHelper.class.getSimpleName(); + + public static boolean sVerbose = false; + public static boolean sDebug = true; /** * Used to log text that could contain PII. @@ -34,6 +47,61 @@ final class ContentCaptureHelper { return text == null ? null : text.length() + "_chars"; } + /** + * Gets the value of a device config property from the Content Capture namespace. + */ + public static int getIntDeviceConfigProperty(@NonNull String key, int defaultValue) { + final String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, key); + if (value == null) return defaultValue; + + try { + return Integer.parseInt(value); + } catch (Exception e) { + Log.w(TAG, "error parsing value (" + value + ") of property " + key + ": " + e); + return defaultValue; + } + } + + /** + * Sets the value of the static logging level constants based on device config. + */ + public static void setLoggingLevel() { + final int defaultLevel = Build.IS_DEBUGGABLE ? LOGGING_LEVEL_DEBUG : LOGGING_LEVEL_OFF; + final int level = getIntDeviceConfigProperty(DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL, + defaultLevel); + Log.i(TAG, "Setting logging level to " + getLoggingLevelAsString(level)); + sVerbose = sDebug = false; + switch (level) { + case LOGGING_LEVEL_VERBOSE: + sVerbose = true; + // fall through + case LOGGING_LEVEL_DEBUG: + sDebug = true; + return; + case LOGGING_LEVEL_OFF: + // You log nothing, Jon Snow! + return; + default: + Log.w(TAG, "setLoggingLevel(): invalud level: " + level); + } + } + + /** + * Gets a user-friendly value for a content capture logging level. + */ + public static String getLoggingLevelAsString(@LoggingLevel int level) { + switch (level) { + case LOGGING_LEVEL_OFF: + return "OFF"; + case LOGGING_LEVEL_DEBUG: + return "DEBUG"; + case LOGGING_LEVEL_VERBOSE: + return "VERBOSE"; + default: + return "UNKNOWN-" + level; + } + } + private ContentCaptureHelper() { throw new UnsupportedOperationException("contains only static methods"); } diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index 634443d78b49..99063083e229 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -15,9 +15,10 @@ */ package android.view.contentcapture; -import static android.view.contentcapture.ContentCaptureHelper.DEBUG; -import static android.view.contentcapture.ContentCaptureHelper.VERBOSE; +import static android.view.contentcapture.ContentCaptureHelper.sDebug; +import static android.view.contentcapture.ContentCaptureHelper.sVerbose; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -38,16 +39,11 @@ import com.android.internal.util.Preconditions; import com.android.internal.util.SyncResultReceiver; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; -/* - * NOTE: all methods in this class should return right away, or do the real work in a handler - * thread. - * - * Hence, the only field that must be thread-safe is mEnabled, which is called at the beginning - * of every method. - */ /** - * TODO(b/123577059): add javadocs / implement + * TODO(b/123577059): add javadocs / mention it can be null */ @SystemService(Context.CONTENT_CAPTURE_MANAGER_SERVICE) public final class ContentCaptureManager { @@ -85,12 +81,81 @@ public final class ContentCaptureManager { public static final String DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED = "service_explicitly_enabled"; + /** + * Maximum number of events that are buffered before sent to the app. + * + * @hide + */ + @TestApi + public static final String DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE = "max_buffer_size"; + + /** + * Frequency (in ms) of buffer flushes when no events are received. + * + * @hide + */ + @TestApi + public static final String DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY = "idle_flush_frequency"; + + /** + * Frequency (in ms) of buffer flushes when no events are received and the last one was a + * text change event. + * + * @hide + */ + @TestApi + public static final String DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY = + "text_change_flush_frequency"; + + /** + * Size of events that are logging on {@code dump}. + * + * <p>Set it to {@code 0} or less to disable history. + * + * @hide + */ + @TestApi + public static final String DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE = "log_history_size"; + + /** + * Sets the logging level for {@code logcat} statements. + * + * <p>Valid values are: {@link #LOGGING_LEVEL_OFF}, {@value #LOGGING_LEVEL_DEBUG}, and + * {@link #LOGGING_LEVEL_VERBOSE}. + * + * @hide + */ + @TestApi + public static final String DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL = "logging_level"; + + + /** @hide */ + @TestApi + public static final int LOGGING_LEVEL_OFF = 0; + + /** @hide */ + @TestApi + public static final int LOGGING_LEVEL_DEBUG = 1; + + /** @hide */ + @TestApi + public static final int LOGGING_LEVEL_VERBOSE = 2; + + /** @hide */ + @IntDef(flag = false, value = { + LOGGING_LEVEL_OFF, + LOGGING_LEVEL_DEBUG, + LOGGING_LEVEL_VERBOSE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface LoggingLevel {} + private final Object mLock = new Object(); @NonNull private final Context mContext; - @Nullable + @NonNull private final IContentCaptureManager mService; // Flags used for starting session. @@ -107,11 +172,17 @@ public final class ContentCaptureManager { /** @hide */ public ContentCaptureManager(@NonNull Context context, - @Nullable IContentCaptureManager service) { + @NonNull IContentCaptureManager service) { mContext = Preconditions.checkNotNull(context, "context cannot be null"); - if (VERBOSE) Log.v(TAG, "Constructor for " + context.getPackageName()); + mService = Preconditions.checkNotNull(service, "service cannot be null"); + + // TODO(b/123096662): right now we're reading the device config values here, but ideally + // it should be read on ContentCaptureManagerService and passed back when the activity + // started. + ContentCaptureHelper.setLoggingLevel(); + + if (sVerbose) Log.v(TAG, "Constructor for " + context.getPackageName()); - mService = service; // TODO(b/119220549): we might not even need a handler, as the IPCs are oneway. But if we // do, then we should optimize it to run the tests after the Choreographer finishes the most // important steps of the frame. @@ -133,7 +204,7 @@ public final class ContentCaptureManager { synchronized (mLock) { if (mMainSession == null) { mMainSession = new MainContentCaptureSession(mContext, this, mHandler, mService); - if (VERBOSE) Log.v(TAG, "getMainContentCaptureSession(): created " + mMainSession); + if (sVerbose) Log.v(TAG, "getMainContentCaptureSession(): created " + mMainSession); } return mMainSession; } @@ -199,8 +270,6 @@ public final class ContentCaptureManager { * </ul> */ public boolean isContentCaptureEnabled() { - if (mService == null) return false; - final MainContentCaptureSession mainSession; synchronized (mLock) { mainSession = mMainSession; @@ -219,7 +288,7 @@ public final class ContentCaptureManager { * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}. */ public void setContentCaptureEnabled(boolean enabled) { - if (DEBUG) { + if (sDebug) { Log.d(TAG, "setContentCaptureEnabled(): setting to " + enabled + " for " + mContext); } @@ -242,8 +311,6 @@ public final class ContentCaptureManager { @SystemApi @TestApi public boolean isContentCaptureFeatureEnabled() { - if (mService == null) return false; - final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); final int resultCode; try { @@ -275,7 +342,7 @@ public final class ContentCaptureManager { @SystemApi @TestApi public void setContentCaptureFeatureEnabled(boolean enabled) { - if (DEBUG) Log.d(TAG, "setContentCaptureFeatureEnabled(): setting to " + enabled); + if (sDebug) Log.d(TAG, "setContentCaptureFeatureEnabled(): setting to " + enabled); final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); final int resultCode; @@ -314,22 +381,23 @@ public final class ContentCaptureManager { /** @hide */ public void dump(String prefix, PrintWriter pw) { + pw.print(prefix); pw.println("ContentCaptureManager"); + final String prefix2 = prefix + " "; synchronized (mLock) { - pw.print(prefix); pw.println("ContentCaptureManager"); - pw.print(prefix); pw.print("isContentCaptureEnabled(): "); + pw.print(prefix2); pw.print("isContentCaptureEnabled(): "); pw.println(isContentCaptureEnabled()); + pw.print(prefix); pw.print("Debug: "); pw.print(sDebug); + pw.print(" Verbose: "); pw.println(sVerbose); pw.print(prefix); pw.print("Context: "); pw.println(mContext); pw.print(prefix); pw.print("User: "); pw.println(mContext.getUserId()); - if (mService != null) { - pw.print(prefix); pw.print("Service: "); pw.println(mService); - } + pw.print(prefix); pw.print("Service: "); pw.println(mService); pw.print(prefix); pw.print("Flags: "); pw.println(mFlags); if (mMainSession != null) { - final String prefix2 = prefix + " "; - pw.print(prefix); pw.println("Main session:"); - mMainSession.dump(prefix2, pw); + final String prefix3 = prefix2 + " "; + pw.print(prefix2); pw.println("Main session:"); + mMainSession.dump(prefix3, pw); } else { - pw.print(prefix); pw.println("No sessions"); + pw.print(prefix2); pw.println("No sessions"); } } } diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index e028961692f9..1e051a43a42f 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -15,8 +15,8 @@ */ package android.view.contentcapture; -import static android.view.contentcapture.ContentCaptureHelper.DEBUG; -import static android.view.contentcapture.ContentCaptureHelper.VERBOSE; +import static android.view.contentcapture.ContentCaptureHelper.sDebug; +import static android.view.contentcapture.ContentCaptureHelper.sVerbose; import android.annotation.CallSuper; import android.annotation.IntDef; @@ -120,6 +120,13 @@ public abstract class ContentCaptureSession implements AutoCloseable { */ public static final int STATE_INTERNAL_ERROR = 0x100; + /** + * Session is disabled because service didn't whitelist package. + * + * @hide + */ + public static final int STATE_PACKAGE_NOT_WHITELISTED = 0x200; + private static final int INITIAL_CHILDREN_CAPACITY = 5; /** @hide */ @@ -166,6 +173,14 @@ public abstract class ContentCaptureSession implements AutoCloseable { private ContentCaptureSessionId mContentCaptureSessionId; /** + * {@link ContentCaptureContext} set by client, or {@code null} when it's the + * {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the + * context. + */ + @Nullable + private ContentCaptureContext mClientContext; + + /** * List of children session. */ @Nullable @@ -183,6 +198,12 @@ public abstract class ContentCaptureSession implements AutoCloseable { mId = Preconditions.checkNotNull(id); } + // Used by ChildCOntentCaptureSession + ContentCaptureSession(@NonNull ContentCaptureContext initialContext) { + this(); + mClientContext = Preconditions.checkNotNull(initialContext); + } + /** @hide */ @NonNull abstract MainContentCaptureSession getMainCaptureSession(); @@ -219,7 +240,7 @@ public abstract class ContentCaptureSession implements AutoCloseable { public final ContentCaptureSession createContentCaptureSession( @NonNull ContentCaptureContext context) { final ContentCaptureSession child = newChild(context); - if (DEBUG) { + if (sDebug) { Log.d(TAG, "createContentCaptureSession(" + context + ": parent=" + mId + ", child=" + child.mId); } @@ -240,6 +261,30 @@ public abstract class ContentCaptureSession implements AutoCloseable { abstract void flush(@FlushReason int reason); /** + * Sets the {@link ContentCaptureContext} associated with the session. + * + * <p>Typically used to change the context associated with the default session from an activity. + */ + public final void setContentCaptureContext(@Nullable ContentCaptureContext context) { + mClientContext = context; + updateContentCaptureContext(context); + } + + abstract void updateContentCaptureContext(@Nullable ContentCaptureContext context); + + /** + * Gets the {@link ContentCaptureContext} associated with the session. + * + * @return context set on constructor or by + * {@link #setContentCaptureContext(ContentCaptureContext)}, or {@code null} if never + * explicitly set. + */ + @Nullable + public final ContentCaptureContext getContentCaptureContext() { + return mClientContext; + } + + /** * Destroys this session, flushing out all pending notifications to the service. * * <p>Once destroyed, any new notification will be dropped. @@ -247,20 +292,20 @@ public abstract class ContentCaptureSession implements AutoCloseable { public final void destroy() { synchronized (mLock) { if (mDestroyed) { - if (DEBUG) Log.d(TAG, "destroy(" + mId + "): already destroyed"); + if (sDebug) Log.d(TAG, "destroy(" + mId + "): already destroyed"); return; } mDestroyed = true; // TODO(b/111276913): check state (for example, how to handle if it's waiting for remote // id) and send it to the cache of batched commands - if (VERBOSE) { + if (sVerbose) { Log.v(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId); } // Finish children first if (mChildren != null) { final int numberChildren = mChildren.size(); - if (VERBOSE) Log.v(TAG, "Destroying " + numberChildren + " children first"); + if (sVerbose) Log.v(TAG, "Destroying " + numberChildren + " children first"); for (int i = 0; i < numberChildren; i++) { final ContentCaptureSession child = mChildren.get(i); try { @@ -424,6 +469,9 @@ public abstract class ContentCaptureSession implements AutoCloseable { @CallSuper void dump(@NonNull String prefix, @NonNull PrintWriter pw) { pw.print(prefix); pw.print("id: "); pw.println(mId); + if (mClientContext != null) { + pw.print(prefix); mClientContext.dump(pw); pw.println(); + } synchronized (mLock) { pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed); if (mChildren != null && !mChildren.isEmpty()) { diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index 810c967ce2c8..f4021b11f317 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -15,6 +15,7 @@ */ package android.view.contentcapture; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARING; import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED; @@ -22,9 +23,13 @@ import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_START import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED; -import static android.view.contentcapture.ContentCaptureHelper.DEBUG; -import static android.view.contentcapture.ContentCaptureHelper.VERBOSE; +import static android.view.contentcapture.ContentCaptureHelper.getIntDeviceConfigProperty; import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString; +import static android.view.contentcapture.ContentCaptureHelper.sDebug; +import static android.view.contentcapture.ContentCaptureHelper.sVerbose; +import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY; +import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE; +import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE; import android.annotation.NonNull; import android.annotation.Nullable; @@ -63,6 +68,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { private static final String TAG = MainContentCaptureSession.class.getSimpleName(); + // For readability purposes... private static final boolean FORCE_FLUSH = true; /** @@ -70,17 +76,9 @@ public final class MainContentCaptureSession extends ContentCaptureSession { */ private static final int MSG_FLUSH = 1; - /** - * Maximum number of events that are buffered before sent to the app. - */ - // TODO(b/121044064): use settings - private static final int MAX_BUFFER_SIZE = 100; - - /** - * Frequency the buffer is flushed if stale. - */ - // TODO(b/121044064): use settings - private static final int FLUSHING_FREQUENCY_MS = 5_000; + private static final int DEFAULT_MAX_BUFFER_SIZE = 100; + private static final int DEFAULT_FLUSHING_FREQUENCY_MS = 5_000; + private static final int DEFAULT_LOG_HISTORY_SIZE = 10; /** * Name of the {@link IResultReceiver} extra used to pass the binder interface to the service. @@ -104,14 +102,14 @@ public final class MainContentCaptureSession extends ContentCaptureSession { * Interface to the system_server binder object - it's only used to start the session (and * notify when the session is finished). */ - @Nullable // TODO(b/122959591): shoul never be null, we should make main session null instead + @NonNull private final IContentCaptureManager mSystemServerInterface; /** * Direct interface to the service binder object - it's used to send the events, including the * last ones (when the session is finished) */ - @Nullable + @NonNull private IContentCaptureDirectManager mDirectServiceInterface; @Nullable private DeathRecipient mDirectServiceVulture; @@ -130,20 +128,42 @@ public final class MainContentCaptureSession extends ContentCaptureSession { @Nullable private ArrayList<ContentCaptureEvent> mEvents; + /** + * Maximum number of events that are buffered before sent to the app. + */ + private final int mMaxBufferSize; + + /** + * Frequency the buffer is flushed if idle. + */ + private final int mIdleFlushingFrequencyMs; + // Used just for debugging purposes (on dump) private long mNextFlush; - // TODO(b/121044064): use settings to set size - private final LocalLog mFlushHistory = new LocalLog(10); + @Nullable + private final LocalLog mFlushHistory; /** @hide */ protected MainContentCaptureSession(@NonNull Context context, @NonNull ContentCaptureManager manager, @NonNull Handler handler, - @Nullable IContentCaptureManager systemServerInterface) { + @NonNull IContentCaptureManager systemServerInterface) { mContext = context; mManager = manager; mHandler = handler; mSystemServerInterface = systemServerInterface; + + // TODO(b/123096662): right now we're reading the device config values here, but ideally + // it should be read on ContentCaptureManagerService and passed back when the activity + // started. + mMaxBufferSize = getIntDeviceConfigProperty(DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE, + DEFAULT_MAX_BUFFER_SIZE); + mIdleFlushingFrequencyMs = getIntDeviceConfigProperty( + DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY, DEFAULT_FLUSHING_FREQUENCY_MS); + final int logHistorySize = getIntDeviceConfigProperty( + DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, DEFAULT_LOG_HISTORY_SIZE); + + mFlushHistory = logHistorySize > 0 ? new LocalLog(logHistorySize) : null; } @Override @@ -168,14 +188,14 @@ public final class MainContentCaptureSession extends ContentCaptureSession { int flags) { if (!isContentCaptureEnabled()) return; - if (VERBOSE) { + if (sVerbose) { Log.v(TAG, "start(): token=" + token + ", comp=" + ComponentName.flattenToShortString(component)); } if (hasStarted()) { // TODO(b/122959591): make sure this is expected (and when), or use Log.w - if (DEBUG) { + if (sDebug) { Log.d(TAG, "ignoring handleStartSession(" + token + "/" + ComponentName.flattenToShortString(component) + " while on state " + getStateAsString(mState)); @@ -186,14 +206,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession { mApplicationToken = token; mComponentName = component; - if (VERBOSE) { + if (sVerbose) { Log.v(TAG, "handleStartSession(): token=" + token + ", act=" + getDebugState() + ", id=" + mId); } try { - if (mSystemServerInterface == null) return; - mSystemServerInterface.startSession(mApplicationToken, component, mId, flags, new IResultReceiver.Stub() { @Override @@ -253,7 +271,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { mState = resultCode; mDisabled.set(false); } - if (VERBOSE) { + if (sVerbose) { Log.v(TAG, "handleSessionStarted() result: id=" + mId + " resultCode=" + resultCode + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get() + ", binder=" + binder + ", events=" + (mEvents == null ? 0 : mEvents.size())); @@ -268,26 +286,27 @@ public final class MainContentCaptureSession extends ContentCaptureSession { @UiThread private void sendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) { final int eventType = event.getType(); - if (VERBOSE) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event); - if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED) { + if (sVerbose) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event); + if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED + && eventType != ContentCaptureEvent.TYPE_CONTEXT_UPDATED) { // TODO(b/120494182): comment when this could happen (dialogs?) Log.v(TAG, "handleSendEvent(" + getDebugState() + ", " + ContentCaptureEvent.getTypeAsString(eventType) - + "): session not started yet"); + + "): dropping because session not started yet"); return; } if (mDisabled.get()) { // This happens when the event was queued in the handler before the sesison was ready, // then handleSessionStarted() returned and set it as disabled - we need to drop it, // otherwise it will keep triggering handleScheduleFlush() - if (VERBOSE) Log.v(TAG, "handleSendEvent(): ignoring when disabled"); + if (sVerbose) Log.v(TAG, "handleSendEvent(): ignoring when disabled"); return; } if (mEvents == null) { - if (VERBOSE) { - Log.v(TAG, "handleSendEvent(): creating buffer for " + MAX_BUFFER_SIZE + " events"); + if (sVerbose) { + Log.v(TAG, "handleSendEvent(): creating buffer for " + mMaxBufferSize + " events"); } - mEvents = new ArrayList<>(MAX_BUFFER_SIZE); + mEvents = new ArrayList<>(mMaxBufferSize); } // Some type of events can be merged together @@ -299,7 +318,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { // TODO(b/121045053): check if flags match if (lastEvent.getType() == TYPE_VIEW_TEXT_CHANGED && lastEvent.getId().equals(event.getId())) { - if (VERBOSE) { + if (sVerbose) { Log.v(TAG, "Buffering VIEW_TEXT_CHANGED event, updated text=" + getSanitizedString(event.getText())); } @@ -313,7 +332,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { final ContentCaptureEvent lastEvent = mEvents.get(mEvents.size() - 1); if (lastEvent.getType() == TYPE_VIEW_DISAPPEARED && event.getSessionId().equals(lastEvent.getSessionId())) { - if (VERBOSE) { + if (sVerbose) { Log.v(TAG, "Buffering TYPE_VIEW_DISAPPEARED events for session " + lastEvent.getSessionId()); } @@ -328,20 +347,20 @@ public final class MainContentCaptureSession extends ContentCaptureSession { final int numberEvents = mEvents.size(); - final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE; + final boolean bufferEvent = numberEvents < mMaxBufferSize; if (bufferEvent && !forceFlush) { scheduleFlush(FLUSH_REASON_IDLE_TIMEOUT, /* checkExisting= */ true); return; } - if (mState != STATE_ACTIVE && numberEvents >= MAX_BUFFER_SIZE) { + if (mState != STATE_ACTIVE && numberEvents >= mMaxBufferSize) { // Callback from startSession hasn't been called yet - typically happens on system // apps that are started before the system service // TODO(b/122959591): try to ignore session while system is not ready / boot // not complete instead. Similarly, the manager service should return right away // when the user does not have a service set - if (DEBUG) { + if (sDebug) { Log.d(TAG, "Closing session for " + getDebugState() + " after " + numberEvents + " delayed events"); } @@ -396,12 +415,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession { @UiThread private void scheduleFlush(@FlushReason int reason, boolean checkExisting) { - if (VERBOSE) { + if (sVerbose) { Log.v(TAG, "handleScheduleFlush(" + getDebugState(reason) + ", checkExisting=" + checkExisting); } if (!hasStarted()) { - if (VERBOSE) Log.v(TAG, "handleScheduleFlush(): session not started yet"); + if (sVerbose) Log.v(TAG, "handleScheduleFlush(): session not started yet"); return; } @@ -416,19 +435,19 @@ public final class MainContentCaptureSession extends ContentCaptureSession { // "Renew" the flush message by removing the previous one mHandler.removeMessages(MSG_FLUSH); } - mNextFlush = System.currentTimeMillis() + FLUSHING_FREQUENCY_MS; - if (VERBOSE) { + mNextFlush = System.currentTimeMillis() + mIdleFlushingFrequencyMs; + if (sVerbose) { Log.v(TAG, "handleScheduleFlush(): scheduled to flush in " - + FLUSHING_FREQUENCY_MS + "ms: " + TimeUtils.logTimeOfDay(mNextFlush)); + + mIdleFlushingFrequencyMs + "ms: " + TimeUtils.logTimeOfDay(mNextFlush)); } // Post using a Runnable directly to trim a few μs from PooledLambda.obtainMessage() - mHandler.postDelayed(() -> flushIfNeeded(reason), MSG_FLUSH, FLUSHING_FREQUENCY_MS); + mHandler.postDelayed(() -> flushIfNeeded(reason), MSG_FLUSH, mIdleFlushingFrequencyMs); } @UiThread private void flushIfNeeded(@FlushReason int reason) { if (mEvents == null || mEvents.isEmpty()) { - if (VERBOSE) Log.v(TAG, "Nothing to flush"); + if (sVerbose) Log.v(TAG, "Nothing to flush"); return; } flush(reason); @@ -446,7 +465,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } if (mDirectServiceInterface == null) { - if (VERBOSE) { + if (sVerbose) { Log.v(TAG, "handleForceFlush(" + getDebugState(reason) + "): hold your horses, " + "client not ready: " + mEvents); } @@ -458,14 +477,16 @@ public final class MainContentCaptureSession extends ContentCaptureSession { final int numberEvents = mEvents.size(); final String reasonString = getflushReasonAsString(reason); - if (DEBUG) { + if (sDebug) { Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getDebugState(reason)); } - // Logs reason, size, max size, idle timeout - final String logRecord = "r=" + reasonString + " s=" + numberEvents - + " m=" + MAX_BUFFER_SIZE + " i=" + FLUSHING_FREQUENCY_MS; - try { + if (mFlushHistory != null) { + // Logs reason, size, max size, idle timeout + final String logRecord = "r=" + reasonString + " s=" + numberEvents + + " m=" + mMaxBufferSize + " i=" + mIdleFlushingFrequencyMs; mFlushHistory.log(logRecord); + } + try { mHandler.removeMessages(MSG_FLUSH); final ParceledListSlice<ContentCaptureEvent> events = clearEvents(); @@ -476,6 +497,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } } + @Override + public void updateContentCaptureContext(@Nullable ContentCaptureContext context) { + notifyContextUpdated(mId, context); + } + /** * Resets the buffer and return a {@link ParceledListSlice} with the previous events. */ @@ -493,15 +519,13 @@ public final class MainContentCaptureSession extends ContentCaptureSession { @UiThread private void destroySession() { - if (DEBUG) { + if (sDebug) { Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with " + (mEvents == null ? 0 : mEvents.size()) + " event(s) for " + getDebugState()); } try { - if (mSystemServerInterface == null) return; - mSystemServerInterface.finishSession(mId); } catch (RemoteException e) { Log.e(TAG, "Error destroying system-service session " + mId + " for " @@ -513,7 +537,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { // clearings out. @UiThread private void resetSession(int newState) { - if (VERBOSE) { + if (sVerbose) { Log.v(TAG, "handleResetSession(" + getActivityName() + "): from " + getStateAsString(mState) + " to " + getStateAsString(newState)); } @@ -613,14 +637,19 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } } + void notifyContextUpdated(@NonNull String sessionId, + @Nullable ContentCaptureContext context) { + sendEvent(new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED) + .setClientContext(context)); + } + @Override void dump(@NonNull String prefix, @NonNull PrintWriter pw) { + super.dump(prefix, pw); + pw.print(prefix); pw.print("mContext: "); pw.println(mContext); pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId()); - if (mSystemServerInterface != null) { - pw.print(prefix); pw.print("mSystemServerInterface: "); - pw.println(mSystemServerInterface); - } + pw.print(prefix); pw.print("mSystemServerInterface: "); if (mDirectServiceInterface != null) { pw.print(prefix); pw.print("mDirectServiceInterface: "); pw.println(mDirectServiceInterface); @@ -638,8 +667,8 @@ public final class MainContentCaptureSession extends ContentCaptureSession { if (mEvents != null && !mEvents.isEmpty()) { final int numberEvents = mEvents.size(); pw.print(prefix); pw.print("buffered events: "); pw.print(numberEvents); - pw.print('/'); pw.println(MAX_BUFFER_SIZE); - if (VERBOSE && numberEvents > 0) { + pw.print('/'); pw.println(mMaxBufferSize); + if (sVerbose && numberEvents > 0) { final String prefix3 = prefix + " "; for (int i = 0; i < numberEvents; i++) { final ContentCaptureEvent event = mEvents.get(i); @@ -647,13 +676,17 @@ public final class MainContentCaptureSession extends ContentCaptureSession { pw.println(); } } - pw.print(prefix); pw.print("flush frequency: "); pw.println(FLUSHING_FREQUENCY_MS); + pw.print(prefix); pw.print("flush frequency: "); pw.println(mIdleFlushingFrequencyMs); pw.print(prefix); pw.print("next flush: "); TimeUtils.formatDuration(mNextFlush - System.currentTimeMillis(), pw); pw.print(" ("); pw.print(TimeUtils.logTimeOfDay(mNextFlush)); pw.println(")"); } - pw.print(prefix); pw.println("flush history:"); - mFlushHistory.reverseDump(/* fd= */ null, pw, /* args= */ null); pw.println(); + if (mFlushHistory != null) { + pw.print(prefix); pw.println("flush history:"); + mFlushHistory.reverseDump(/* fd= */ null, pw, /* args= */ null); pw.println(); + } else { + pw.print(prefix); pw.println("not logging flush history"); + } super.dump(prefix, pw); } diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index 904a86261e6c..89e205caa693 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -1230,9 +1230,16 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe * Ensures that the drop down is not obscuring the IME. * @param visible whether the ime should be in front. If false, the ime is pushed to * the background. + * + * This method is deprecated. Please use the following methods instead. + * Use {@link #setInputMethodMode} to ensure that the drop down is not obscuring the IME. + * Use {@link #showDropDown()} to show the drop down immediately + * A combination of {@link #isDropDownAlwaysVisible()} and {@link #enoughToFilter()} to decide + * whether to manually trigger {@link #showDropDown()} or not. + * * @hide internal used only here and SearchDialog */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768913) public void ensureImeVisible(boolean visible) { mPopup.setInputMethodMode(visible ? ListPopupWindow.INPUT_METHOD_NEEDED : ListPopupWindow.INPUT_METHOD_NOT_NEEDED); @@ -1242,14 +1249,39 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } /** - * @hide internal used only here and SearchDialog + * This method is deprecated. Please use {@link #getInputMethodMode()} instead. + * + * @hide This API is not being used and can be removed. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public boolean isInputMethodNotNeeded() { return mPopup.getInputMethodMode() == ListPopupWindow.INPUT_METHOD_NOT_NEEDED; } /** + * Returns the input method mode used by the auto complete dropdown. + */ + public int getInputMethodMode() { + return mPopup.getInputMethodMode(); + } + + /** + * Use this method to specify when the IME should be displayed. This function can be used to + * prevent the dropdown from obscuring the IME. + * + * @param mode speficies the input method mode. use one of the following values: + * + * {@link ListPopupWindow#INPUT_METHOD_FROM_FOCUSABLE} IME Displayed if the auto-complete box is + * focusable. + * {@link ListPopupWindow#INPUT_METHOD_NEEDED} Always display the IME. + * {@link ListPopupWindow#INPUT_METHOD_NOT_NEEDED}. The auto-complete suggestions are always + * displayed, even if the suggestions cover/hide the input method. + */ + public void setInputMethodMode(int mode) { + mPopup.setInputMethodMode(mode); + } + + /** * <p>Displays the drop down on screen.</p> */ public void showDropDown() { diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 29f070ed5a65..8113b40eaee0 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -199,7 +199,9 @@ public class ProgressBar extends View { private boolean mMaxInitialized; private int mBehavior; - @UnsupportedAppUsage + // Better to define a Drawable that implements Animatable if you want to modify animation + // characteristics programatically. + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124052713) private int mDuration; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private boolean mIndeterminate; @@ -284,7 +286,6 @@ public class ProgressBar extends View { } } - mDuration = a.getInt(R.styleable.ProgressBar_indeterminateDuration, mDuration); mMinWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_minWidth, mMinWidth); @@ -709,7 +710,17 @@ public class ProgressBar extends View { /** * Define the drawable used to draw the progress bar in indeterminate mode. * + * <p>For the Drawable to animate, it must implement {@link Animatable}, or override + * {@link Drawable#onLevelChange(int)}. A Drawable that implements Animatable will be animated + * via that interface and therefore provides the greatest amount of customization. A Drawable + * that only overrides onLevelChange(int) is animated directly by ProgressBar and only the + * animation {@link android.R.styleable#ProgressBar_indeterminateDuration duration}, + * {@link android.R.styleable#ProgressBar_indeterminateBehavior repeating behavior}, and + * {@link #setInterpolator(Interpolator) interpolator} can be modified, and only before the + * indeterminate animation begins. + * * @param d the new drawable + * @attr ref android.R.styleable#ProgressBar_indeterminateDrawable * @see #getIndeterminateDrawable() * @see #setIndeterminate(boolean) */ @@ -1774,10 +1785,21 @@ public class ProgressBar extends View { /** * Sets the acceleration curve for the indeterminate animation. - * The interpolator is loaded as a resource from the specified context. + * + * <p>The interpolator is loaded as a resource from the specified context. Defaults to a linear + * interpolation. + * + * <p>The interpolator only affects the indeterminate animation if the + * {@link #setIndeterminateDrawable(Drawable) supplied indeterminate drawable} does not + * implement {@link Animatable}. + * + * <p>This call must be made before the indeterminate animation starts for it to have an affect. * * @param context The application environment * @param resID The resource identifier of the interpolator to load + * @attr ref android.R.styleable#ProgressBar_interpolator + * @see #setInterpolator(Interpolator) + * @see #getInterpolator() */ public void setInterpolator(Context context, @InterpolatorRes int resID) { setInterpolator(AnimationUtils.loadInterpolator(context, resID)); @@ -1787,7 +1809,17 @@ public class ProgressBar extends View { * Sets the acceleration curve for the indeterminate animation. * Defaults to a linear interpolation. * + * <p>The interpolator only affects the indeterminate animation if the + * {@link #setIndeterminateDrawable(Drawable) supplied indeterminate drawable} does not + * implement {@link Animatable}. + * + * <p>This call must be made before the indeterminate animation starts for it to have + * an affect. + * * @param interpolator The interpolator which defines the acceleration curve + * @attr ref android.R.styleable#ProgressBar_interpolator + * @see #setInterpolator(Context, int) + * @see #getInterpolator() */ public void setInterpolator(Interpolator interpolator) { mInterpolator = interpolator; @@ -1797,6 +1829,9 @@ public class ProgressBar extends View { * Gets the acceleration curve type for the indeterminate animation. * * @return the {@link Interpolator} associated to this animation + * @attr ref android.R.styleable#ProgressBar_interpolator + * @see #setInterpolator(Context, int) + * @see #setInterpolator(Interpolator) */ @InspectableProperty public Interpolator getInterpolator() { diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index eef40e1f6e2e..d037337d4546 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -95,7 +95,7 @@ public class Toast { public static final int LENGTH_LONG = 1; final Context mContext; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) final TN mTN; @UnsupportedAppUsage int mDuration; @@ -354,7 +354,7 @@ public class Toast { } private static class TN extends ITransientNotification.Stub { - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(); private static final int SHOW = 0; @@ -362,18 +362,18 @@ public class Toast { private static final int CANCEL = 2; final Handler mHandler; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) int mGravity; int mX; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) int mY; float mHorizontalMargin; float mVerticalMargin; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) View mView; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) View mNextView; int mDuration; diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 8ebcef5133b6..2009fd50137a 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -46,6 +46,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; +import android.content.res.Configuration; import android.database.Cursor; import android.database.DataSetObserver; import android.graphics.Bitmap; @@ -486,6 +487,27 @@ public class ChooserActivity extends ResolverActivity { } } + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + int width = -1; + if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { + width = getResources().getDimensionPixelSize(R.dimen.chooser_preview_width); + } + + updateLayoutWidth(R.id.content_preview_text_layout, width); + updateLayoutWidth(R.id.content_preview_title_layout, width); + updateLayoutWidth(R.id.content_preview_file_layout, width); + } + + private void updateLayoutWidth(int layoutResourceId, int width) { + View view = findViewById(layoutResourceId); + LayoutParams params = view.getLayoutParams(); + params.width = width; + view.setLayoutParams(params); + } + private void displayContentPreview(@ContentPreviewType int previewType, Intent targetIntent) { switch (previewType) { case CONTENT_PREVIEW_TEXT: @@ -601,7 +623,14 @@ public class ChooserActivity extends ResolverActivity { private FileInfo extractFileInfo(Uri uri, ContentResolver resolver) { String fileName = null; boolean hasThumbnail = false; - Cursor cursor = resolver.query(uri, null, null, null, null); + Cursor cursor = null; + + try { + cursor = resolver.query(uri, null, null, null, null); + } catch (SecurityException e) { + Log.w(TAG, "Error loading file preview", e); + } + if (cursor != null && cursor.getCount() > 0) { int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); int flagsIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_FLAGS); @@ -633,40 +662,50 @@ public class ChooserActivity extends ResolverActivity { // due to permissions issues findViewById(R.id.file_copy_button).setVisibility(View.GONE); - ContentResolver resolver = getContentResolver(); - TextView fileNameView = findViewById(R.id.content_preview_filename); String action = targetIntent.getAction(); if (Intent.ACTION_SEND.equals(action)) { Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); - - FileInfo fileInfo = extractFileInfo(uri, resolver); - fileNameView.setText(fileInfo.name); - - if (fileInfo.hasThumbnail) { - loadUriIntoView(R.id.content_preview_file_thumbnail, uri); - } else { - ImageView fileIconView = findViewById(R.id.content_preview_file_icon); - fileIconView.setVisibility(View.VISIBLE); - fileIconView.setImageResource(R.drawable.ic_doc_generic); - } + loadFileUriIntoView(uri); } else { List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); - if (uris.size() == 0) { + int uriCount = uris.size(); + + if (uriCount == 0) { contentPreviewLayout.setVisibility(View.GONE); Log.i(TAG, - "Appears to be no uris available in EXTRA_STREAM, removing preview area"); + "Appears to be no uris available in EXTRA_STREAM, removing " + + "preview area"); return; + } else if (uriCount == 1) { + loadFileUriIntoView(uris.get(0)); + } else { + FileInfo fileInfo = extractFileInfo(uris.get(0), getContentResolver()); + int remUriCount = uriCount - 1; + String fileName = getResources().getQuantityString(R.plurals.file_count, + remUriCount, fileInfo.name, remUriCount); + + TextView fileNameView = findViewById(R.id.content_preview_filename); + fileNameView.setText(fileName); + + ImageView fileIconView = findViewById(R.id.content_preview_file_icon); + fileIconView.setVisibility(View.VISIBLE); + fileIconView.setImageResource(R.drawable.ic_file_copy); } + } + } - FileInfo fileInfo = extractFileInfo(uris.get(0), resolver); - int remFileCount = uris.size() - 1; - String fileName = getResources().getQuantityString(R.plurals.file_count, - remFileCount, fileInfo.name, remFileCount); + private void loadFileUriIntoView(Uri uri) { + FileInfo fileInfo = extractFileInfo(uri, getContentResolver()); - fileNameView.setText(fileName); + TextView fileNameView = findViewById(R.id.content_preview_filename); + fileNameView.setText(fileInfo.name); + + if (fileInfo.hasThumbnail) { + loadUriIntoView(R.id.content_preview_file_thumbnail, uri); + } else { ImageView fileIconView = findViewById(R.id.content_preview_file_icon); fileIconView.setVisibility(View.VISIBLE); - fileIconView.setImageResource(R.drawable.ic_file_copy); + fileIconView.setImageResource(R.drawable.ic_doc_generic); } } diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl index 8dde44e6bfee..9ce7ed1562ce 100644 --- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl @@ -153,17 +153,7 @@ interface IVoiceInteractionManagerService { in IVoiceActionCheckCallback callback); /** - * Sets the transcribed voice to the given string. + * Provide hints for showing UI. */ - void setTranscription(IVoiceInteractionService service, String transcription); - - /** - * Indicates that the transcription session is finished. - */ - void clearTranscription(IVoiceInteractionService service, boolean immediate); - - /** - * Sets the voice state indication based upon the given value. - */ - void setVoiceState(IVoiceInteractionService service, int state); + void setUiHints(in IVoiceInteractionService service, in Bundle hints); } diff --git a/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl index 674ad5b4ab67..bc757e24c852 100644 --- a/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl @@ -16,6 +16,8 @@ package com.android.internal.app; + import android.os.Bundle; + oneway interface IVoiceInteractionSessionListener { /** * Called when a voice session is shown. @@ -28,18 +30,7 @@ void onVoiceSessionHidden(); /** - * Called when voice assistant transcription has been updated to the given string. - */ - void onTranscriptionUpdate(in String transcription); - - /** - * Called when voice transcription is completed. - */ - void onTranscriptionComplete(in boolean immediate); - - /** - * Called when the voice assistant's state has changed. Values are from - * VoiceInteractionService's VOICE_STATE* constants. + * Called when UI hints were received. */ - void onVoiceStateChange(in int state); + void onSetUiHints(in Bundle args); }
\ No newline at end of file diff --git a/core/java/com/android/internal/os/BackgroundThread.java b/core/java/com/android/internal/os/BackgroundThread.java index eada142dd3c6..22c832e0689c 100644 --- a/core/java/com/android/internal/os/BackgroundThread.java +++ b/core/java/com/android/internal/os/BackgroundThread.java @@ -17,10 +17,13 @@ package com.android.internal.os; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.HandlerThread; import android.os.Looper; import android.os.Trace; +import java.util.concurrent.Executor; + /** * Shared singleton background thread for each process. */ @@ -29,6 +32,7 @@ public final class BackgroundThread extends HandlerThread { private static final long SLOW_DELIVERY_THRESHOLD_MS = 30_000; private static BackgroundThread sInstance; private static Handler sHandler; + private static HandlerExecutor sHandlerExecutor; private BackgroundThread() { super("android.bg", android.os.Process.THREAD_PRIORITY_BACKGROUND); @@ -43,6 +47,7 @@ public final class BackgroundThread extends HandlerThread { looper.setSlowLogThresholdMs( SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS); sHandler = new Handler(sInstance.getLooper()); + sHandlerExecutor = new HandlerExecutor(sHandler); } } @@ -59,4 +64,11 @@ public final class BackgroundThread extends HandlerThread { return sHandler; } } + + public static Executor getExecutor() { + synchronized (BackgroundThread.class) { + ensureThreadLocked(); + return sHandlerExecutor; + } + } } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 52e1748c621c..4ff99482feff 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -187,6 +187,8 @@ public class BatteryStatsImpl extends BatteryStats { static final int MSG_REPORT_RESET_STATS = 4; static final long DELAY_UPDATE_WAKELOCKS = 5*1000; + private static final double MILLISECONDS_IN_HOUR = 3600 * 1000; + private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader(); private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats(); @@ -252,6 +254,9 @@ public class BatteryStatsImpl extends BatteryStats { private static final long RPM_STATS_UPDATE_FREQ_MS = 1000; /** Last time that RPM stats were updated by updateRpmStatsLocked. */ private long mLastRpmStatsUpdateTimeMs = -RPM_STATS_UPDATE_FREQ_MS; + + /** Container for Rail Energy Data stats. */ + private final RailStats mTmpRailStats = new RailStats(); /** * Use a queue to delay removing UIDs from {@link KernelCpuUidUserSysTimeReader}, * {@link KernelCpuUidActiveTimeReader}, {@link KernelCpuUidClusterTimeReader}, @@ -327,6 +332,15 @@ public class BatteryStatsImpl extends BatteryStats { public String getSubsystemLowPowerStats(); } + /** interface to update rail information for power monitor */ + public interface RailEnergyDataCallback { + /** Function to fill the map for the rail data stats + * Used for power monitoring feature + * @param railStats + */ + void fillRailDataStats(RailStats railStats); + } + public static abstract class UserInfoProvider { private int[] userIds; protected abstract @Nullable int[] getUserIds(); @@ -361,6 +375,8 @@ public class BatteryStatsImpl extends BatteryStats { } }; + public final RailEnergyDataCallback mRailEnergyDataCallback; + /** * This handler is running on {@link BackgroundThread}. */ @@ -593,7 +609,9 @@ public class BatteryStatsImpl extends BatteryStats { int UPDATE_RADIO = 0x04; int UPDATE_BT = 0x08; int UPDATE_RPM = 0x10; // 16 - int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM; + int UPDATE_RAIL = 0x20; // 32 + int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM + | UPDATE_RAIL; Future<?> scheduleSync(String reason, int flags); Future<?> scheduleCpuSyncDueToRemovedUid(int uid); @@ -1078,6 +1096,7 @@ public class BatteryStatsImpl extends BatteryStats { mBatteryStatsHistory = null; mHandler = null; mPlatformIdleStateCallback = null; + mRailEnergyDataCallback = null; mUserInfoProvider = null; mConstants = new Constants(mHandler); clearHistoryLocked(); @@ -3005,6 +3024,7 @@ public class BatteryStatsImpl extends BatteryStats { private final LongSamplingCounter mRxTimeMillis; private final LongSamplingCounter[] mTxTimeMillis; private final LongSamplingCounter mPowerDrainMaMs; + private final LongSamplingCounter mMonitoredRailChargeConsumedMaMs; public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates) { mIdleTimeMillis = new LongSamplingCounter(timeBase); @@ -3016,6 +3036,7 @@ public class BatteryStatsImpl extends BatteryStats { mTxTimeMillis[i] = new LongSamplingCounter(timeBase); } mPowerDrainMaMs = new LongSamplingCounter(timeBase); + mMonitoredRailChargeConsumedMaMs = new LongSamplingCounter(timeBase); } public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates, Parcel in) { @@ -3033,6 +3054,7 @@ public class BatteryStatsImpl extends BatteryStats { mTxTimeMillis[i] = new LongSamplingCounter(timeBase, in); } mPowerDrainMaMs = new LongSamplingCounter(timeBase, in); + mMonitoredRailChargeConsumedMaMs = new LongSamplingCounter(timeBase, in); } public void readSummaryFromParcel(Parcel in) { @@ -3048,6 +3070,7 @@ public class BatteryStatsImpl extends BatteryStats { counter.readSummaryFromParcelLocked(in); } mPowerDrainMaMs.readSummaryFromParcelLocked(in); + mMonitoredRailChargeConsumedMaMs.readSummaryFromParcelLocked(in); } @Override @@ -3065,6 +3088,7 @@ public class BatteryStatsImpl extends BatteryStats { counter.writeSummaryFromParcelLocked(dest); } mPowerDrainMaMs.writeSummaryFromParcelLocked(dest); + mMonitoredRailChargeConsumedMaMs.writeSummaryFromParcelLocked(dest); } @Override @@ -3078,6 +3102,7 @@ public class BatteryStatsImpl extends BatteryStats { counter.writeToParcel(dest); } mPowerDrainMaMs.writeToParcel(dest); + mMonitoredRailChargeConsumedMaMs.writeToParcel(dest); } public void reset(boolean detachIfReset) { @@ -3089,6 +3114,7 @@ public class BatteryStatsImpl extends BatteryStats { counter.reset(detachIfReset); } mPowerDrainMaMs.reset(detachIfReset); + mMonitoredRailChargeConsumedMaMs.reset(detachIfReset); } public void detach() { @@ -3100,6 +3126,7 @@ public class BatteryStatsImpl extends BatteryStats { counter.detach(); } mPowerDrainMaMs.detach(); + mMonitoredRailChargeConsumedMaMs.detach(); } /** @@ -3154,6 +3181,15 @@ public class BatteryStatsImpl extends BatteryStats { public LongSamplingCounter getPowerCounter() { return mPowerDrainMaMs; } + + /** + * @return a LongSamplingCounter, measuring actual monitored rail energy consumed + * milli-ampere milli-seconds (mAmS). + */ + @Override + public LongSamplingCounter getMonitoredRailChargeConsumedMaMs() { + return mMonitoredRailChargeConsumedMaMs; + } } /** Get Resource Power Manager stats. Create a new one if it doesn't already exist. */ @@ -3497,6 +3533,8 @@ public class BatteryStatsImpl extends BatteryStats { if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryChargeUAh=" + cur.batteryChargeUAh); dest.writeInt(cur.batteryChargeUAh); } + dest.writeDouble(cur.modemRailChargeMah); + dest.writeDouble(cur.wifiRailChargeMah); } private int buildBatteryLevelInt(HistoryItem h) { @@ -3747,6 +3785,8 @@ public class BatteryStatsImpl extends BatteryStats { if ((firstToken&DELTA_BATTERY_CHARGE_FLAG) != 0) { cur.batteryChargeUAh = src.readInt(); } + cur.modemRailChargeMah = src.readDouble(); + cur.wifiRailChargeMah = src.readDouble(); } @Override @@ -10111,12 +10151,12 @@ public class BatteryStatsImpl extends BatteryStats { } public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb, - UserInfoProvider userInfoProvider) { - this(new SystemClocks(), systemDir, handler, cb, userInfoProvider); + RailEnergyDataCallback railStatsCb, UserInfoProvider userInfoProvider) { + this(new SystemClocks(), systemDir, handler, cb, railStatsCb, userInfoProvider); } private BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler, - PlatformIdleStateCallback cb, + PlatformIdleStateCallback cb, RailEnergyDataCallback railStatsCb, UserInfoProvider userInfoProvider) { init(clocks); @@ -10218,6 +10258,7 @@ public class BatteryStatsImpl extends BatteryStats { clearHistoryLocked(); updateDailyDeadlineLocked(); mPlatformIdleStateCallback = cb; + mRailEnergyDataCallback = railStatsCb; mUserInfoProvider = userInfoProvider; } @@ -10238,6 +10279,7 @@ public class BatteryStatsImpl extends BatteryStats { mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer); readFromParcel(p); mPlatformIdleStateCallback = null; + mRailEnergyDataCallback = null; } public void setPowerProfileLocked(PowerProfile profile) { @@ -10934,6 +10976,8 @@ public class BatteryStatsImpl extends BatteryStats { mWakeupReasonStats.clear(); } + mTmpRailStats.reset(); + mLastHistoryStepDetails = null; mLastStepCpuUserTime = mLastStepCpuSystemTime = 0; mCurStepCpuUserTime = mCurStepCpuSystemTime = 0; @@ -11321,6 +11365,16 @@ public class BatteryStatsImpl extends BatteryStats { mWifiActivity.getPowerCounter().addCountLocked( (long) (info.getControllerEnergyUsed() / opVolt)); } + // Converting uWs to mAms. + // Conversion: (uWs * (1000ms / 1s) * (1mW / 1000uW)) / mV = mAms + long monitoredRailChargeConsumedMaMs = + (long) (mTmpRailStats.getWifiTotalEnergyUseduWs() / opVolt); + mWifiActivity.getMonitoredRailChargeConsumedMaMs().addCountLocked( + monitoredRailChargeConsumedMaMs); + mHistoryCur.wifiRailChargeMah += + (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR); + addHistoryRecordLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis()); + mTmpRailStats.resetWifiTotalEnergyUsed(); } } } @@ -11411,9 +11465,18 @@ public class BatteryStatsImpl extends BatteryStats { // We store the power drain as mAms. mModemActivity.getPowerCounter().addCountLocked((long) energyUsed); + // Converting uWs to mAms. + // Conversion: (uWs * (1000ms / 1s) * (1mW / 1000uW)) / mV = mAms + long monitoredRailChargeConsumedMaMs = + (long) (mTmpRailStats.getCellularTotalEnergyUseduWs() / opVolt); + mModemActivity.getMonitoredRailChargeConsumedMaMs().addCountLocked( + monitoredRailChargeConsumedMaMs); + mHistoryCur.modemRailChargeMah += + (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR); + addHistoryRecordLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis()); + mTmpRailStats.resetCellularTotalEnergyUsed(); } } - final long elapsedRealtimeMs = mClocks.elapsedRealtime(); long radioTime = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked( elapsedRealtimeMs * 1000); @@ -11812,6 +11875,16 @@ public class BatteryStatsImpl extends BatteryStats { } /** + * Read and record Rail Energy data. + */ + public void updateRailStatsLocked() { + if (mRailEnergyDataCallback == null || !mTmpRailStats.isRailStatsAvailable()) { + return; + } + mRailEnergyDataCallback.fillRailDataStats(mTmpRailStats); + } + + /** * Read and distribute kernel wake lock use across apps. */ public void updateKernelWakelocksLocked() { @@ -12950,6 +13023,8 @@ public class BatteryStatsImpl extends BatteryStats { final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which); final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which); final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which); + final long monitoredRailChargeConsumedMaMs = + counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which); long[] timeInRatMs = new long[BatteryStats.NUM_DATA_CONNECTION_TYPES]; for (int i = 0; i < timeInRatMs.length; i++) { timeInRatMs[i] = getPhoneDataConnectionTime(i, rawRealTime, which) / 1000; @@ -12979,58 +13054,62 @@ public class BatteryStatsImpl extends BatteryStats { s.setTimeInRatMs(timeInRatMs); s.setTimeInRxSignalStrengthLevelMs(timeInRxSignalStrengthLevelMs); s.setTxTimeMs(txTimeMs); + s.setMonitoredRailChargeConsumedMaMs(monitoredRailChargeConsumedMaMs); return s; } - /*@hide */ - public WifiBatteryStats getWifiBatteryStats() { - WifiBatteryStats s = new WifiBatteryStats(); - final int which = STATS_SINCE_CHARGED; - final long rawRealTime = SystemClock.elapsedRealtime() * 1000; - final ControllerActivityCounter counter = getWifiControllerActivity(); - final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which); - final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which); - final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which); - final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(which); - final long totalControllerActivityTimeMs - = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000; - final long sleepTimeMs - = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + txTimeMs); - final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which); - long numAppScanRequest = 0; - for (int i = 0; i < mUidStats.size(); i++) { - numAppScanRequest += mUidStats.valueAt(i).mWifiScanTimer.getCountLocked(which); - } - long[] timeInStateMs = new long[NUM_WIFI_STATES]; - for (int i=0; i<NUM_WIFI_STATES; i++) { + /*@hide */ + public WifiBatteryStats getWifiBatteryStats() { + WifiBatteryStats s = new WifiBatteryStats(); + final int which = STATS_SINCE_CHARGED; + final long rawRealTime = SystemClock.elapsedRealtime() * 1000; + final ControllerActivityCounter counter = getWifiControllerActivity(); + final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which); + final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which); + final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which); + final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(which); + final long totalControllerActivityTimeMs + = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000; + final long sleepTimeMs + = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + txTimeMs); + final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which); + final long monitoredRailChargeConsumedMaMs = + counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which); + long numAppScanRequest = 0; + for (int i = 0; i < mUidStats.size(); i++) { + numAppScanRequest += mUidStats.valueAt(i).mWifiScanTimer.getCountLocked(which); + } + long[] timeInStateMs = new long[NUM_WIFI_STATES]; + for (int i=0; i<NUM_WIFI_STATES; i++) { timeInStateMs[i] = getWifiStateTime(i, rawRealTime, which) / 1000; - } - long[] timeInSupplStateMs = new long[NUM_WIFI_SUPPL_STATES]; - for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) { - timeInSupplStateMs[i] = getWifiSupplStateTime(i, rawRealTime, which) / 1000; - } - long[] timeSignalStrengthTimeMs = new long[NUM_WIFI_SIGNAL_STRENGTH_BINS]; - for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) { - timeSignalStrengthTimeMs[i] = getWifiSignalStrengthTime(i, rawRealTime, which) / 1000; - } - s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000); - s.setKernelActiveTimeMs(getWifiActiveTime(rawRealTime, which) / 1000); - s.setNumPacketsTx(getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which)); - s.setNumBytesTx(getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which)); - s.setNumPacketsRx(getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which)); - s.setNumBytesRx(getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which)); - s.setSleepTimeMs(sleepTimeMs); - s.setIdleTimeMs(idleTimeMs); - s.setRxTimeMs(rxTimeMs); - s.setTxTimeMs(txTimeMs); - s.setScanTimeMs(scanTimeMs); - s.setEnergyConsumedMaMs(energyConsumedMaMs); - s.setNumAppScanRequest(numAppScanRequest); - s.setTimeInStateMs(timeInStateMs); - s.setTimeInSupplicantStateMs(timeInSupplStateMs); - s.setTimeInRxSignalStrengthLevelMs(timeSignalStrengthTimeMs); - return s; - } + } + long[] timeInSupplStateMs = new long[NUM_WIFI_SUPPL_STATES]; + for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) { + timeInSupplStateMs[i] = getWifiSupplStateTime(i, rawRealTime, which) / 1000; + } + long[] timeSignalStrengthTimeMs = new long[NUM_WIFI_SIGNAL_STRENGTH_BINS]; + for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) { + timeSignalStrengthTimeMs[i] = getWifiSignalStrengthTime(i, rawRealTime, which) / 1000; + } + s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000); + s.setKernelActiveTimeMs(getWifiActiveTime(rawRealTime, which) / 1000); + s.setNumPacketsTx(getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which)); + s.setNumBytesTx(getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which)); + s.setNumPacketsRx(getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which)); + s.setNumBytesRx(getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which)); + s.setSleepTimeMs(sleepTimeMs); + s.setIdleTimeMs(idleTimeMs); + s.setRxTimeMs(rxTimeMs); + s.setTxTimeMs(txTimeMs); + s.setScanTimeMs(scanTimeMs); + s.setEnergyConsumedMaMs(energyConsumedMaMs); + s.setNumAppScanRequest(numAppScanRequest); + s.setTimeInStateMs(timeInStateMs); + s.setTimeInSupplicantStateMs(timeInSupplStateMs); + s.setTimeInRxSignalStrengthLevelMs(timeSignalStrengthTimeMs); + s.setMonitoredRailChargeConsumedMaMs(monitoredRailChargeConsumedMaMs); + return s; + } /*@hide */ public GpsBatteryStats getGpsBatteryStats() { diff --git a/core/java/com/android/internal/os/RailStats.java b/core/java/com/android/internal/os/RailStats.java new file mode 100644 index 000000000000..ff0083138b30 --- /dev/null +++ b/core/java/com/android/internal/os/RailStats.java @@ -0,0 +1,147 @@ +/* + * 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.os; + +import android.util.ArrayMap; +import android.util.Slog; + +import java.util.Map; + +/** Rail Stats Power Monitoring Class */ +public final class RailStats { + private static final String TAG = "RailStats"; + + private static final String WIFI_SUBSYSTEM = "wifi"; + private static final String CELLULAR_SUBSYSTEM = "cellular"; + + private Map<Long, RailInfoData> mRailInfoData = new ArrayMap<>(); + + private long mCellularTotalEnergyUseduWs = 0; + private long mWifiTotalEnergyUseduWs = 0; + private boolean mRailStatsAvailability = true; + + /** Updates the rail data map of all power monitor rails being monitored + * Function is called from native side + * @param index + * @param railName + * @param subSystemName + * @param timestampSinceBootMs + * @param energyUsedSinceBootuWs + */ + public void updateRailData(long index, String railName, String subSystemName, + long timestampSinceBootMs, long energyUsedSinceBootuWs) { + if (!(subSystemName.equals(WIFI_SUBSYSTEM) || subSystemName.equals(CELLULAR_SUBSYSTEM))) { + return; + } + RailInfoData node = mRailInfoData.get(index); + if (node == null) { + mRailInfoData.put(index, new RailInfoData(index, railName, subSystemName, + timestampSinceBootMs, energyUsedSinceBootuWs)); + if (subSystemName.equals(WIFI_SUBSYSTEM)) { + mWifiTotalEnergyUseduWs += energyUsedSinceBootuWs; + return; + } + if (subSystemName.equals(CELLULAR_SUBSYSTEM)) { + mCellularTotalEnergyUseduWs += energyUsedSinceBootuWs; + } + return; + } + long timeSinceLastLogMs = timestampSinceBootMs - node.timestampSinceBootMs; + long energyUsedSinceLastLoguWs = energyUsedSinceBootuWs - node.energyUsedSinceBootuWs; + if (timeSinceLastLogMs < 0 || energyUsedSinceLastLoguWs < 0) { + energyUsedSinceLastLoguWs = node.energyUsedSinceBootuWs; + } + node.timestampSinceBootMs = timestampSinceBootMs; + node.energyUsedSinceBootuWs = energyUsedSinceBootuWs; + if (subSystemName.equals(WIFI_SUBSYSTEM)) { + mWifiTotalEnergyUseduWs += energyUsedSinceLastLoguWs; + return; + } + if (subSystemName.equals(CELLULAR_SUBSYSTEM)) { + mCellularTotalEnergyUseduWs += energyUsedSinceLastLoguWs; + } + } + + /** resets the cellular total energy used aspect. + */ + public void resetCellularTotalEnergyUsed() { + mCellularTotalEnergyUseduWs = 0; + } + + /** resets the wifi total energy used aspect. + */ + public void resetWifiTotalEnergyUsed() { + mWifiTotalEnergyUseduWs = 0; + } + + public long getCellularTotalEnergyUseduWs() { + return mCellularTotalEnergyUseduWs; + } + + public long getWifiTotalEnergyUseduWs() { + return mWifiTotalEnergyUseduWs; + } + + /** reset the total energy subsystems + * + */ + public void reset() { + mCellularTotalEnergyUseduWs = 0; + mWifiTotalEnergyUseduWs = 0; + } + + public RailStats getRailStats() { + return this; + } + + public void setRailStatsAvailability(boolean railStatsAvailability) { + mRailStatsAvailability = railStatsAvailability; + } + + public boolean isRailStatsAvailable() { + return mRailStatsAvailability; + } + + /** Container class to contain rail data information */ + public static class RailInfoData { + private static final String TAG = "RailInfoData"; + public long index; + public String railName; + public String subSystemName; + public long timestampSinceBootMs; + public long energyUsedSinceBootuWs; + + private RailInfoData(long index, String railName, String subSystemName, + long timestampSinceBootMs, long energyUsedSinceBoot) { + this.index = index; + this.railName = railName; + this.subSystemName = subSystemName; + this.timestampSinceBootMs = timestampSinceBootMs; + this.energyUsedSinceBootuWs = energyUsedSinceBoot; + } + + /** print the rail data + * + */ + public void printData() { + Slog.d(TAG, "Index = " + index); + Slog.d(TAG, "RailName = " + railName); + Slog.d(TAG, "SubSystemName = " + subSystemName); + Slog.d(TAG, "TimestampSinceBootMs = " + timestampSinceBootMs); + Slog.d(TAG, "EnergyUsedSinceBootuWs = " + energyUsedSinceBootuWs); + } + } +} diff --git a/core/java/com/android/server/backup/PermissionBackupHelper.java b/core/java/com/android/server/backup/PermissionBackupHelper.java index c0ba18159639..c7c423b82cf1 100644 --- a/core/java/com/android/server/backup/PermissionBackupHelper.java +++ b/core/java/com/android/server/backup/PermissionBackupHelper.java @@ -16,11 +16,14 @@ package com.android.server.backup; -import android.app.AppGlobals; +import android.annotation.NonNull; import android.app.backup.BlobBackupHelper; -import android.content.pm.IPackageManager; +import android.os.UserHandle; +import android.permission.PermissionManagerInternal; import android.util.Slog; +import com.android.server.LocalServices; + public class PermissionBackupHelper extends BlobBackupHelper { private static final String TAG = "PermissionBackup"; private static final boolean DEBUG = false; @@ -31,24 +34,26 @@ public class PermissionBackupHelper extends BlobBackupHelper { // key under which the permission-grant state blob is committed to backup private static final String KEY_PERMISSIONS = "permissions"; - private final int mUserId; + private final @NonNull UserHandle mUser; + + private final @NonNull PermissionManagerInternal mPermissionManager; public PermissionBackupHelper(int userId) { super(STATE_VERSION, KEY_PERMISSIONS); - mUserId = userId; + mUser = UserHandle.of(userId); + mPermissionManager = LocalServices.getService(PermissionManagerInternal.class); } @Override protected byte[] getBackupPayload(String key) { - IPackageManager pm = AppGlobals.getPackageManager(); if (DEBUG) { Slog.d(TAG, "Handling backup of " + key); } try { switch (key) { case KEY_PERMISSIONS: - return pm.getPermissionGrantBackup(mUserId); + return mPermissionManager.backupRuntimePermissions(mUser); default: Slog.w(TAG, "Unexpected backup key " + key); @@ -61,14 +66,13 @@ public class PermissionBackupHelper extends BlobBackupHelper { @Override protected void applyRestoredPayload(String key, byte[] payload) { - IPackageManager pm = AppGlobals.getPackageManager(); if (DEBUG) { Slog.d(TAG, "Handling restore of " + key); } try { switch (key) { case KEY_PERMISSIONS: - pm.restorePermissionGrants(payload, mUserId); + mPermissionManager.restoreRuntimePermissions(payload, mUser); break; default: diff --git a/core/jni/Android.bp b/core/jni/Android.bp index af0b7c307ef6..74061367804e 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -230,6 +230,7 @@ cc_library_shared { ], static_libs: [ + "libasync_safe", "libgif", "libseccomp_policy", "libgrallocusage", diff --git a/core/jni/OWNERS b/core/jni/OWNERS index 774c2242e144..7ff15f2e182d 100644 --- a/core/jni/OWNERS +++ b/core/jni/OWNERS @@ -6,4 +6,4 @@ per-file *Camera*,*camera* = shuzhenwang@google.com, yinchiayeh@google.com, zhij per-file android_net_* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com # Zygote -per-file com_android_inernal_os_Zygote.*,fd_utils.* = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com +per-file com_android_internal_os_Zygote.*,fd_utils.* = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index e8172172f5d2..8f007594dd67 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -355,9 +355,16 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, colorType = kN32_SkColorType; } + sk_sp<SkColorSpace> colorSpace; + if (colorType == kAlpha_8_SkColorType) { + colorSpace = nullptr; + } else { + colorSpace = GraphicsJNI::getNativeColorSpace(colorSpacePtr); + } + SkBitmap bitmap; bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType, - GraphicsJNI::getNativeColorSpace(colorSpacePtr))); + colorSpace)); sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(&bitmap); if (!nativeBitmap) { @@ -385,15 +392,17 @@ static bool bitmapCopyTo(SkBitmap* dst, SkColorType dstCT, const SkBitmap& src, case kRGB_565_SkColorType: dstInfo = dstInfo.makeAlphaType(kOpaque_SkAlphaType); break; - case kRGBA_F16_SkColorType: - // The caller does not have an opportunity to pass a dst color space. Assume that - // they want linear sRGB. - dstInfo = dstInfo.makeColorSpace(SkColorSpace::MakeSRGBLinear()); + case kAlpha_8_SkColorType: + dstInfo = dstInfo.makeColorSpace(nullptr); break; default: break; } + if (!dstInfo.colorSpace() && dstCT != kAlpha_8_SkColorType) { + dstInfo = dstInfo.makeColorSpace(SkColorSpace::MakeSRGB()); + } + if (!dst->setInfo(dstInfo)) { return false; } @@ -608,14 +617,6 @@ static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) { return static_cast<jint>(bitmap->getGenerationID()); } -static jboolean Bitmap_isConfigF16(JNIEnv* env, jobject, jlong bitmapHandle) { - LocalScopedBitmap bitmap(bitmapHandle); - if (bitmap->info().colorType() == kRGBA_F16_SkColorType) { - return JNI_TRUE; - } - return JNI_FALSE; -} - static jboolean Bitmap_isPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle) { LocalScopedBitmap bitmap(bitmapHandle); if (bitmap->info().alphaType() == kPremul_SkAlphaType) { @@ -684,9 +685,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { const SkAlphaType alphaType = (SkAlphaType)p->readInt32(); const uint32_t colorSpaceSize = p->readUint32(); sk_sp<SkColorSpace> colorSpace; - if (kRGBA_F16_SkColorType == colorType) { - colorSpace = SkColorSpace::MakeSRGBLinear(); - } else if (colorSpaceSize > 0) { + if (colorSpaceSize > 0) { if (colorSpaceSize > kMaxColorSpaceSerializedBytes) { ALOGD("Bitmap_createFromParcel: Serialized SkColorSpace is larger than expected: " "%d bytes\n", colorSpaceSize); @@ -811,7 +810,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, p->writeInt32(bitmap.colorType()); p->writeInt32(bitmap.alphaType()); SkColorSpace* colorSpace = bitmap.colorSpace(); - if (colorSpace != nullptr && bitmap.colorType() != kRGBA_F16_SkColorType) { + if (colorSpace != nullptr) { sk_sp<SkData> data = colorSpace->serialize(); size_t size = data->size(); p->writeUint32(size); @@ -924,44 +923,14 @@ static jboolean Bitmap_isSRGBLinear(JNIEnv* env, jobject, jlong bitmapHandle) { return colorSpace == srgbLinear.get() ? JNI_TRUE : JNI_FALSE; } -static jboolean Bitmap_getColorSpace(JNIEnv* env, jobject, jlong bitmapHandle, - jfloatArray xyzArray, jfloatArray paramsArray) { - +static jobject Bitmap_computeColorSpace(JNIEnv* env, jobject, jlong bitmapHandle) { LocalScopedBitmap bitmapHolder(bitmapHandle); - if (!bitmapHolder.valid()) return JNI_FALSE; + if (!bitmapHolder.valid()) return nullptr; SkColorSpace* colorSpace = bitmapHolder->info().colorSpace(); - if (colorSpace == nullptr) return JNI_FALSE; - - skcms_Matrix3x3 xyzMatrix; - if (!colorSpace->toXYZD50(&xyzMatrix)) return JNI_FALSE; - - jfloat* xyz = env->GetFloatArrayElements(xyzArray, NULL); - xyz[0] = xyzMatrix.vals[0][0]; - xyz[1] = xyzMatrix.vals[1][0]; - xyz[2] = xyzMatrix.vals[2][0]; - xyz[3] = xyzMatrix.vals[0][1]; - xyz[4] = xyzMatrix.vals[1][1]; - xyz[5] = xyzMatrix.vals[2][1]; - xyz[6] = xyzMatrix.vals[0][2]; - xyz[7] = xyzMatrix.vals[1][2]; - xyz[8] = xyzMatrix.vals[2][2]; - env->ReleaseFloatArrayElements(xyzArray, xyz, 0); - - skcms_TransferFunction transferParams; - if (!colorSpace->isNumericalTransferFn(&transferParams)) return JNI_FALSE; - - jfloat* params = env->GetFloatArrayElements(paramsArray, NULL); - params[0] = transferParams.a; - params[1] = transferParams.b; - params[2] = transferParams.c; - params[3] = transferParams.d; - params[4] = transferParams.e; - params[5] = transferParams.f; - params[6] = transferParams.g; - env->ReleaseFloatArrayElements(paramsArray, params, 0); + if (colorSpace == nullptr) return nullptr; - return JNI_TRUE; + return GraphicsJNI::getColorSpace(env, colorSpace, bitmapHolder->info().colorType()); } static void Bitmap_setColorSpace(JNIEnv* env, jobject, jlong bitmapHandle, jlong colorSpacePtr) { @@ -1174,13 +1143,6 @@ static jobject Bitmap_createGraphicBufferHandle(JNIEnv* env, jobject, jlong bitm return createJavaGraphicBuffer(env, buffer); } -static void Bitmap_copyColorSpace(JNIEnv* env, jobject, jlong srcBitmapPtr, jlong dstBitmapPtr) { - LocalScopedBitmap srcBitmapHandle(srcBitmapPtr); - LocalScopedBitmap dstBitmapHandle(dstBitmapPtr); - - dstBitmapHandle->bitmap().setColorSpace(srcBitmapHandle->bitmap().info().refColorSpace()); -} - static jboolean Bitmap_isImmutable(jlong bitmapHandle) { LocalScopedBitmap bitmapHolder(bitmapHandle); if (!bitmapHolder.valid()) return JNI_FALSE; @@ -1215,7 +1177,6 @@ static const JNINativeMethod gBitmapMethods[] = { { "nativeErase", "(JJJ)V", (void*)Bitmap_eraseLong }, { "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes }, { "nativeConfig", "(J)I", (void*)Bitmap_config }, - { "nativeIsConfigF16", "(J)Z", (void*)Bitmap_isConfigF16 }, { "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha }, { "nativeIsPremultiplied", "(J)Z", (void*)Bitmap_isPremultiplied}, { "nativeSetHasAlpha", "(JZZ)V", (void*)Bitmap_setHasAlpha}, @@ -1248,12 +1209,10 @@ static const JNINativeMethod gBitmapMethods[] = { (void*) Bitmap_wrapHardwareBufferBitmap }, { "nativeCreateGraphicBufferHandle", "(J)Landroid/graphics/GraphicBuffer;", (void*) Bitmap_createGraphicBufferHandle }, - { "nativeGetColorSpace", "(J[F[F)Z", (void*)Bitmap_getColorSpace }, + { "nativeComputeColorSpace", "(J)Landroid/graphics/ColorSpace;", (void*)Bitmap_computeColorSpace }, { "nativeSetColorSpace", "(JJ)V", (void*)Bitmap_setColorSpace }, { "nativeIsSRGB", "(J)Z", (void*)Bitmap_isSRGB }, { "nativeIsSRGBLinear", "(J)Z", (void*)Bitmap_isSRGBLinear}, - { "nativeCopyColorSpace", "(JJ)V", - (void*)Bitmap_copyColorSpace }, { "nativeSetImmutable", "(J)V", (void*)Bitmap_setImmutable}, // ------------ @CriticalNative ---------------- diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 70e6604fddeb..4ba4540f7dbc 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -307,7 +307,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream, env->SetObjectField(options, gOptions_outConfigFieldID, config); env->SetObjectField(options, gOptions_outColorSpaceFieldID, - GraphicsJNI::getColorSpace(env, decodeColorSpace, decodeColorType)); + GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType)); if (onlyDecodeSize) { return nullptr; diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp index d65f324d1065..9c07e2d64c6e 100644 --- a/core/jni/android/graphics/BitmapRegionDecoder.cpp +++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp @@ -215,7 +215,7 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in env->SetObjectField(options, gOptions_outConfigFieldID, config); env->SetObjectField(options, gOptions_outColorSpaceFieldID, - GraphicsJNI::getColorSpace(env, decodeColorSpace, decodeColorType)); + GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType)); } // If we may have reused a bitmap, we need to indicate that the pixels have changed. diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 6570992b4b23..2987c5ed56b5 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -187,6 +187,8 @@ static jmethodID gColorSpaceRGB_constructorMethodID; static jclass gColorSpace_Named_class; static jfieldID gColorSpace_Named_sRGBFieldID; +static jfieldID gColorSpace_Named_ExtendedSRGBFieldID; +static jfieldID gColorSpace_Named_LinearSRGBFieldID; static jfieldID gColorSpace_Named_LinearExtendedSRGBFieldID; static jclass gTransferParameters_class; @@ -412,67 +414,78 @@ jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region) /////////////////////////////////////////////////////////////////////////////// -jobject GraphicsJNI::getColorSpace(JNIEnv* env, sk_sp<SkColorSpace>& decodeColorSpace, +jobject GraphicsJNI::getColorSpace(JNIEnv* env, SkColorSpace* decodeColorSpace, SkColorType decodeColorType) { - jobject colorSpace = nullptr; + if (!decodeColorSpace || decodeColorType == kAlpha_8_SkColorType) { + return nullptr; + } - // No need to match, we know what the output color space will be + // Special checks for the common sRGB cases and their extended variants. + jobject namedCS = nullptr; + sk_sp<SkColorSpace> srgbLinear = SkColorSpace::MakeSRGBLinear(); if (decodeColorType == kRGBA_F16_SkColorType) { - jobject linearExtendedSRGB = env->GetStaticObjectField( - gColorSpace_Named_class, gColorSpace_Named_LinearExtendedSRGBFieldID); - colorSpace = env->CallStaticObjectMethod(gColorSpace_class, - gColorSpace_getMethodID, linearExtendedSRGB); - } else { - // Same here, no need to match + // An F16 Bitmap will always report that it is EXTENDED if + // it matches a ColorSpace that has an EXTENDED variant. if (decodeColorSpace->isSRGB()) { - jobject sRGB = env->GetStaticObjectField( - gColorSpace_Named_class, gColorSpace_Named_sRGBFieldID); - colorSpace = env->CallStaticObjectMethod(gColorSpace_class, - gColorSpace_getMethodID, sRGB); - } else if (decodeColorSpace.get() != nullptr) { - // Try to match against known RGB color spaces using the CIE XYZ D50 - // conversion matrix and numerical transfer function parameters - skcms_Matrix3x3 xyzMatrix; - LOG_ALWAYS_FATAL_IF(!decodeColorSpace->toXYZD50(&xyzMatrix)); - - skcms_TransferFunction transferParams; - // We can only handle numerical transfer functions at the moment - LOG_ALWAYS_FATAL_IF(!decodeColorSpace->isNumericalTransferFn(&transferParams)); - - jobject params = env->NewObject(gTransferParameters_class, - gTransferParameters_constructorMethodID, - transferParams.a, transferParams.b, transferParams.c, - transferParams.d, transferParams.e, transferParams.f, - transferParams.g); - - jfloatArray xyzArray = env->NewFloatArray(9); - jfloat xyz[9] = { - xyzMatrix.vals[0][0], - xyzMatrix.vals[1][0], - xyzMatrix.vals[2][0], - xyzMatrix.vals[0][1], - xyzMatrix.vals[1][1], - xyzMatrix.vals[2][1], - xyzMatrix.vals[0][2], - xyzMatrix.vals[1][2], - xyzMatrix.vals[2][2] - }; - env->SetFloatArrayRegion(xyzArray, 0, 9, xyz); - - colorSpace = env->CallStaticObjectMethod(gColorSpace_class, - gColorSpace_matchMethodID, xyzArray, params); - - if (colorSpace == nullptr) { - // We couldn't find an exact match, let's create a new color space - // instance with the 3x3 conversion matrix and transfer function - colorSpace = env->NewObject(gColorSpaceRGB_class, - gColorSpaceRGB_constructorMethodID, - env->NewStringUTF("Unknown"), xyzArray, params); - } - - env->DeleteLocalRef(xyzArray); + namedCS = env->GetStaticObjectField(gColorSpace_Named_class, + gColorSpace_Named_ExtendedSRGBFieldID); + } else if (decodeColorSpace == srgbLinear.get()) { + namedCS = env->GetStaticObjectField(gColorSpace_Named_class, + gColorSpace_Named_LinearExtendedSRGBFieldID); } + } else if (decodeColorSpace->isSRGB()) { + namedCS = env->GetStaticObjectField(gColorSpace_Named_class, + gColorSpace_Named_sRGBFieldID); + } else if (decodeColorSpace == srgbLinear.get()) { + namedCS = env->GetStaticObjectField(gColorSpace_Named_class, + gColorSpace_Named_LinearSRGBFieldID); + } + + if (namedCS) { + return env->CallStaticObjectMethod(gColorSpace_class, gColorSpace_getMethodID, namedCS); } + + // Try to match against known RGB color spaces using the CIE XYZ D50 + // conversion matrix and numerical transfer function parameters + skcms_Matrix3x3 xyzMatrix; + LOG_ALWAYS_FATAL_IF(!decodeColorSpace->toXYZD50(&xyzMatrix)); + + skcms_TransferFunction transferParams; + // We can only handle numerical transfer functions at the moment + LOG_ALWAYS_FATAL_IF(!decodeColorSpace->isNumericalTransferFn(&transferParams)); + + jobject params = env->NewObject(gTransferParameters_class, + gTransferParameters_constructorMethodID, + transferParams.a, transferParams.b, transferParams.c, + transferParams.d, transferParams.e, transferParams.f, + transferParams.g); + + jfloatArray xyzArray = env->NewFloatArray(9); + jfloat xyz[9] = { + xyzMatrix.vals[0][0], + xyzMatrix.vals[1][0], + xyzMatrix.vals[2][0], + xyzMatrix.vals[0][1], + xyzMatrix.vals[1][1], + xyzMatrix.vals[2][1], + xyzMatrix.vals[0][2], + xyzMatrix.vals[1][2], + xyzMatrix.vals[2][2] + }; + env->SetFloatArrayRegion(xyzArray, 0, 9, xyz); + + jobject colorSpace = env->CallStaticObjectMethod(gColorSpace_class, + gColorSpace_matchMethodID, xyzArray, params); + + if (colorSpace == nullptr) { + // We couldn't find an exact match, let's create a new color space + // instance with the 3x3 conversion matrix and transfer function + colorSpace = env->NewObject(gColorSpaceRGB_class, + gColorSpaceRGB_constructorMethodID, + env->NewStringUTF("Unknown"), xyzArray, params); + } + + env->DeleteLocalRef(xyzArray); return colorSpace; } @@ -658,6 +671,10 @@ int register_android_graphics_Graphics(JNIEnv* env) FindClassOrDie(env, "android/graphics/ColorSpace$Named")); gColorSpace_Named_sRGBFieldID = GetStaticFieldIDOrDie(env, gColorSpace_Named_class, "SRGB", "Landroid/graphics/ColorSpace$Named;"); + gColorSpace_Named_ExtendedSRGBFieldID = GetStaticFieldIDOrDie(env, + gColorSpace_Named_class, "EXTENDED_SRGB", "Landroid/graphics/ColorSpace$Named;"); + gColorSpace_Named_LinearSRGBFieldID = GetStaticFieldIDOrDie(env, + gColorSpace_Named_class, "LINEAR_SRGB", "Landroid/graphics/ColorSpace$Named;"); gColorSpace_Named_LinearExtendedSRGBFieldID = GetStaticFieldIDOrDie(env, gColorSpace_Named_class, "LINEAR_EXTENDED_SRGB", "Landroid/graphics/ColorSpace$Named;"); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index dc0d022d94c0..f80651c30d64 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -109,7 +109,13 @@ public: */ static sk_sp<SkColorSpace> getNativeColorSpace(jlong colorSpaceHandle); - static jobject getColorSpace(JNIEnv* env, sk_sp<SkColorSpace>& decodeColorSpace, + /** + * Return the android.graphics.ColorSpace Java object that corresponds to decodeColorSpace + * and decodeColorType. + * + * This may create a new object if none of the Named ColorSpaces match. + */ + static jobject getColorSpace(JNIEnv* env, SkColorSpace* decodeColorSpace, SkColorType decodeColorType); /** diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp index 2d83ac320733..9efcace06be3 100644 --- a/core/jni/android/graphics/ImageDecoder.cpp +++ b/core/jni/android/graphics/ImageDecoder.cpp @@ -506,9 +506,9 @@ static jstring ImageDecoder_nGetMimeType(JNIEnv* env, jobject /*clazz*/, jlong n static jobject ImageDecoder_nGetColorSpace(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { auto* codec = reinterpret_cast<ImageDecoder*>(nativePtr)->mCodec.get(); - auto colorType = codec->computeOutputColorType(codec->getInfo().colorType()); + auto colorType = codec->computeOutputColorType(kN32_SkColorType); sk_sp<SkColorSpace> colorSpace = codec->computeOutputColorSpace(colorType); - return GraphicsJNI::getColorSpace(env, colorSpace, colorType); + return GraphicsJNI::getColorSpace(env, colorSpace.get(), colorType); } static const JNINativeMethod gImageDecoderMethods[] = { diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index a2ed7d3391ed..bd998999ce63 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -1918,7 +1918,6 @@ static jint android_media_AudioSystem_setUidDeviceAffinities(JNIEnv *env, jobjec for (jint i = 0; i < nb; i++) { deviceTypesVector.push_back((audio_devices_t) typesPtr[i]); } - env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0); // check each address is a string and add device type/address to list for device affinity Vector<AudioDeviceTypeAddr> deviceVector; @@ -1932,6 +1931,7 @@ static jint android_media_AudioSystem_setUidDeviceAffinities(JNIEnv *env, jobjec AudioDeviceTypeAddr dev = AudioDeviceTypeAddr(typesPtr[i], address); deviceVector.add(dev); } + env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0); status_t status = AudioSystem::setUidDeviceAffinities((uid_t) uid, deviceVector); return (jint) nativeToJavaStatus(status); diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp index 95f99b760382..0ccc327f6d97 100644 --- a/core/jni/android_os_GraphicsEnvironment.cpp +++ b/core/jni/android_os_GraphicsEnvironment.cpp @@ -34,13 +34,16 @@ void setDriverPath(JNIEnv* env, jobject clazz, jstring path) { void setGpuStats_native(JNIEnv* env, jobject clazz, jstring driverPackageName, jstring driverVersionName, jlong driverVersionCode, - jstring appPackageName) { + jstring driverBuildDate, jstring appPackageName) { ScopedUtfChars driverPackageNameChars(env, driverPackageName); ScopedUtfChars driverVersionNameChars(env, driverVersionName); + ScopedUtfChars driverBuildDateChars(env, driverBuildDate); ScopedUtfChars appPackageNameChars(env, appPackageName); android::GraphicsEnv::getInstance().setGpuStats(driverPackageNameChars.c_str(), driverVersionNameChars.c_str(), - driverVersionCode, appPackageNameChars.c_str()); + driverVersionCode, + driverBuildDateChars.c_str(), + appPackageNameChars.c_str()); } void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jstring devOptIn, @@ -84,7 +87,7 @@ void setDebugLayersGLES_native(JNIEnv* env, jobject clazz, jstring layers) { const JNINativeMethod g_methods[] = { { "getCanLoadSystemLibraries", "()I", reinterpret_cast<void*>(getCanLoadSystemLibraries_native) }, { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) }, - { "setGpuStats", "(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;)V", reinterpret_cast<void*>(setGpuStats_native) }, + { "setGpuStats", "(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;)V", reinterpret_cast<void*>(setGpuStats_native) }, { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) }, { "getShouldUseAngle", "(Ljava/lang/String;)Z", reinterpret_cast<void*>(shouldUseAngle_native) }, { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) }, diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 8c7363021d3b..2aa5cb41d5ab 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -33,7 +33,6 @@ #include <private/EGL/cache.h> -#include <utils/Looper.h> #include <utils/RefBase.h> #include <utils/StrongPointer.h> #include <utils/Timers.h> @@ -144,52 +143,22 @@ private: uint32_t mRequestId; }; -class RenderingException : public MessageHandler { +class FrameCompleteWrapper : public LightRefBase<FrameCompleteWrapper> { public: - RenderingException(JavaVM* vm, const std::string& message) - : mVm(vm) - , mMessage(message) { - } - - virtual void handleMessage(const Message&) { - throwException(mVm, mMessage); - } - - static void throwException(JavaVM* vm, const std::string& message) { - JNIEnv* env = getenv(vm); - jniThrowException(env, "java/lang/IllegalStateException", message.c_str()); - } - -private: - JavaVM* mVm; - std::string mMessage; -}; - -class FrameCompleteWrapper : public MessageHandler { -public: - FrameCompleteWrapper(JNIEnv* env, jobject jobject) { - mLooper = Looper::getForThread(); - LOG_ALWAYS_FATAL_IF(!mLooper.get(), "Must create runnable on a Looper thread!"); + explicit FrameCompleteWrapper(JNIEnv* env, jobject jobject) { env->GetJavaVM(&mVm); mObject = env->NewGlobalRef(jobject); LOG_ALWAYS_FATAL_IF(!mObject, "Failed to make global ref"); } - virtual ~FrameCompleteWrapper() { + ~FrameCompleteWrapper() { releaseObject(); } - void postFrameComplete(int64_t frameNr) { - if (mObject) { - mFrameNr = frameNr; - mLooper->sendMessage(this, 0); - } - } - - virtual void handleMessage(const Message&) { + void onFrameComplete(int64_t frameNr) { if (mObject) { - ATRACE_FORMAT("frameComplete %" PRId64, mFrameNr); - getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, mFrameNr); + ATRACE_FORMAT("frameComplete %" PRId64, frameNr); + getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, frameNr); releaseObject(); } } @@ -197,8 +166,6 @@ public: private: JavaVM* mVm; jobject mObject; - sp<Looper> mLooper; - int64_t mFrameNr = -1; void releaseObject() { if (mObject) { @@ -211,16 +178,14 @@ private: class RootRenderNode : public RenderNode, ErrorHandler { public: explicit RootRenderNode(JNIEnv* env) : RenderNode() { - mLooper = Looper::getForThread(); - LOG_ALWAYS_FATAL_IF(!mLooper.get(), - "Must create RootRenderNode on a thread with a looper!"); env->GetJavaVM(&mVm); } virtual ~RootRenderNode() {} virtual void onError(const std::string& message) override { - mLooper->sendMessage(new RenderingException(mVm, message), 0); + JNIEnv* env = getenv(mVm); + jniThrowException(env, "java/lang/IllegalStateException", message.c_str()); } virtual void prepareTree(TreeInfo& info) override { @@ -249,14 +214,6 @@ public: info.errorHandler = nullptr; } - void sendMessage(const sp<MessageHandler>& handler) { - mLooper->sendMessage(handler, 0); - } - - void sendMessageDelayed(const sp<MessageHandler>& handler, nsecs_t delayInMs) { - mLooper->sendMessageDelayed(ms2ns(delayInMs), handler, 0); - } - void attachAnimatingNode(RenderNode* animatingNode) { mPendingAnimatingRenderNodes.push_back(animatingNode); } @@ -404,7 +361,6 @@ public: } private: - sp<Looper> mLooper; JavaVM* mVm; std::vector< sp<RenderNode> > mPendingAnimatingRenderNodes; std::set< sp<PropertyValuesAnimatorSet> > mPendingVectorDrawableAnimators; @@ -435,7 +391,9 @@ private: // the onFinished callback will then be ignored. sp<FinishAndInvokeListener> message = new FinishAndInvokeListener(anim); - sendMessageDelayed(message, remainingTimeInMs); + auto looper = Looper::getForThread(); + LOG_ALWAYS_FATAL_IF(looper == nullptr, "Not on a looper thread?"); + looper->sendMessageDelayed(ms2ns(remainingTimeInMs), message, 0); anim->clearOneShotListener(); } } @@ -463,7 +421,6 @@ public: virtual void runRemainingAnimations(TreeInfo& info) { AnimationContext::runRemainingAnimations(info); mRootNode->runVectorDrawableAnimators(this, info); - postOnFinishedEvents(); } virtual void pauseAnimators() override { @@ -471,27 +428,16 @@ public: } virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) { - OnFinishedEvent event(animator, listener); - mOnFinishedEvents.push_back(event); + listener->onAnimationFinished(animator); } virtual void destroy() { AnimationContext::destroy(); mRootNode->detachAnimators(); - postOnFinishedEvents(); } private: sp<RootRenderNode> mRootNode; - std::vector<OnFinishedEvent> mOnFinishedEvents; - - void postOnFinishedEvents() { - if (mOnFinishedEvents.size()) { - sp<InvokeAnimationListeners> message - = new InvokeAnimationListeners(mOnFinishedEvents); - mRootNode->sendMessage(message); - } - } }; class ContextFactoryImpl : public IContextFactory { @@ -958,7 +904,7 @@ static void android_view_ThreadedRenderer_setFrameCompleteCallback(JNIEnv* env, } else { sp<FrameCompleteWrapper> wrapper = new FrameCompleteWrapper{env, callback}; proxy->setFrameCompleteCallback([wrapper](int64_t frameNr) { - wrapper->postFrameComplete(frameNr); + wrapper->onFrameComplete(frameNr); }); } } diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 5cecf66a593c..7b4e4ea43415 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -25,6 +25,8 @@ #define LOG_TAG "Zygote" +#include <async_safe/log.h> + // sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc #include <sys/mount.h> #include <linux/fs.h> @@ -303,27 +305,23 @@ static void SigChldHandler(int /*signal_number*/) { int saved_errno = errno; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { - // Log process-death status that we care about. In general it is - // not safe to call LOG(...) from a signal handler because of - // possible reentrancy. However, we know a priori that the - // current implementation of LOG() is safe to call from a SIGCHLD - // handler in the zygote process. If the LOG() implementation - // changes its locking strategy or its use of syscalls within the - // lazy-init critical section, its use here may become unsafe. + // Log process-death status that we care about. if (WIFEXITED(status)) { - ALOGI("Process %d exited cleanly (%d)", pid, WEXITSTATUS(status)); + async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG, + "Process %d exited cleanly (%d)", pid, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { - ALOGI("Process %d exited due to signal (%d)", pid, WTERMSIG(status)); - if (WCOREDUMP(status)) { - ALOGI("Process %d dumped core.", pid); - } + async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG, + "Process %d exited due to signal %d (%s)%s", pid, + WTERMSIG(status), strsignal(WTERMSIG(status)), + WCOREDUMP(status) ? "; core dumped" : ""); } // If the just-crashed process is the system_server, bring down zygote // so that it is restarted by init and system server will be restarted // from there. if (pid == gSystemServerPid) { - ALOGE("Exit zygote because system server (%d) has terminated", pid); + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, + "Exit zygote because system server (pid %d) has terminated", pid); kill(getpid(), SIGKILL); } @@ -336,14 +334,17 @@ static void SigChldHandler(int /*signal_number*/) { // Note that we shouldn't consider ECHILD an error because // the secondary zygote might have no children left to wait for. if (pid < 0 && errno != ECHILD) { - ALOGW("Zygote SIGCHLD error in waitpid: %s", strerror(errno)); + async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, + "Zygote SIGCHLD error in waitpid: %s", strerror(errno)); } if (blastulas_removed > 0) { if (write(gBlastulaPoolEventFD, &blastulas_removed, sizeof(blastulas_removed)) == -1) { // If this write fails something went terribly wrong. We will now kill // the zygote and let the system bring it back up. - ALOGE("Zygote failed to write to blastula pool event FD: %s", strerror(errno)); + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, + "Zygote failed to write to blastula pool event FD: %s", + strerror(errno)); kill(getpid(), SIGKILL); } } @@ -612,7 +613,7 @@ static void CreateDir(const std::string& dir, } } -static void CreatePkgSandbox(uid_t uid, const std::string& package_name, fail_fn_t fail_fn) { +static void CreatePkgSandboxTarget(uid_t uid, const std::string& package_name, fail_fn_t fail_fn) { // Create /mnt/user/0/package/<package-name> userid_t user_id = multiuser_get_user_id(uid); std::string pkg_sandbox_dir = StringPrintf("/mnt/user/%d", user_id); @@ -622,7 +623,7 @@ static void CreatePkgSandbox(uid_t uid, const std::string& package_name, fail_fn CreateDir(pkg_sandbox_dir, 0700, AID_ROOT, AID_ROOT, fail_fn); StringAppendF(&pkg_sandbox_dir, "/%s", package_name.c_str()); - CreateDir(pkg_sandbox_dir, 0700, AID_ROOT, AID_ROOT, fail_fn); + CreateDir(pkg_sandbox_dir, 0755, uid, uid, fail_fn); } static void BindMount(const std::string& sourceDir, const std::string& targetDir, @@ -642,29 +643,98 @@ static void MountPkgSpecificDir(const std::string& mntSourceRoot, fail_fn_t fail_fn) { std::string mntSourceDir = StringPrintf("%s/Android/%s/%s", mntSourceRoot.c_str(), dirName, packageName.c_str()); - CreateDir(mntSourceDir, 0755, uid, uid, fail_fn); std::string mntTargetDir = StringPrintf("%s/Android/%s/%s", mntTargetRoot.c_str(), dirName, packageName.c_str()); - CreateDir(mntTargetDir, 0755, uid, uid, fail_fn); BindMount(mntSourceDir, mntTargetDir, fail_fn); } +static void CreateSubDirs(int dirfd, const std::string& parentDirPath, + const std::vector<std::string>& subDirs, + fail_fn_t fail_fn) { + for (auto& dirName : subDirs) { + struct stat sb; + if (TEMP_FAILURE_RETRY(fstatat(dirfd, dirName.c_str(), &sb, 0)) == 0) { + if (S_ISDIR(sb.st_mode)) { + continue; + } else if (TEMP_FAILURE_RETRY(unlinkat(dirfd, dirName.c_str(), 0)) == -1) { + fail_fn(CREATE_ERROR("Failed to unlinkat on %s/%s: %s", + parentDirPath.c_str(), dirName.c_str(), strerror(errno))); + } + } else if (errno != ENOENT) { + fail_fn(CREATE_ERROR("Failed to fstatat on %s/%s: %s", + parentDirPath.c_str(), dirName.c_str(), strerror(errno))); + } + if (TEMP_FAILURE_RETRY(mkdirat(dirfd, dirName.c_str(), 0700)) == -1) { + fail_fn(CREATE_ERROR("Failed to mkdirat on %s/%s: %s", + parentDirPath.c_str(), dirName.c_str(), strerror(errno))); + } + } +} + +static void EnsurePkgSpecificDirs(const std::string& path, + const std::vector<std::string>& packageNames, + bool createSandboxDir, + fail_fn_t fail_fn) { + std::string androidDir = StringPrintf("%s/Android", path.c_str()); + android::base::unique_fd androidFd( + open(androidDir.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC)); + if (androidFd.get() < 0) { + if (errno == ENOENT || errno == ENOTDIR) { + if (errno == ENOTDIR && TEMP_FAILURE_RETRY(unlink(androidDir.c_str())) == -1) { + fail_fn(CREATE_ERROR("Failed to unlink %s: %s", + androidDir.c_str(), strerror(errno))); + } + if (TEMP_FAILURE_RETRY(mkdir(androidDir.c_str(), 0700)) == -1) { + fail_fn(CREATE_ERROR("Failed to mkdir %s: %s", + androidDir.c_str(), strerror(errno))); + } + androidFd.reset(open(androidDir.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC)); + } -static void createPkgSpecificDirRoots(const std::string& parentDir, - bool createSandbox, - mode_t mode, uid_t uid, gid_t gid, - fail_fn_t fail_fn) { - std::string androidDir = StringPrintf("%s/Android", parentDir.c_str()); - CreateDir(androidDir, mode, uid, gid, fail_fn); - std::vector<std::string> dirs = {"data", "media", "obb"}; - if (createSandbox) { - dirs.push_back("sandbox"); + if (androidFd.get() < 0) { + fail_fn(CREATE_ERROR("Failed to open %s: %s", androidDir.c_str(), strerror(errno))); + } + } + + std::vector<std::string> dataMediaObbDirs = {"data", "media", "obb"}; + if (createSandboxDir) { + dataMediaObbDirs.push_back("sandbox"); + } + CreateSubDirs(androidFd.get(), androidDir, dataMediaObbDirs, fail_fn); + if (createSandboxDir) { + dataMediaObbDirs.pop_back(); + } + for (auto& dirName : dataMediaObbDirs) { + std::string dataDir = StringPrintf("%s/%s", androidDir.c_str(), dirName.c_str()); + android::base::unique_fd dataFd( + openat(androidFd, dirName.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC)); + if (dataFd.get() < 0) { + fail_fn(CREATE_ERROR("Failed to openat %s/%s: %s", + androidDir.c_str(), dirName.c_str(), strerror(errno))); + } + CreateSubDirs(dataFd.get(), dataDir, packageNames, fail_fn); + } +} + +static void CreatePkgSandboxSource(const std::string& sandboxSource, fail_fn_t fail_fn) { + + struct stat sb; + if (TEMP_FAILURE_RETRY(stat(sandboxSource.c_str(), &sb)) == 0) { + if (S_ISDIR(sb.st_mode)) { + return; + } else if (TEMP_FAILURE_RETRY(unlink(sandboxSource.c_str())) == -1) { + fail_fn(CREATE_ERROR("Failed to unlink %s: %s", + sandboxSource.c_str(), strerror(errno))); + } + } else if (errno != ENOENT) { + fail_fn(CREATE_ERROR("Failed to stat %s: %s", + sandboxSource.c_str(), strerror(errno))); } - for (auto& dir : dirs) { - std::string path = StringPrintf("%s/%s", androidDir.c_str(), dir.c_str()); - CreateDir(path, mode, uid, gid, fail_fn); + if (TEMP_FAILURE_RETRY(mkdir(sandboxSource.c_str(), 0700)) == -1) { + fail_fn(CREATE_ERROR("Failed to mkdir %s: %s", + sandboxSource.c_str(), strerror(errno))); } } @@ -680,21 +750,21 @@ static void PreparePkgSpecificDirs(const std::vector<std::string>& packageNames, StringAppendF(&mntTarget, "/%d", userId); } - if (TEMP_FAILURE_RETRY(access(mntSource.c_str(), F_OK)) < 0) { + if (TEMP_FAILURE_RETRY(access(mntSource.c_str(), F_OK)) == -1) { ALOGE("Can't access %s: %s", mntSource.c_str(), strerror(errno)); continue; } - // Create /mnt/runtime/write/emulated/0/Android/{data,media,obb,sandbox} - createPkgSpecificDirRoots(mntSource, true, 0700, AID_ROOT, AID_ROOT, fail_fn); + // Ensure /mnt/runtime/write/emulated/0/Android/{data,media,obb} + EnsurePkgSpecificDirs(mntSource, packageNames, true, fail_fn); std::string sandboxSource = StringPrintf("%s/Android/sandbox/%s", mntSource.c_str(), sandboxId.c_str()); - CreateDir(sandboxSource, 0755, uid, uid, fail_fn); + CreatePkgSandboxSource(sandboxSource, fail_fn); BindMount(sandboxSource, mntTarget, fail_fn); - // Create /storage/emulated/0/Android/{data,media,obb} - createPkgSpecificDirRoots(mntTarget, false, 0755, uid, uid, fail_fn); + // Ensure /storage/emulated/0/Android/{data,media,obb} + EnsurePkgSpecificDirs(mntTarget, packageNames, false, fail_fn); for (auto& package : packageNames) { MountPkgSpecificDir(mntSource, mntTarget, package, uid, "data", fail_fn); MountPkgSpecificDir(mntSource, mntTarget, package, uid, "media", fail_fn); @@ -775,15 +845,14 @@ static void MountEmulatedStorage(uid_t uid, jint mount_mode, userid_t user_id = multiuser_get_user_id(uid); std::string pkgSandboxDir = StringPrintf("/mnt/user/%d/package/%s", user_id, package_name.c_str()); - struct stat sb; bool sandboxAlreadyCreated = true; - if (TEMP_FAILURE_RETRY(lstat(pkgSandboxDir.c_str(), &sb)) == -1) { + if (TEMP_FAILURE_RETRY(access(pkgSandboxDir.c_str(), F_OK)) == -1) { if (errno == ENOENT) { ALOGD("Sandbox not yet created for %s", pkgSandboxDir.c_str()); sandboxAlreadyCreated = false; - CreatePkgSandbox(uid, package_name, fail_fn); + CreatePkgSandboxTarget(uid, package_name, fail_fn); } else { - fail_fn(CREATE_ERROR("Failed to lstat %s: %s", + fail_fn(CREATE_ERROR("Failed to access %s: %s", pkgSandboxDir.c_str(), strerror(errno))); } } @@ -794,7 +863,7 @@ static void MountEmulatedStorage(uid_t uid, jint mount_mode, pkgSandboxDir.c_str(), strerror(errno))); } - if (access("/storage/obb_mount", F_OK) == 0) { + if (TEMP_FAILURE_RETRY(access("/storage/obb_mount", F_OK)) == 0) { if (mount_mode != MOUNT_EXTERNAL_INSTALLER) { remove("/storage/obb_mount"); } @@ -1384,8 +1453,8 @@ static jlong CalculateCapabilities(JNIEnv* env, jint uid, jint gid, jintArray gi RuntimeAbort(env, __LINE__, "Bad gids array"); } - for (int gid_index = gids_num; --gids_num >= 0;) { - if (native_gid_proxy[gid_index] == AID_WAKELOCK) { + for (int gids_index = 0; gids_index < gids_num; ++gids_index) { + if (native_gid_proxy[gids_index] == AID_WAKELOCK) { gid_wakelock_found = true; break; } diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto index a4167c187194..516fa7b9336b 100644 --- a/core/proto/android/os/batterystats.proto +++ b/core/proto/android/os/batterystats.proto @@ -58,6 +58,10 @@ message ControllerActivityProto { optional int64 duration_ms = 2; } repeated TxLevel tx = 4; + + // Total rail charge consumed by the monitored rails by the controller. The value may + // always be 0 if the device doesn't support monitored rail calculations. + optional double monitored_rail_charge_mah = 5; } message SystemProto { diff --git a/core/proto/android/server/connectivity/Android.bp b/core/proto/android/server/connectivity/Android.bp new file mode 100644 index 000000000000..c0ac2cb8f800 --- /dev/null +++ b/core/proto/android/server/connectivity/Android.bp @@ -0,0 +1,25 @@ +// 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. + +java_library_static { + name: "datastallprotosnano", + proto: { + type: "nano", + }, + srcs: [ + "data_stall_event.proto", + ], + sdk_version: "system_current", + no_framework_libs: true, +}
\ No newline at end of file diff --git a/core/res/res/drawable/ic_qs_night_display_on.xml b/core/res/res/drawable/ic_qs_night_display_on.xml index 35907cc83fe0..a4755ee256e2 100644 --- a/core/res/res/drawable/ic_qs_night_display_on.xml +++ b/core/res/res/drawable/ic_qs_night_display_on.xml @@ -1,5 +1,5 @@ <!-- - Copyright (C) 2017 The Android Open Source Project + Copyright (C) 2019 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,14 +14,13 @@ limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="64dp" - android:height="64dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0"> - <group - android:translateX="-1.0"> - <path - android:pathData="M13,12c0,-3.57 2.2,-6.62 5.31,-7.87 0.89,-0.36 0.75,-1.69 -0.19,-1.9 -1.1,-0.24 -2.27,-0.3 -3.48,-0.14 -4.51,0.6 -8.12,4.31 -8.59,8.83C5.43,16.93 10.12,22 16,22c0.73,0 1.43,-0.08 2.12,-0.23 0.95,-0.21 1.1,-1.53 0.2,-1.9A8.471,8.471 0,0 1,13 12z" - android:fillColor="#FFF"/> - </group> -</vector>
\ No newline at end of file + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + <path + android:fillColor="#FFFFFF" + android:pathData="M6.28,4.81c0,0.16,0.01,0.32,0.02,0.48c0.38,6.58,5.83,12.03,12.41,12.41c0.16,0.01,0.32,0.02,0.47,0.02 c-1.58,1.2-3.53,1.88-5.56,1.88c-0.46,0-0.93-0.03-1.4-0.1c-3.96-0.58-7.13-3.75-7.71-7.71C4.13,9.24,4.8,6.75,6.28,4.81 M8.27,0.6 c-0.08,0-0.17,0.02-0.25,0.07c-3.8,2.2-6.2,6.56-5.49,11.4c0.7,4.82,4.59,8.7,9.4,9.4c0.57,0.08,1.13,0.12,1.69,0.12 c4.15,0,7.78-2.26,9.72-5.62c0.2-0.35-0.07-0.76-0.44-0.76c-0.05,0-0.1,0.01-0.15,0.02c-1.03,0.31-2.12,0.48-3.25,0.48 c-0.22,0-0.44-0.01-0.67-0.02C13.23,15.38,8.62,10.77,8.29,5.17C8.21,3.81,8.38,2.49,8.75,1.26C8.86,0.91,8.59,0.6,8.27,0.6 L8.27,0.6z" /> +</vector> + diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml index 3683bfd02e87..10798ad51792 100644 --- a/core/res/res/layout/chooser_grid.xml +++ b/core/res/res/layout/chooser_grid.xml @@ -20,12 +20,10 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:maxWidth="@dimen/resolver_max_width" android:maxCollapsedHeight="288dp" android:maxCollapsedHeightSmall="56dp" android:id="@id/contentPanel"> - <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" @@ -153,8 +151,9 @@ android:background="?attr/colorBackgroundFloating"> <LinearLayout - android:layout_width="match_parent" + android:layout_width="@dimen/chooser_preview_width" android:layout_height="wrap_content" + android:layout_gravity="center" android:orientation="horizontal" android:paddingLeft="@dimen/chooser_edge_margin_normal" android:paddingRight="@dimen/chooser_edge_margin_normal" @@ -182,8 +181,9 @@ <!-- Required sub-layout so we can get the nice rounded corners--> <!-- around this section --> <LinearLayout - android:layout_width="match_parent" + android:layout_width="@dimen/chooser_preview_width" android:layout_height="wrap_content" + android:layout_gravity="center" android:orientation="horizontal" android:layout_marginLeft="@dimen/chooser_edge_margin_thin" android:layout_marginRight="@dimen/chooser_edge_margin_thin" @@ -224,8 +224,9 @@ android:background="?attr/colorBackgroundFloating"> <LinearLayout - android:layout_width="match_parent" + android:layout_width="@dimen/chooser_preview_width" android:layout_height="wrap_content" + android:layout_gravity="center" android:orientation="horizontal" android:paddingLeft="@dimen/chooser_edge_margin_normal" android:paddingRight="@dimen/chooser_edge_margin_normal" diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml index 351bd818b09a..9e87a47219f3 100644 --- a/core/res/res/values-land/dimens.xml +++ b/core/res/res/values-land/dimens.xml @@ -76,4 +76,6 @@ <!-- Floating toolbar dimensions --> <dimen name="floating_toolbar_preferred_width">544dp</dimen> + <dimen name="chooser_preview_width">480dp</dimen> + </resources> diff --git a/core/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml index 949c12e7d1da..0721f6fb3802 100644 --- a/core/res/res/values-night/themes_device_defaults.xml +++ b/core/res/res/values-night/themes_device_defaults.xml @@ -65,4 +65,7 @@ easier. <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert" /> <style name="Theme.DeviceDefault.DayNight" parent="Theme.DeviceDefault" /> + + <style name="ThemeOverlay.DeviceDefault.Accent.DayNight" + parent="@style/ThemeOverlay.DeviceDefault.Accent" /> </resources>
\ No newline at end of file diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 46e14b41960c..224f54c64b63 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -4349,14 +4349,18 @@ <attr name="indeterminate" format="boolean" /> <!-- Restricts to ONLY indeterminate mode (state-keeping progress mode will not work). --> <attr name="indeterminateOnly" format="boolean" /> - <!-- Drawable used for the indeterminate mode. --> + <!-- Drawable used for the indeterminate mode. One that implements Animatable offers more + control over the animation.--> <attr name="indeterminateDrawable" format="reference" /> <!-- Drawable used for the progress mode. --> <attr name="progressDrawable" format="reference" /> - <!-- Duration of the indeterminate animation. --> + <!-- Duration of the indeterminate animation. Only affects the indeterminate animation + if the indeterminate Drawable does not implement + android.graphics.drawable.Animatable. --> <attr name="indeterminateDuration" format="integer" min="1" /> - <!-- Defines how the indeterminate mode should behave when the progress - reaches max. --> + <!-- Defines how the indeterminate mode should behave when the progress reaches max. Only + affects the indeterminate animation if the indeterminate Drawable does not implement + android.graphics.drawable.Animatable. --> <attr name="indeterminateBehavior"> <!-- Progress starts over from 0. --> <enum name="repeat" value="1" /> @@ -4367,6 +4371,9 @@ <attr name="maxWidth" /> <attr name="minHeight" format="dimension" /> <attr name="maxHeight" /> + <!-- Sets the acceleration curve for the indeterminate animation. Defaults to a linear + interpolation. Only affects the indeterminate animation if the indeterminate Drawable + does not implement android.graphics.drawable.Animatable.--> <attr name="interpolator" format="reference" /> <!-- Timeout between frames of animation in milliseconds. {@deprecated Not used by the framework}. --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index d14164f69850..e65e7da57e58 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -46,6 +46,8 @@ <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item> <item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item> <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_camera</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_microphone</xliff:g></item> <item><xliff:g id="id">@string/status_bar_location</xliff:g></item> <item><xliff:g id="id">@string/status_bar_mute</xliff:g></item> <item><xliff:g id="id">@string/status_bar_volume</xliff:g></item> @@ -55,8 +57,6 @@ <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item> <item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item> <item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_microphone</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_camera</xliff:g></item> <item><xliff:g id="id">@string/status_bar_battery</xliff:g></item> </string-array> @@ -869,6 +869,9 @@ which means to get a larger screen. --> <bool name="config_lidControlsDisplayFold">false</bool> + <!-- Indicate the display area rect for foldable devices in folded state. --> + <string name="config_foldedArea"></string> + <!-- Desk dock behavior --> <!-- The number of degrees to rotate the display when the device is in a desk dock. @@ -3252,7 +3255,7 @@ skinny aspect ratio that is not expected to be widely used. --> <item name="config_pictureInPictureMinAspectRatio" format="float" type="dimen">0.41841004184</item> - <!-- The minimum aspect ratio (width/height) that is supported for picture-in-picture. Any + <!-- The maximum aspect ratio (width/height) that is supported for picture-in-picture. Any ratio larger than this is considered to wide and short to be usable. Currently 2.39:1. --> <item name="config_pictureInPictureMaxAspectRatio" format="float" type="dimen">2.39</item> @@ -3273,6 +3276,11 @@ --> <integer name="config_dockedStackDividerSnapMode">0</integer> + <!-- The maximum aspect ratio (longerSide/shorterSide) that is treated as close-to-square. If + config_forceDefaultOrientation is set to true, the rotation on a close-to-square display + will be fixed. --> + <item name="config_closeToSquareDisplayMaxAspectRatio" format="float" type="dimen">1.333</item> + <!-- List of comma separated package names for which we the system will not show crash, ANR, etc. dialogs. --> <string translatable="false" name="config_appsNotReportingCrashes"></string> @@ -3906,19 +3914,19 @@ <!-- See DisplayWhiteBalanceController. A float array containing a list of ambient color temperatures, in Kelvin. This array, - together with config_displayWhiteBalanceDisplayTemperatureValues, is used to generate a + together with config_displayWhiteBalanceDisplayColorTemperatures, is used to generate a lookup table used in DisplayWhiteBalanceController. This lookup table is used to map ambient color temperature readings to a target color temperature for the display. This table is optional. If used, this array must, 1) Contain at least two entries - 2) Be the same length as config_displayWhiteBalanceDisplayTemperatureValues. --> - <array name="config_displayWhiteBalanceAmbientTemperatureValues"> + 2) Be the same length as config_displayWhiteBalanceDisplayColorTemperatures. --> + <array name="config_displayWhiteBalanceAmbientColorTemperatures"> </array> <!-- See DisplayWhiteBalanceController. An array containing a list of display color temperatures, in Kelvin. See - config_displayWhiteBalanceAmbientTemperatureValues for additional details. + config_displayWhiteBalanceAmbientColorTemperatures for additional details. The same restrictions apply to this array. --> - <array name="config_displayWhiteBalanceDisplayTemperatureValues"> + <array name="config_displayWhiteBalanceDisplayColorTemperatures"> </array> </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 9f86f8416fb9..39cbd269c75a 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -721,4 +721,5 @@ <dimen name="chooser_edge_margin_thin">16dp</dimen> <dimen name="chooser_edge_margin_normal">24dp</dimen> <dimen name="chooser_preview_image_font_size">20sp</dimen> + <dimen name="chooser_preview_width">-1px</dimen> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 5e65605a4c65..3580dd475ab9 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2951,6 +2951,8 @@ <public-group type="style" first-id="0x010302e2"> <!-- @hide @SystemApi --> <public name="Theme.DeviceDefault.DocumentsUI" /> + <public name="Theme.DeviceDefault.DayNight" /> + <public name="ThemeOverlay.DeviceDefault.Accent.DayNight" /> </public-group> <public-group type="id" first-id="0x01020046"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 0cdf38849d8b..5948f2988bc4 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3712,7 +3712,7 @@ <string name="ext_media_browse_action">Explore</string> <!-- Notification action to transfer media [CHAR LIMIT=40] --> - <string name="ext_media_seamless_action">Seamless transfer</string> + <string name="ext_media_seamless_action">Switch output</string> <!-- Notification title when external media is missing [CHAR LIMIT=30] --> <string name="ext_media_missing_title"><xliff:g id="name" example="SD card">%s</xliff:g> missing</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a1bafbf8de69..7ef5e022c1c4 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -403,6 +403,7 @@ <java-symbol type="integer" name="config_defaultPictureInPictureGravity" /> <java-symbol type="dimen" name="config_pictureInPictureMinAspectRatio" /> <java-symbol type="dimen" name="config_pictureInPictureMaxAspectRatio" /> + <java-symbol type="dimen" name="config_closeToSquareDisplayMaxAspectRatio" /> <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_threshold" /> <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_factor" /> <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_threshold" /> @@ -2755,6 +2756,7 @@ <java-symbol type="dimen" name="chooser_edge_margin_thin" /> <java-symbol type="dimen" name="chooser_edge_margin_normal" /> <java-symbol type="dimen" name="chooser_preview_image_font_size"/> + <java-symbol type="dimen" name="chooser_preview_width" /> <java-symbol type="layout" name="chooser_grid" /> <java-symbol type="layout" name="resolve_grid_item" /> <java-symbol type="id" name="day_picker_view_pager" /> @@ -3584,7 +3586,10 @@ <java-symbol type="integer" name="config_defaultRingVibrationIntensity" /> <java-symbol type="bool" name="config_maskMainBuiltInDisplayCutout" /> + + <!-- For Foldables --> <java-symbol type="bool" name="config_lidControlsDisplayFold" /> + <java-symbol type="string" name="config_foldedArea" /> <java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" /> <java-symbol type="array" name="config_disableApkUnlessMatchedSku_skus_list" /> @@ -3651,7 +3656,7 @@ <java-symbol type="array" name="config_displayWhiteBalanceDecreaseThresholds" /> <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientBrightnessThreshold" /> <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperature" /> - <java-symbol type="array" name="config_displayWhiteBalanceAmbientTemperatureValues" /> - <java-symbol type="array" name="config_displayWhiteBalanceDisplayTemperatureValues" /> + <java-symbol type="array" name="config_displayWhiteBalanceAmbientColorTemperatures" /> + <java-symbol type="array" name="config_displayWhiteBalanceDisplayColorTemperatures" /> <java-symbol type="drawable" name="ic_action_open" /> </resources> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index 160350878349..194c86c2fb81 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -1652,6 +1652,7 @@ easier. <style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" /> + <!-- DeviceDefault theme for day/night activities. --> <style name="Theme.DeviceDefault.DayNight" parent="Theme.DeviceDefault.Light" /> <!-- Theme used for the intent picker activity. --> @@ -1697,6 +1698,10 @@ easier. <item name="colorAccent">@color/accent_device_default_light</item> </style> + <!-- Theme overlay that replaces colorAccent with the colorAccent from {@link #Theme_DeviceDefault_DayNight}. --> + <style name="ThemeOverlay.DeviceDefault.Accent.DayNight" + parent="@style/ThemeOverlay.DeviceDefault.Accent.Light" /> + <style name="ThemeOverlay.DeviceDefault.Dark.ActionBar.Accent" parent="ThemeOverlay.Material.Dark.ActionBar"> <item name="colorAccent">@color/accent_device_default_dark</item> </style> diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java index f325d8943ea3..a97c3fa362eb 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java @@ -15,6 +15,7 @@ */ package android.view.contentcapture; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED; @@ -174,7 +175,8 @@ public class ContentCaptureEventTest { assertThat(event.getIds()).isNull(); assertThat(event.getText()).isNull(); assertThat(event.getViewNode()).isNull(); - final ContentCaptureContext clientContext = event.getClientContext(); + final ContentCaptureContext clientContext = event.getContentCaptureContext(); + assertThat(clientContext).isNotNull(); assertThat(clientContext.getAction()).isEqualTo("WHATEVER"); } @@ -205,9 +207,44 @@ public class ContentCaptureEventTest { assertThat(event.getIds()).isNull(); assertThat(event.getText()).isNull(); assertThat(event.getViewNode()).isNull(); - assertThat(event.getClientContext()).isNull(); + assertThat(event.getContentCaptureContext()).isNull(); } + + @Test + public void testContextUpdated_directly() { + final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_CONTEXT_UPDATED) + .setClientContext(mClientContext); + assertThat(event).isNotNull(); + assertContextUpdatedEvent(event); + } + + @Test + public void testContextUpdated_throughParcel() { + final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_CONTEXT_UPDATED) + .setClientContext(mClientContext); + assertThat(event).isNotNull(); + final ContentCaptureEvent clone = cloneThroughParcel(event); + assertContextUpdatedEvent(clone); + } + + private void assertContextUpdatedEvent(ContentCaptureEvent event) { + assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_UPDATED); + assertThat(event.getEventTime()).isAtLeast(MY_EPOCH); + assertThat(event.getSessionId()).isEqualTo("42"); + assertThat(event.getParentSessionId()).isNull(); + assertThat(event.getId()).isNull(); + assertThat(event.getIds()).isNull(); + assertThat(event.getText()).isNull(); + assertThat(event.getViewNode()).isNull(); + final ContentCaptureContext clientContext = event.getContentCaptureContext(); + assertThat(clientContext).isNotNull(); + assertThat(clientContext.getAction()).isEqualTo("WHATEVER"); + } + + // TODO(b/123036895): add test for all events type (right now we're just testing the 3 types + // that use logic to write to parcel + private ContentCaptureEvent cloneThroughParcel(ContentCaptureEvent event) { Parcel parcel = Parcel.obtain(); diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java index 34fdebfdf348..b6717e16256f 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java @@ -160,5 +160,10 @@ public class ContentCaptureSessionTest { public void internalNotifyViewHierarchyEvent(boolean started) { throw new UnsupportedOperationException("should not have been called"); } + + @Override + public void updateContentCaptureContext(ContentCaptureContext context) { + throw new UnsupportedOperationException("should not have been called"); + } } } diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 915cf954f665..f19e44c2f8a0 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -207,7 +207,7 @@ applications that come with the platform <permission name="android.permission.USE_RESERVED_DISK"/> </privapp-permissions> - <privapp-permissions package="com.android.mainline.networkstack"> + <privapp-permissions package="com.android.networkstack"> <permission name="android.permission.ACCESS_NETWORK_CONDITIONS"/> <permission name="android.permission.CONNECTIVITY_INTERNAL"/> <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/> diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 18f0cae4733c..81356710cce6 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -891,8 +891,10 @@ public final class Bitmap implements Parcelable { } } + ColorSpace cs = source.getColorSpace(); + if (m == null || m.isIdentity()) { - bitmap = createBitmap(neww, newh, newConfig, source.hasAlpha()); + bitmap = createBitmap(null, neww, newh, newConfig, source.hasAlpha(), cs); paint = null; // not needed } else { final boolean transformed = !m.rectStaysRect(); @@ -906,9 +908,14 @@ public final class Bitmap implements Parcelable { if (transformed) { if (transformedConfig != Config.ARGB_8888 && transformedConfig != Config.RGBA_F16) { transformedConfig = Config.ARGB_8888; + if (cs == null) { + cs = ColorSpace.get(ColorSpace.Named.SRGB); + } } } - bitmap = createBitmap(neww, newh, transformedConfig, transformed || source.hasAlpha()); + + bitmap = createBitmap(null, neww, newh, transformedConfig, + transformed || source.hasAlpha(), cs); paint = new Paint(); paint.setFilterBitmap(filter); @@ -917,8 +924,6 @@ public final class Bitmap implements Parcelable { } } - nativeCopyColorSpace(source.mNativePtr, bitmap.mNativePtr); - // The new bitmap was created from a known bitmap source so assume that // they use the same density bitmap.mDensity = source.mDensity; @@ -1000,10 +1005,10 @@ public final class Bitmap implements Parcelable { * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to * mark the bitmap as opaque. Doing so will clear the bitmap in black * instead of transparent. - * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16}, - * {@link ColorSpace.Named#EXTENDED_SRGB scRGB} is assumed, and if the - * config is not {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB} - * is assumed. + * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16} + * and {@link ColorSpace.Named#SRGB sRGB} or + * {@link ColorSpace.Named#LINEAR_SRGB Linear sRGB} is provided then the + * corresponding extended range variant is assumed. * * @throws IllegalArgumentException if the width or height are <= 0, if * Config is Config.HARDWARE (because hardware bitmaps are always @@ -1055,10 +1060,10 @@ public final class Bitmap implements Parcelable { * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to * mark the bitmap as opaque. Doing so will clear the bitmap in black * instead of transparent. - * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16}, - * {@link ColorSpace.Named#EXTENDED_SRGB scRGB} is assumed, and if the - * config is not {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB} - * is assumed. + * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16} + * and {@link ColorSpace.Named#SRGB sRGB} or + * {@link ColorSpace.Named#LINEAR_SRGB Linear sRGB} is provided then the + * corresponding extended range variant is assumed. * * @throws IllegalArgumentException if the width or height are <= 0, if * Config is Config.HARDWARE (because hardware bitmaps are always @@ -1075,22 +1080,12 @@ public final class Bitmap implements Parcelable { if (config == Config.HARDWARE) { throw new IllegalArgumentException("can't create mutable bitmap with Config.HARDWARE"); } - if (colorSpace == null) { + if (colorSpace == null && config != Config.ALPHA_8) { throw new IllegalArgumentException("can't create bitmap without a color space"); } - if (config != Config.ARGB_8888) { - if (config == Config.RGBA_F16) { - // FIXME: This should be LINEAR_EXTENDED_SRGB, but that would fail a CTS test. See - // b/120960866. SRGB matches the old (incorrect) behavior. - //colorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB); - colorSpace = ColorSpace.get(ColorSpace.Named.SRGB); - } else { - colorSpace = ColorSpace.get(ColorSpace.Named.SRGB); - } - } Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true, - colorSpace.getNativeInstance()); + colorSpace == null ? 0 : colorSpace.getNativeInstance()); if (display != null) { bm.mDensity = display.densityDpi; @@ -1514,6 +1509,9 @@ public final class Bitmap implements Parcelable { * Convenience method that returns the width of this bitmap divided * by the density scale factor. * + * Returns the bitmap's width multiplied by the ratio of the target density to the bitmap's + * source density + * * @param targetDensity The density of the target canvas of the bitmap. * @return The scaled width of this bitmap, according to the density scale factor. */ @@ -1525,6 +1523,9 @@ public final class Bitmap implements Parcelable { * Convenience method that returns the height of this bitmap divided * by the density scale factor. * + * Returns the bitmap's height multiplied by the ratio of the target density to the bitmap's + * source density + * * @param targetDensity The density of the target canvas of the bitmap. * @return The scaled height of this bitmap, according to the density scale factor. */ @@ -1701,41 +1702,9 @@ public final class Bitmap implements Parcelable { @Nullable public final ColorSpace getColorSpace() { checkRecycled("getColorSpace called on a recycled bitmap"); - // Cache the color space retrieval since it can be fairly expensive if (mColorSpace == null) { - if (nativeIsConfigF16(mNativePtr)) { - // an F16 bitmaps is intended to always be linear extended, but due to - // inconsistencies in Bitmap.create() functions it is possible to have - // rendered into a bitmap in non-linear sRGB. - if (nativeIsSRGB(mNativePtr)) { - mColorSpace = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB); - } else { - mColorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB); - } - } else if (nativeIsSRGB(mNativePtr)) { - mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB); - } else if (nativeIsSRGBLinear(mNativePtr)) { - mColorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_SRGB); - } else { - float[] xyz = new float[9]; - float[] params = new float[7]; - - boolean hasColorSpace = nativeGetColorSpace(mNativePtr, xyz, params); - if (hasColorSpace) { - ColorSpace.Rgb.TransferParameters parameters = - new ColorSpace.Rgb.TransferParameters( - params[0], params[1], params[2], - params[3], params[4], params[5], params[6]); - ColorSpace cs = ColorSpace.match(xyz, parameters); - if (cs != null) { - mColorSpace = cs; - } else { - mColorSpace = new ColorSpace.Rgb("Unknown", xyz, parameters); - } - } - } + mColorSpace = nativeComputeColorSpace(mNativePtr); } - return mColorSpace; } @@ -1749,6 +1718,9 @@ public final class Bitmap implements Parcelable { * components min/max values reduce the numerical range compared to the * previously assigned color space. * + * @throws IllegalArgumentException If the {@code Config} (returned by {@link #getConfig()}) + * is {@link Config#ALPHA_8}. + * * @param colorSpace to assign to the bitmap */ public void setColorSpace(@NonNull ColorSpace colorSpace) { @@ -1756,29 +1728,47 @@ public final class Bitmap implements Parcelable { if (colorSpace == null) { throw new IllegalArgumentException("The colorSpace cannot be set to null"); } - if (getColorSpace() != null) { - if (mColorSpace.getComponentCount() != colorSpace.getComponentCount()) { + + if (getConfig() == Config.ALPHA_8) { + throw new IllegalArgumentException("Cannot set a ColorSpace on ALPHA_8"); + } + + // Keep track of the old ColorSpace for comparison, and so we can reset it in case of an + // Exception. + final ColorSpace oldColorSpace = getColorSpace(); + nativeSetColorSpace(mNativePtr, colorSpace.getNativeInstance()); + + // This will update mColorSpace. It may not be the same as |colorSpace|, e.g. if we + // corrected it because the Bitmap is F16. + mColorSpace = null; + final ColorSpace newColorSpace = getColorSpace(); + + try { + if (oldColorSpace.getComponentCount() != newColorSpace.getComponentCount()) { throw new IllegalArgumentException("The new ColorSpace must have the same " + "component count as the current ColorSpace"); - } - for (int i = 0; i < mColorSpace.getComponentCount(); i++) { - if (mColorSpace.getMinValue(i) < colorSpace.getMinValue(i)) { - throw new IllegalArgumentException("The new ColorSpace cannot increase the " - + "minimum value for any of the components compared to the current " - + "ColorSpace. To perform this type of conversion create a new Bitmap " - + "in the desired ColorSpace and draw this Bitmap into it."); - } - if (mColorSpace.getMaxValue(i) > colorSpace.getMaxValue(i)) { - throw new IllegalArgumentException("The new ColorSpace cannot decrease the " - + "maximum value for any of the components compared to the current " - + "ColorSpace/ To perform this type of conversion create a new Bitmap" - + "in the desired ColorSpace and draw this Bitmap into it."); + } else { + for (int i = 0; i < oldColorSpace.getComponentCount(); i++) { + if (oldColorSpace.getMinValue(i) < newColorSpace.getMinValue(i)) { + throw new IllegalArgumentException("The new ColorSpace cannot increase the " + + "minimum value for any of the components compared to the current " + + "ColorSpace. To perform this type of conversion create a new " + + "Bitmap in the desired ColorSpace and draw this Bitmap into it."); + } + if (oldColorSpace.getMaxValue(i) > newColorSpace.getMaxValue(i)) { + throw new IllegalArgumentException("The new ColorSpace cannot decrease the " + + "maximum value for any of the components compared to the current " + + "ColorSpace/ To perform this type of conversion create a new " + + "Bitmap in the desired ColorSpace and draw this Bitmap into it."); + } } } + } catch (IllegalArgumentException e) { + // Undo the change to the ColorSpace. + mColorSpace = oldColorSpace; + nativeSetColorSpace(mNativePtr, mColorSpace.getNativeInstance()); + throw e; } - - nativeSetColorSpace(mNativePtr, colorSpace.getNativeInstance()); - mColorSpace = colorSpace; } /** @@ -2197,7 +2187,6 @@ public final class Bitmap implements Parcelable { private static native void nativeErase(long nativeBitmap, long colorSpacePtr, long color); private static native int nativeRowBytes(long nativeBitmap); private static native int nativeConfig(long nativeBitmap); - private static native boolean nativeIsConfigF16(long nativeBitmap); private static native int nativeGetPixel(long nativeBitmap, int x, int y); private static native long nativeGetColor(long nativeBitmap, int x, int y); @@ -2241,11 +2230,10 @@ public final class Bitmap implements Parcelable { private static native Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer, long nativeColorSpace); private static native GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap); - private static native boolean nativeGetColorSpace(long nativePtr, float[] xyz, float[] params); + private static native ColorSpace nativeComputeColorSpace(long nativePtr); private static native void nativeSetColorSpace(long nativePtr, long nativeColorSpace); private static native boolean nativeIsSRGB(long nativePtr); private static native boolean nativeIsSRGBLinear(long nativePtr); - private static native void nativeCopyColorSpace(long srcBitmap, long dstBitmap); private static native void nativeSetImmutable(long nativePtr); diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index 7aff0414106a..49c3a3ba68b8 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -151,12 +151,9 @@ public class BitmapFactory { * the decoder will pick either the color space embedded in the image * or the color space best suited for the requested image configuration * (for instance {@link ColorSpace.Named#SRGB sRGB} for - * the {@link Bitmap.Config#ARGB_8888} configuration).</p> - * - * <p>{@link Bitmap.Config#RGBA_F16} always uses the - * {@link ColorSpace.Named#LINEAR_EXTENDED_SRGB scRGB} color space). - * Bitmaps in other configurations without an embedded color space are - * assumed to be in the {@link ColorSpace.Named#SRGB sRGB} color space.</p> + * {@link Bitmap.Config#ARGB_8888} configuration and + * {@link ColorSpace.Named#EXTENDED_SRGB EXTENDED_SRGB} for + * {@link Bitmap.Config#RGBA_F16}).</p> * * <p class="note">Only {@link ColorSpace.Model#RGB} color spaces are * currently supported. An <code>IllegalArgumentException</code> will diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index c9e46942a51a..0d5233880674 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -1475,7 +1475,7 @@ public abstract class ColorSpace { x -> absRcpResponse(x, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4), x -> absResponse(x, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4), -0.799f, 2.399f, - null, // FIXME: Use SRGB_TRANSFER_PARAMETERS + SRGB_TRANSFER_PARAMETERS, Named.EXTENDED_SRGB.ordinal() ); sNamedColorSpaces[Named.LINEAR_EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb( diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index 7ec76d79dfb1..99d8c1b42a4a 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -20,7 +20,6 @@ import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.TestApi; import android.app.Activity; import android.app.ActivityManager; import android.os.IBinder; @@ -28,13 +27,16 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; +import android.util.TimeUtils; import android.view.FrameMetricsObserver; import android.view.IGraphicsStats; import android.view.IGraphicsStatsCallback; import android.view.NativeVectorDrawableAnimator; +import android.view.PixelCopy; import android.view.Surface; import android.view.SurfaceHolder; import android.view.TextureLayer; +import android.view.animation.AnimationUtils; import com.android.internal.util.VirtualRefBasePtr; @@ -42,6 +44,7 @@ import java.io.File; import java.io.FileDescriptor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; import sun.misc.Cleaner; @@ -50,13 +53,8 @@ import sun.misc.Cleaner; * from {@link RenderNode}'s to an output {@link android.view.Surface}. There can be as many * HardwareRenderer instances as desired.</p> * - * <h3>Threading</h3> - * <p>HardwareRenderer is not thread safe. An instance of a HardwareRenderer must only be - * created & used from a single thread. It does not matter what thread is used, however - * it must have a {@link android.os.Looper}. Multiple instances do not have to share the same - * thread, although they can.</p> - * * <h3>Resources & lifecycle</h3> + * * <p>All HardwareRenderer instances share a common render thread. The render thread contains * the GPU context & resources necessary to do GPU-accelerated rendering. As such, the first * HardwareRenderer created comes with the cost of also creating the associated GPU contexts, @@ -64,6 +62,7 @@ import sun.misc.Cleaner; * is to have a HardwareRenderer instance for every active {@link Surface}. For example * when an Activity shows a Dialog the system internally will use 2 hardware renderers, both * of which may be drawing at the same time.</p> + * * <p>NOTE: Due to the shared, cooperative nature of the render thread it is critical that * any {@link Surface} used must have a prompt, reliable consuming side. System-provided * consumers such as {@link android.view.SurfaceView}, @@ -73,8 +72,6 @@ import sun.misc.Cleaner; * it is the app's responsibility to ensure that they consume updates promptly and rapidly. * Failure to do so will cause the render thread to stall on that surface, blocking all * HardwareRenderer instances.</p> - * - * @hide */ public class HardwareRenderer { private static final String LOG_TAG = "HardwareRenderer"; @@ -89,18 +86,18 @@ public class HardwareRenderer { * The renderer is requesting a redraw. This can occur if there's an animation that's running * in the RenderNode tree and the hardware renderer is unable to self-animate. * - * If this is returned from syncAndDrawFrame the expectation is that syncAndDrawFrame + * <p>If this is returned from syncAndDraw the expectation is that syncAndDraw * will be called again on the next vsync signal. */ public static final int SYNC_REDRAW_REQUESTED = 1 << 0; /** * The hardware renderer no longer has a valid {@link android.view.Surface} to render to. - * This can happen if {@link Surface#destroy()} was called. The user should no longer - * attempt to call syncAndDrawFrame until a new surface has been provided by calling + * This can happen if {@link Surface#release()} was called. The user should no longer + * attempt to call syncAndDraw until a new surface has been provided by calling * setSurface. * - * Spoiler: the reward is GPU-accelerated drawing, better find that Surface! + * <p>Spoiler: the reward is GPU-accelerated drawing, better find that Surface! */ public static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 1 << 1; @@ -119,6 +116,7 @@ public class HardwareRenderer { */ public static final int SYNC_FRAME_DROPPED = 1 << 3; + /** @hide */ @IntDef(value = { SYNC_OK, SYNC_REDRAW_REQUESTED, SYNC_LOST_SURFACE_REWARD_IF_FOUND, SYNC_CONTEXT_IS_STOPPED, SYNC_FRAME_DROPPED}) @@ -153,7 +151,6 @@ public class HardwareRenderer { protected RenderNode mRootNode; private boolean mOpaque = true; private boolean mForceDark = false; - private FrameInfo mScratchInfo; private boolean mIsWideGamut = false; /** @@ -175,14 +172,14 @@ public class HardwareRenderer { * Destroys the rendering context of this HardwareRenderer. This destroys the resources * associated with this renderer and releases the currently set {@link Surface}. * - * The renderer may be restored from this state by setting a new {@link Surface}, setting + * <p>The renderer may be restored from this state by setting a new {@link Surface}, setting * new rendering content with {@link #setContentRoot(RenderNode)}, and resuming - * rendering with {@link #syncAndDrawFrame(long)}. + * rendering by issuing a new {@link FrameRenderRequest}. * - * It is suggested to call this in response to callbacks such as + * <p>It is suggested to call this in response to callbacks such as * {@link android.view.SurfaceHolder.Callback#surfaceDestroyed(SurfaceHolder)}. * - * Note that if there are any outstanding frame commit callbacks they may end up never being + * <p>Note that if there are any outstanding frame commit callbacks they may never being * invoked if the frame was deferred to a later vsync. */ public void destroy() { @@ -204,14 +201,14 @@ public class HardwareRenderer { * Sets the center of the light source. The light source point controls the directionality * and shape of shadows rendered by RenderNode Z & elevation. * - * The platform's recommendation is to set lightX to 'displayWidth / 2f - windowLeft', set + * <p>The platform's recommendation is to set lightX to 'displayWidth / 2f - windowLeft', set * lightY to 0 - windowTop, lightZ set to 600dp, and lightRadius to 800dp. * - * The light source should be setup both as part of initial configuration, and whenever + * <p>The light source should be setup both as part of initial configuration, and whenever * the window moves to ensure the light source stays anchored in display space instead * of in window space. * - * This must be set at least once along with {@link #setLightSourceAlpha(float, float)} + * <p>This must be set at least once along with {@link #setLightSourceAlpha(float, float)} * before shadows will work. * * @param lightX The X position of the light source @@ -233,10 +230,10 @@ public class HardwareRenderer { * Configures the ambient & spot shadow alphas. This is the alpha used when the shadow * has max alpha, and ramps down from the values provided to zero. * - * These values are typically provided by the current theme, see + * <p>These values are typically provided by the current theme, see * {@link android.R.attr#spotShadowAlpha} and {@link android.R.attr#ambientShadowAlpha}. * - * This must be set at least once along with + * <p>This must be set at least once along with * {@link #setLightSourceGeometry(float, float, float, float)} before shadows will work. * * @param ambientShadowAlpha The alpha for the ambient shadow. If unsure, a reasonable default @@ -254,8 +251,8 @@ public class HardwareRenderer { /** * Sets the content root to render. It is not necessary to call this whenever the content * recording changes. Any mutations to the RenderNode content, or any of the RenderNode's - * contained within the content node, will be applied whenever {@link #syncAndDrawFrame(long)} - * is called. + * contained within the content node, will be applied whenever a new {@link FrameRenderRequest} + * is issued via {@link #createRenderRequest()} and {@link FrameRenderRequest#syncAndDraw()}. * * @param content The content to set as the root RenderNode. If null the content root is removed * and the renderer will draw nothing. @@ -295,53 +292,133 @@ public class HardwareRenderer { } /** - * Syncs the RenderNode tree to the render thread and requests a frame to be drawn. - * - * @hide + * Sets the parameters that can be used to control a render request for a + * {@link HardwareRenderer}. This is not thread-safe and must not be held on to for longer + * than a single frame request. */ - @SyncAndDrawResult - public int syncAndDrawFrame(@NonNull FrameInfo frameInfo) { - return nSyncAndDrawFrame(mNativeProxy, frameInfo.frameInfo, frameInfo.frameInfo.length); + public final class FrameRenderRequest { + private FrameInfo mFrameInfo = new FrameInfo(); + private boolean mWaitForPresent; + + private FrameRenderRequest() { } + + private void reset() { + mWaitForPresent = false; + // Default to the animation time which, if choreographer is in play, will default to the + // current vsync time. Otherwise it will be 'now'. + mRenderRequest.setVsyncTime( + AnimationUtils.currentAnimationTimeMillis() * TimeUtils.NANOS_PER_MS); + } + + /** @hide */ + public void setFrameInfo(FrameInfo info) { + System.arraycopy(info.frameInfo, 0, mFrameInfo.frameInfo, 0, info.frameInfo.length); + } + + /** + * Sets the vsync time that represents the start point of this frame. Typically this + * comes from {@link android.view.Choreographer.FrameCallback}. Other compatible time + * sources include {@link System#nanoTime()}, however if the result is being displayed + * on-screen then using {@link android.view.Choreographer} is strongly recommended to + * ensure smooth animations. + * + * <p>If the clock source is not from a CLOCK_MONOTONIC source then any animations driven + * directly by RenderThread will not be synchronized properly with the current frame. + * + * @param vsyncTime The vsync timestamp for this frame. The timestamp is in nanoseconds + * and should come from a CLOCK_MONOTONIC source. + * + * @return this instance + */ + public FrameRenderRequest setVsyncTime(long vsyncTime) { + mFrameInfo.setVsync(vsyncTime, vsyncTime); + mFrameInfo.addFlags(FrameInfo.FLAG_SURFACE_CANVAS); + return this; + } + + /** + * Adds a frame commit callback. This callback will be invoked when the current rendering + * content has been rendered into a frame and submitted to the swap chain. The frame may + * not currently be visible on the display when this is invoked, but it has been submitted. + * This callback is useful in combination with {@link PixelCopy} to capture the current + * rendered content of the UI reliably. + * + * @param executor The executor to run the callback on. It is strongly recommended that + * this executor post to a different thread, as the calling thread is + * highly sensitive to being blocked. + * @param frameCommitCallback The callback to invoke when the frame content has been drawn. + * Will be invoked on the given {@link Executor}. + * + * @return this instance + */ + public FrameRenderRequest setFrameCommitCallback(@NonNull Executor executor, + @NonNull Runnable frameCommitCallback) { + setFrameCompleteCallback(frameNr -> executor.execute(frameCommitCallback)); + return this; + } + + /** + * Sets whether or not {@link #syncAndDraw()} should block until the frame has been + * presented. If this is true and {@link #syncAndDraw()} does not return + * {@link #SYNC_FRAME_DROPPED} or an error then when {@link #syncAndDraw()} has returned + * the frame has been submitted to the {@link Surface}. The default and typically + * recommended value is false, as blocking for present will prevent pipelining from + * happening, reducing overall throughput. This is useful for situations such as + * {@link SurfaceHolder.Callback2#surfaceRedrawNeeded(SurfaceHolder)} where it is desired + * to block until a frame has been presented to ensure first-frame consistency with + * other Surfaces. + * + * @param shouldWait If true the next call to {@link #syncAndDraw()} will block until + * completion. + * @return this instance + */ + public FrameRenderRequest setWaitForPresent(boolean shouldWait) { + mWaitForPresent = shouldWait; + return this; + } + + /** + * Syncs the RenderNode tree to the render thread and requests a frame to be drawn. This + * {@link FrameRenderRequest} instance should no longer be used after calling this method. + * The system internally may reuse instances of {@link FrameRenderRequest} to reduce + * allocation churn. + * + * @return The result of the sync operation. See {@link SyncAndDrawResult}. + */ + @SyncAndDrawResult + public int syncAndDraw() { + int syncResult = syncAndDrawFrame(mFrameInfo); + if (mWaitForPresent && (syncResult & SYNC_FRAME_DROPPED) == 0) { + fence(); + } + return syncResult; + } } + private FrameRenderRequest mRenderRequest = new FrameRenderRequest(); + /** - * Syncs the RenderNode tree to the render thread and requests a frame to be drawn. + * Returns a {@link FrameRenderRequest} that can be used to render a new frame. This is used + * to synchronize the RenderNode content provided by {@link #setContentRoot(RenderNode)} with + * the RenderThread and then renders a single frame to the Surface set with + * {@link #setSurface(Surface)}. * - * @param vsyncTime The vsync timestamp for this frame. Typically this comes from - * {@link android.view.Choreographer.FrameCallback}. Must be set and be valid - * as the renderer uses this time internally to drive animations. - * @return The result of the sync operation. See {@link SyncAndDrawResult}. + * @return An instance of {@link FrameRenderRequest}. The instance may be reused for every + * frame, so the caller should not hold onto it for longer than a single render request. */ - @SyncAndDrawResult - public int syncAndDrawFrame(long vsyncTime) { - if (mScratchInfo == null) { - mScratchInfo = new FrameInfo(); - } - mScratchInfo.setVsync(vsyncTime, vsyncTime); - mScratchInfo.addFlags(FrameInfo.FLAG_SURFACE_CANVAS); - return syncAndDrawFrame(mScratchInfo); + public FrameRenderRequest createRenderRequest() { + mRenderRequest.reset(); + return mRenderRequest; } /** * Syncs the RenderNode tree to the render thread and requests a frame to be drawn. - * frameCommitCallback callback will be invoked when the current rendering content has been - * rendered into a frame and submitted to the swap chain. * - * @param vsyncTime The vsync timestamp for this frame. Typically this comes from - * {@link android.view.Choreographer.FrameCallback}. Must be set and - * be valid as the renderer uses this time internally to drive - * animations. - * @param frameCommitCallback The callback to invoke when the frame content has been drawn. - * Will be invoked on the current {@link android.os.Looper} thread. - * @return The result of the sync operation. See {@link SyncAndDrawResult}. + * @hide */ @SyncAndDrawResult - public int syncAndDrawFrame(long vsyncTime, - @Nullable Runnable frameCommitCallback) { - if (frameCommitCallback != null) { - setFrameCompleteCallback(frameNr -> frameCommitCallback.run()); - } - return syncAndDrawFrame(vsyncTime); + public int syncAndDrawFrame(@NonNull FrameInfo frameInfo) { + return nSyncAndDrawFrame(mNativeProxy, frameInfo.frameInfo, frameInfo.frameInfo.length); } /** @@ -349,10 +426,11 @@ public class HardwareRenderer { * is useful to temporarily suspend using the active Surface in order to do any Surface * mutations necessary. * - * Any subsequent draws will override the pause, resuming normal operation. + * <p>Any subsequent draws will override the pause, resuming normal operation. * * @return true if there was an outstanding render request, false otherwise. If this is true - * the caller should ensure that {@link #syncAndDrawFrame(long)} is called at the soonest + * the caller should ensure that {@link #createRenderRequest()} + * and {@link FrameRenderRequest#syncAndDraw()} is called at the soonest * possible time to resume normal operation. * * TODO Should this be exposed? ViewRootImpl needs it because it destroys the old @@ -367,14 +445,14 @@ public class HardwareRenderer { /** * Hard stops rendering into the surface. If the renderer is stopped it will - * block any attempt to render. Calls to {@link #syncAndDrawFrame(long)} will still - * sync over the latest rendering content, however they will not render and instead + * block any attempt to render. Calls to {@link FrameRenderRequest#syncAndDraw()} will + * still sync over the latest rendering content, however they will not render and instead * {@link #SYNC_CONTEXT_IS_STOPPED} will be returned. * - * If false is passed then rendering will resume as normal. Any pending rendering requests + * <p>If false is passed then rendering will resume as normal. Any pending rendering requests * will produce a new frame at the next vsync signal. * - * This is useful in combination with lifecycle events such as {@link Activity#onStop()} + * <p>This is useful in combination with lifecycle events such as {@link Activity#onStop()} * and {@link Activity#onStart()}. * * @param stopped true to stop all rendering, false to resume @@ -384,24 +462,26 @@ public class HardwareRenderer { } /** - * Destroys all hardware rendering resources associated with the current rendering content. + * Destroys all the display lists associated with the current rendering content. * This includes releasing a reference to the current content root RenderNode. It will * therefore be necessary to call {@link #setContentRoot(RenderNode)} in order to resume - * rendering after calling this. + * rendering after calling this, along with re-recording the display lists for the + * RenderNode tree. * - * It is recommended, but not necessary, to use this in combination with lifecycle events + * <p>It is recommended, but not necessary, to use this in combination with lifecycle events * such as {@link Activity#onStop()} and {@link Activity#onStart()} or in response to * {@link android.content.ComponentCallbacks2#onTrimMemory(int)} signals such as * {@link android.content.ComponentCallbacks2#TRIM_MEMORY_UI_HIDDEN} * * See also {@link #setStopped(boolean)} */ - public void destroyHardwareResources() { + public void clearContent() { nDestroyHardwareResources(mNativeProxy); } /** * Whether or not the force-dark feature should be used for this renderer. + * @hide */ public boolean setForceDark(boolean enable) { if (mForceDark != enable) { @@ -415,20 +495,24 @@ public class HardwareRenderer { /** * Allocate buffers ahead of time to avoid allocation delays during rendering. * - * Typically a Surface will allocate buffers lazily. This is usually fine and reduces the + * <p>Typically a Surface will allocate buffers lazily. This is usually fine and reduces the * memory usage of Surfaces that render rarely or never hit triple buffering. However * for UI it can result in a slight bit of jank on first launch. This hint will * tell the HardwareRenderer that now is a good time to allocate the 3 buffers * necessary for typical rendering. * - * Must be called after a {@link Surface} has been set. + * <p>Must be called after a {@link Surface} has been set. + * + * TODO: Figure out if we even need/want this. Should HWUI just be doing this in response + * to setSurface anyway? Vulkan swapchain makes this murky, so delay making it public + * @hide */ public void allocateBuffers() { nAllocateBuffers(mNativeProxy); } /** - * Notifies the hardware renderer that a call to {@link #syncAndDrawFrame(long)} will + * Notifies the hardware renderer that a call to {@link FrameRenderRequest#syncAndDraw()} will * be coming soon. This is used to help schedule when RenderThread-driven animations will * happen as the renderer wants to avoid producing more than one frame per vsync signal. */ @@ -439,7 +523,7 @@ public class HardwareRenderer { /** * Change the HardwareRenderer's opacity. Will take effect on the next frame produced. * - * If the renderer is set to opaque it is the app's responsibility to ensure that the + * <p>If the renderer is set to opaque it is the app's responsibility to ensure that the * content renders to every pixel of the Surface, otherwise corruption may result. Note that * this includes ensuring that the first draw of any given pixel does not attempt to blend * against the destination. If this is false then the hardware renderer will clear to @@ -527,7 +611,7 @@ public class HardwareRenderer { } /** - * Prevents any further drawing until {@link #syncAndDrawFrame(long)} is called. + * Prevents any further drawing until {@link FrameRenderRequest#syncAndDraw()} is called. * This is a signal that the contents of the RenderNode tree are no longer safe to play back. * In practice this usually means that there are Functor pointers in the * display list that are no longer valid. @@ -718,10 +802,8 @@ public class HardwareRenderer { * Interface for listening to picture captures * @hide */ - @TestApi public interface PictureCapturedCallback { /** @hide */ - @TestApi void onPictureCaptured(Picture picture); } diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index 466a5fc2a770..9b5e33017743 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -372,7 +372,7 @@ public final class ImageDecoder implements AutoCloseable { } mResources = res; mInputStream = is; - mInputDensity = res != null ? inputDensity : Bitmap.DENSITY_NONE; + mInputDensity = inputDensity; } final Resources mResources; @@ -1556,12 +1556,9 @@ public final class ImageDecoder implements AutoCloseable { * decoder will pick either the color space embedded in the image or the * {@link ColorSpace} best suited for the requested image configuration * (for instance {@link ColorSpace.Named#SRGB sRGB} for the - * {@link Bitmap.Config#ARGB_8888} configuration).</p> - * - * <p>{@link Bitmap.Config#RGBA_F16} always uses the - * {@link ColorSpace.Named#LINEAR_EXTENDED_SRGB scRGB} color space. - * Bitmaps in other configurations without an embedded color space are - * assumed to be in the {@link ColorSpace.Named#SRGB sRGB} color space.</p> + * {@link Bitmap.Config#ARGB_8888} configuration and + * {@link ColorSpace.Named#EXTENDED_SRGB EXTENDED_SRGB} for + * {@link Bitmap.Config#RGBA_F16}).</p> * * <p class="note">Only {@link ColorSpace.Model#RGB} color spaces are * currently supported. An <code>IllegalArgumentException</code> will diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index 789e38c4e650..d7aee7767524 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -43,6 +43,7 @@ import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.RenderNode; import android.os.Build; +import android.os.Handler; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.IntArray; @@ -1241,6 +1242,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { // If the duration of an animation is more than 300 frames, we cap the sample size to 300. private static final int MAX_SAMPLE_POINTS = 300; + private Handler mHandler; private AnimatorListener mListener = null; private final LongArray mStartDelays = new LongArray(); private PropertyValuesHolder.PropertyValues mTmpValues = @@ -1671,6 +1673,9 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { .mRootName); } mStarted = true; + if (mHandler == null) { + mHandler = new Handler(); + } nStart(mSetPtr, this, ++mLastListenerId); invalidateOwningView(); if (mListener != null) { @@ -1780,7 +1785,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { // onFinished: should be called from native @UnsupportedAppUsage private static void callOnFinished(VectorDrawableAnimatorRT set, int id) { - set.onAnimationEnd(id); + set.mHandler.post(() -> set.onAnimationEnd(id)); } private void transferPendingActions(VectorDrawableAnimator animatorSet) { diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java index aa2917484a05..3dc884eb38ad 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java @@ -17,7 +17,6 @@ package android.security.keystore; import android.security.Credentials; -import android.security.GateKeeper; import android.security.KeyStore; import android.security.keymaster.KeyCharacteristics; import android.security.keymaster.KeymasterArguments; @@ -204,7 +203,12 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { } } } - + if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_3DES) { + if (mKeySizeBits != 168) { + throw new InvalidAlgorithmParameterException( + "3DES key size must be 168 bits."); + } + } if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) { if (mKeySizeBits < 64) { throw new InvalidAlgorithmParameterException( diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 742813e58129..875b90b7ac65 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -1647,6 +1647,10 @@ struct ResTable_overlayable_policy_header // The overlay must reside of the product partition or must have existed on the product // partition before an upgrade to overlay these resources. POLICY_PRODUCT_PARTITION = 0x00000008, + + // The overlay must be signed with the same signature as the actor of the target resource, + // which can be separate or the same as the target package with the resource. + POLICY_SIGNATURE = 0x00000010, }; uint32_t policy_flags; diff --git a/libs/hwui/thread/ThreadBase.h b/libs/hwui/thread/ThreadBase.h index 8cdcc46b97fb..0289d3fd2ef7 100644 --- a/libs/hwui/thread/ThreadBase.h +++ b/libs/hwui/thread/ThreadBase.h @@ -68,10 +68,12 @@ protected: void processQueue() { mQueue.process(); } virtual bool threadLoop() override { + Looper::setForThread(mLooper); while (!exitPending()) { waitForWork(); processQueue(); } + Looper::setForThread(nullptr); return false; } diff --git a/libs/incident/proto/android/os/header.proto b/libs/incident/proto/android/os/header.proto index a84dc48b8b34..d463f87055b3 100644 --- a/libs/incident/proto/android/os/header.proto +++ b/libs/incident/proto/android/os/header.proto @@ -37,4 +37,9 @@ message IncidentHeaderProto { optional int64 id = 2; // The unique id of the statsd config. } optional StatsdConfigKey config_key = 3; + + // Details about the trigger. com.android.os.AlertTriggerDetails + // Only use bytes type here to avoid indirect dependency on atoms.proto + // And this header passes through incidentd without incidentd parsing it. + optional bytes trigger_details = 4; } diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 1aeefb84129f..57a0a725fb41 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -114,6 +114,7 @@ interface ILocationManager // for reporting callback completion void locationCallbackFinished(ILocationListener listener); - // used by gts tests to verify throttling whitelist + // used by gts tests to verify whitelists String[] getBackgroundThrottlingWhitelist(); + String[] getIgnoreSettingsWhitelist(); } diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index c027fd499033..586ee2a43683 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -414,6 +414,18 @@ public class LocationManager { } /** + * @hide + */ + @TestApi + public String[] getIgnoreSettingsWhitelist() { + try { + return mService.getIgnoreSettingsWhitelist(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * @hide - hide this constructor because it has a parameter * of type ILocationManager, which is a system private class. The * right way to create an instance of this class is using the diff --git a/media/java/android/media/AudioFocusInfo.java b/media/java/android/media/AudioFocusInfo.java index 0a9ca025e2b0..3594ee7f6a91 100644 --- a/media/java/android/media/AudioFocusInfo.java +++ b/media/java/android/media/AudioFocusInfo.java @@ -16,6 +16,7 @@ package android.media; +import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -29,10 +30,10 @@ import java.util.Objects; @SystemApi public final class AudioFocusInfo implements Parcelable { - private final AudioAttributes mAttributes; + private final @NonNull AudioAttributes mAttributes; private final int mClientUid; - private final String mClientId; - private final String mPackageName; + private final @NonNull String mClientId; + private final @NonNull String mPackageName; private final int mSdkTarget; private int mGainRequest; private int mLossReceived; @@ -80,13 +81,21 @@ public final class AudioFocusInfo implements Parcelable { * The audio attributes for the audio focus request. * @return non-null {@link AudioAttributes}. */ - public AudioAttributes getAttributes() { return mAttributes; } + public @NonNull AudioAttributes getAttributes() { + return mAttributes; + } - public int getClientUid() { return mClientUid; } + public int getClientUid() { + return mClientUid; + } - public String getClientId() { return mClientId; } + public @NonNull String getClientId() { + return mClientId; + } - public String getPackageName() { return mPackageName; } + public @NonNull String getPackageName() { + return mPackageName; + } /** * The type of audio focus gain request. diff --git a/media/java/android/media/AudioFocusRequest.java b/media/java/android/media/AudioFocusRequest.java index b9731d111aa3..4e7050129058 100644 --- a/media/java/android/media/AudioFocusRequest.java +++ b/media/java/android/media/AudioFocusRequest.java @@ -225,9 +225,9 @@ public final class AudioFocusRequest { /** @hide */ public static final String KEY_ACCESSIBILITY_FORCE_FOCUS_DUCKING = "a11y_force_ducking"; - private final OnAudioFocusChangeListener mFocusListener; // may be null - private final Handler mListenerHandler; // may be null - private final AudioAttributes mAttr; // never null + private final @Nullable OnAudioFocusChangeListener mFocusListener; + private final @Nullable Handler mListenerHandler; + private final @NonNull AudioAttributes mAttr; private final int mFocusGain; private final int mFlags; diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 92afe7ede8f2..24a3a9b32f77 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -17,6 +17,7 @@ package android.media; import android.annotation.CallbackExecutor; +import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1713,12 +1714,11 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, /** * Specifies the logical microphone (for processing). * - * @param direction Direction constant (MicrophoneDirection.MIC_DIRECTION_*) - * @return retval OK if the call is successful, an error code otherwise. - * @hide + * @param direction Direction constant. + * @return true if sucessful. */ - public int setMicrophoneDirection(int direction) { - return native_set_microphone_direction(direction); + public boolean setMicrophoneDirection(int direction) { + return native_set_microphone_direction(direction) == 0; } /** @@ -1727,11 +1727,10 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, * * @param zoom the desired field dimension of microphone capture. Range is from -1 (wide angle), * though 0 (no zoom) to 1 (maximum zoom). - * @return retval OK if the call is successful, an error code otherwise. - * @hide + * @return true if sucessful. */ - public int setMicrophoneFieldDimension(float zoom) { - return native_set_microphone_field_dimension(zoom); + public boolean setMicrophoneFieldDimension(@FloatRange(from = -1.0, to = 1.0) float zoom) { + return native_set_microphone_field_dimension(zoom) == 0; } //--------------------------------------------------------- diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java index ad25a06fabe2..5f324f7f97ed 100644 --- a/media/java/android/media/MediaHTTPConnection.java +++ b/media/java/android/media/MediaHTTPConnection.java @@ -16,6 +16,8 @@ package android.media; +import static android.media.MediaPlayer.MEDIA_ERROR_UNSUPPORTED; + import android.annotation.UnsupportedAppUsage; import android.net.NetworkUtils; import android.os.IBinder; @@ -23,21 +25,19 @@ import android.os.StrictMode; import android.util.Log; import java.io.BufferedInputStream; -import java.io.InputStream; import java.io.IOException; +import java.io.InputStream; import java.net.CookieHandler; -import java.net.CookieManager; -import java.net.Proxy; -import java.net.URL; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.NoRouteToHostException; import java.net.ProtocolException; +import java.net.Proxy; +import java.net.URL; import java.net.UnknownServiceException; import java.util.HashMap; import java.util.Map; - -import static android.media.MediaPlayer.MEDIA_ERROR_UNSUPPORTED; +import java.util.concurrent.atomic.AtomicBoolean; /** @hide */ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @@ -67,6 +67,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { // from com.squareup.okhttp.internal.http private final static int HTTP_TEMP_REDIRECT = 307; private final static int MAX_REDIRECTS = 20; + private AtomicBoolean mIsConnected = new AtomicBoolean(false); @UnsupportedAppUsage public MediaHTTPConnection() { @@ -90,6 +91,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { mAllowCrossDomainRedirect = true; mURL = new URL(uri); mHeaders = convertHeaderStringToMap(headers); + mIsConnected.set(true); } catch (MalformedURLException e) { return null; } @@ -140,7 +142,14 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override @UnsupportedAppUsage public void disconnect() { - teardownConnection(); + if (mIsConnected.getAndSet(false)) { + (new Thread() { + @Override + public void run() { + teardownConnection(); + } + }).start(); + } mHeaders = null; mURL = null; } @@ -325,7 +334,14 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override @UnsupportedAppUsage public int readAt(long offset, int size) { - return native_readAt(offset, size); + if (!mIsConnected.get()) { + return -1; + } + int result = native_readAt(offset, size); + if (!mIsConnected.get()) { + return -1; + } + return result; } private int readAt(long offset, byte[] data, int size) { diff --git a/media/java/android/media/MicrophoneDirection.java b/media/java/android/media/MicrophoneDirection.java index 99201c0279bf..489e2683259e 100644 --- a/media/java/android/media/MicrophoneDirection.java +++ b/media/java/android/media/MicrophoneDirection.java @@ -16,38 +16,46 @@ package android.media; -/** - * @hide - */ +import android.annotation.FloatRange; +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + public interface MicrophoneDirection { /** - * @hide + * Don't do any directionality processing of the activated microphone(s). */ int MIC_DIRECTION_UNSPECIFIED = 0; - /** - * @hide + * Optimize capture for audio coming from the screen-side of the device. */ int MIC_DIRECTION_FRONT = 1; - /** - * @hide + * Optimize capture for audio coming from the side of the device opposite the screen. */ int MIC_DIRECTION_BACK = 2; - /** - * @hide + * Optimize capture for audio coming from an off-device microphone. */ int MIC_DIRECTION_EXTERNAL = 3; + /** @hide */ + @IntDef({ + MIC_DIRECTION_UNSPECIFIED, + MIC_DIRECTION_FRONT, + MIC_DIRECTION_BACK, + MIC_DIRECTION_EXTERNAL + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Directionmode{}; /** * Specifies the logical microphone (for processing). * - * @param direction Direction constant (MicrophoneDirection.MIC_DIRECTION_*) - * @return retval OK if the call is successful, an error code otherwise. - * @hide + * @param direction Direction constant. + * @return true if sucessful. */ - int setMicrophoneDirection(int direction); + boolean setMicrophoneDirection(@Directionmode int direction); /** * Specifies the zoom factor (i.e. the field dimension) for the selected microphone @@ -55,8 +63,7 @@ public interface MicrophoneDirection { * * @param zoom the desired field dimension of microphone capture. Range is from -1 (wide angle), * though 0 (no zoom) to 1 (maximum zoom). - * @return retval OK if the call is successful, an error code otherwise. - * @hide + * @return true if sucessful. */ - int setMicrophoneFieldDimension(float zoom); + boolean setMicrophoneFieldDimension(@FloatRange(from = -1.0, to = 1.0) float zoom); } diff --git a/media/java/android/media/session/ControllerLink.java b/media/java/android/media/session/ControllerLink.java index 64d283f168e1..40c716607697 100644 --- a/media/java/android/media/session/ControllerLink.java +++ b/media/java/android/media/session/ControllerLink.java @@ -493,6 +493,22 @@ public final class ControllerLink implements Parcelable { } /** + * Tell system that a controller requests changing the playback speed. + * + * @param packageName the package name of the controller + * @param caller the {@link ControllerCallbackLink} of the controller + * @param speed the playback speed + */ + void setPlaybackSpeed(@NonNull String packageName, @NonNull ControllerCallbackLink caller, + float speed) { + try { + mISessionController.setPlaybackSpeed(packageName, caller, speed); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + /** * Tell system that a controller sends a custom action. * * @param packageName the package name of the controller @@ -759,6 +775,11 @@ public final class ControllerLink implements Parcelable { @NonNull Rating rating) { } + /** Stub method for ISessionController.setPlaybackSpeed */ + public void setPlaybackSpeed(@NonNull String packageName, + @NonNull ControllerCallbackLink caller, float speed) { + } + /** Stub method for ISessionController.sendCustomAction */ public void sendCustomAction(@NonNull String packageName, @NonNull ControllerCallbackLink caller, @NonNull String action, @@ -953,6 +974,12 @@ public final class ControllerLink implements Parcelable { } @Override + public void setPlaybackSpeed(String packageName, ControllerCallbackLink caller, + float speed) { + mControllerStub.setPlaybackSpeed(packageName, caller, speed); + } + + @Override public void sendCustomAction(String packageName, ControllerCallbackLink caller, String action, Bundle args) { mControllerStub.sendCustomAction(packageName, caller, action, args); diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl index 9b86bfced340..cd33c044d142 100644 --- a/media/java/android/media/session/ISessionCallback.aidl +++ b/media/java/android/media/session/ISessionCallback.aidl @@ -60,6 +60,8 @@ oneway interface ISessionCallback { long pos); void notifyRate(String packageName, int pid, int uid, in ControllerCallbackLink caller, in Rating rating); + void notifySetPlaybackSpeed(String packageName, int pid, int uid, + in ControllerCallbackLink caller, float speed); void notifyCustomAction(String packageName, int pid, int uid, in ControllerCallbackLink caller, String action, in Bundle args); diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl index e697c65e11c0..3e7b4fbdebd3 100644 --- a/media/java/android/media/session/ISessionController.aidl +++ b/media/java/android/media/session/ISessionController.aidl @@ -76,6 +76,7 @@ interface ISessionController { void rewind(String packageName, in ControllerCallbackLink caller); void seekTo(String packageName, in ControllerCallbackLink caller, long pos); void rate(String packageName, in ControllerCallbackLink caller, in Rating rating); + void setPlaybackSpeed(String packageName, in ControllerCallbackLink caller, float speed); void sendCustomAction(String packageName, in ControllerCallbackLink caller, String action, in Bundle args); MediaMetadata getMetadata(); diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index 6e2c8c51c734..9e4199cba47a 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -865,6 +865,19 @@ public final class MediaController { } /** + * Set the playback speed. + * + * @param speed The playback speed + */ + public void setPlaybackSpeed(float speed) { + try { + mSessionBinder.setPlaybackSpeed(mContext.getPackageName(), mCbStub, speed); + } catch (RuntimeException e) { + Log.wtf(TAG, "Error calling setPlaybackSpeed.", e); + } + } + + /** * Send a custom action back for the {@link MediaSession} to perform. * * @param customAction The action to perform. diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 1b9ebdafd328..8ab893b03a62 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -681,6 +681,19 @@ public final class MediaSession { } /** + * Override to handle the playback speed change. + * To update the new playback speed, create a new {@link PlaybackState} by using {@link + * PlaybackState.Builder#setState(int, long, float)}, and set it with + * {@link #setPlaybackState(PlaybackState)}. + * + * @param speed the playback speed + * @see #setPlaybackState(PlaybackState) + * @see PlaybackState.Builder#setState(int, long, float) + */ + public void onSetPlaybackSpeed(float speed) { + } + + /** * Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be * performed. * diff --git a/media/java/android/media/session/MediaSessionEngine.java b/media/java/android/media/session/MediaSessionEngine.java index e19bdbcf923e..266bf3226c49 100644 --- a/media/java/android/media/session/MediaSessionEngine.java +++ b/media/java/android/media/session/MediaSessionEngine.java @@ -538,6 +538,10 @@ public final class MediaSessionEngine implements AutoCloseable { postToCallback(caller, CallbackMessageHandler.MSG_RATE, rating, null); } + void dispatchSetPlaybackSpeed(RemoteUserInfo caller, float speed) { + postToCallback(caller, CallbackMessageHandler.MSG_SET_PLAYBACK_SPEED, speed, null); + } + void dispatchCustomAction(RemoteUserInfo caller, String action, Bundle args) { postToCallback(caller, CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args); } @@ -871,6 +875,17 @@ public final class MediaSessionEngine implements AutoCloseable { } /** + * Override to handle the playback speed change. + * + * @param speed the playback speed + */ + public void onSetPlaybackSpeed(float speed) { + if (mCallback != null) { + mCallback.onSetPlaybackSpeed(speed); + } + } + + /** * Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be * performed. * @@ -1092,10 +1107,11 @@ public final class MediaSessionEngine implements AutoCloseable { private static final int MSG_REWIND = 17; private static final int MSG_SEEK_TO = 18; private static final int MSG_RATE = 19; - private static final int MSG_CUSTOM_ACTION = 20; - private static final int MSG_ADJUST_VOLUME = 21; - private static final int MSG_SET_VOLUME = 22; - private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 23; + private static final int MSG_SET_PLAYBACK_SPEED = 20; + private static final int MSG_CUSTOM_ACTION = 21; + private static final int MSG_ADJUST_VOLUME = 22; + private static final int MSG_SET_VOLUME = 23; + private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 24; @SuppressWarnings("WeakerAccess") /* synthetic access */ CallbackWrapper mCallbackWrapper; @@ -1186,6 +1202,9 @@ public final class MediaSessionEngine implements AutoCloseable { case MSG_RATE: mCallbackWrapper.onSetRating((Rating) obj); break; + case MSG_SET_PLAYBACK_SPEED: + mCallbackWrapper.onSetPlaybackSpeed((Float) obj); + break; case MSG_CUSTOM_ACTION: mCallbackWrapper.onCustomAction((String) obj, msg.getData()); break; diff --git a/media/java/android/media/session/SessionCallbackLink.java b/media/java/android/media/session/SessionCallbackLink.java index f59a69d6e157..f9fa45a1b619 100644 --- a/media/java/android/media/session/SessionCallbackLink.java +++ b/media/java/android/media/session/SessionCallbackLink.java @@ -462,6 +462,25 @@ public final class SessionCallbackLink implements Parcelable { } /** + * Notify session that a controller requests changing playback speed. + * + * @param packageName the package name of the controller + * @param pid the pid of the controller + * @param uid the uid of the controller + * @param caller the {@link ControllerCallbackLink} of the controller + * @param speed the playback speed + */ + @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) + public void notifySetPlaybackSpeed(@NonNull String packageName, int pid, int uid, + @NonNull ControllerCallbackLink caller, float speed) { + try { + mISessionCallback.notifySetPlaybackSpeed(packageName, pid, uid, caller, speed); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + /** * Notify session that a controller sends a custom action. * * @param packageName the package name of the controller @@ -871,6 +890,23 @@ public final class SessionCallbackLink implements Parcelable { } } + @Override + public void notifySetPlaybackSpeed(String packageName, int pid, int uid, + ControllerCallbackLink caller, float speed) { + ensureMediaControlPermission(); + final long token = Binder.clearCallingIdentity(); + try { + MediaSessionEngine sessionImpl = mSessionImpl.get(); + if (sessionImpl != null) { + sessionImpl.dispatchSetPlaybackSpeed( + createRemoteUserInfo(packageName, pid, uid), speed); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override public void notifyCustomAction(String packageName, int pid, int uid, ControllerCallbackLink caller, String action, Bundle args) { ensureMediaControlPermission(); diff --git a/packages/CaptivePortalLogin/Android.bp b/packages/CaptivePortalLogin/Android.bp index 7acdfa1fd4a8..a71977f8cc6c 100644 --- a/packages/CaptivePortalLogin/Android.bp +++ b/packages/CaptivePortalLogin/Android.bp @@ -18,7 +18,7 @@ android_app { name: "CaptivePortalLogin", srcs: ["src/**/*.java"], sdk_version: "system_current", - certificate: "platform", + certificate: "networkstack", static_libs: [ "androidx.legacy_legacy-support-v4", "metrics-constants-protos", diff --git a/packages/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml index e15dca046006..0894ee576a2d 100644 --- a/packages/CaptivePortalLogin/AndroidManifest.xml +++ b/packages/CaptivePortalLogin/AndroidManifest.xml @@ -23,8 +23,8 @@ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> - <uses-permission android:name="android.permission.NETWORK_SETTINGS" /> <uses-permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS" /> + <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" /> <application android:label="@string/app_name" android:usesCleartextTraffic="true" diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java index ce627ce47717..a288d010e59b 100644 --- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java +++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java @@ -175,6 +175,7 @@ public class CaptivePortalLoginActivity extends Activity { webSettings.setSupportZoom(true); webSettings.setBuiltInZoomControls(true); webSettings.setDisplayZoomControls(false); + webSettings.setDomStorageEnabled(true); mWebViewClient = new MyWebViewClient(); webview.setWebViewClient(mWebViewClient); webview.setWebChromeClient(new MyWebChromeClient()); diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java index f36b4aa87636..55c9361ce6c4 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java @@ -106,6 +106,7 @@ public class CaptivePortalLoginActivity extends Activity { webSettings.setLoadWithOverviewMode(true); webSettings.setSupportZoom(true); webSettings.setBuiltInZoomControls(true); + webSettings.setDomStorageEnabled(true); mWebViewClient = new MyWebViewClient(); mWebView.setWebViewClient(mWebViewClient); mWebView.setWebChromeClient(new MyWebChromeClient()); diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java index c5e598d8ce46..31a3ff4a9b8f 100644 --- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java +++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java @@ -221,6 +221,12 @@ public class Assistant extends NotificationAssistantService { } @Override + public Adjustment onNotificationEnqueued(StatusBarNotification sbn) { + // we use the version with channel, so this is never called. + return null; + } + + @Override public Adjustment onNotificationEnqueued(StatusBarNotification sbn, NotificationChannel channel) { if (DEBUG) Log.i(TAG, "ENQUEUED " + sbn.getKey() + " on " + channel.getId()); diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp index d6565936c860..b700bf324817 100644 --- a/packages/NetworkStack/Android.bp +++ b/packages/NetworkStack/Android.bp @@ -35,11 +35,12 @@ java_library { android_app { name: "NetworkStack", sdk_version: "system_current", - certificate: "platform", + certificate: "networkstack", privileged: true, static_libs: [ "NetworkStackLib" ], + jarjar_rules: "jarjar-rules-shared.txt", manifest: "AndroidManifest.xml", required: ["NetworkStackPermissionStub"], }
\ No newline at end of file diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml index e4d35915c77c..52c209e5f247 100644 --- a/packages/NetworkStack/AndroidManifest.xml +++ b/packages/NetworkStack/AndroidManifest.xml @@ -17,20 +17,19 @@ */ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.mainline.networkstack" + package="com.android.networkstack" android:sharedUserId="android.uid.networkstack"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" /> - <uses-permission android:name="android.permission.NETWORK_SETTINGS" /> <!-- Signature permission defined in NetworkStackStub --> <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" /> <!-- Send latency broadcast as current user --> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> - <uses-permission android:name="android.permission.NETWORK_STACK" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> + <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" /> <application android:label="NetworkStack" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/NetworkStack/jarjar-rules-shared.txt b/packages/NetworkStack/jarjar-rules-shared.txt new file mode 100644 index 000000000000..a8c712a3336d --- /dev/null +++ b/packages/NetworkStack/jarjar-rules-shared.txt @@ -0,0 +1,19 @@ +rule com.android.internal.util.** android.net.networkstack.util.@1 + +rule android.net.shared.Inet4AddressUtils* android.net.networkstack.shared.Inet4AddressUtils@1 +rule android.net.shared.InetAddressUtils* android.net.networkstack.shared.InetAddressUtils@1 + +# Ignore DhcpResultsParcelable, but jarjar DhcpResults +# TODO: move DhcpResults into services.net and delete from here +rule android.net.DhcpResultsParcelable* @0 +rule android.net.DhcpResults* android.net.networkstack.DhcpResults@1 +rule android.net.LocalLog* android.net.networkstack.LocalLog@1 + +# TODO: remove from framework dependencies, then remove here +rule android.net.InterfaceConfigurationParcel* android.net.networkstack.InterfaceConfigurationParcel@1 +rule android.net.TetherStatsParcel* android.net.networkstack.TetherStatsParcel@1 + +# Used by UidRange, which is used by framework classes such as NetworkCapabilities. +rule android.net.UidRangeParcel* android.net.networkstack.UidRangeParcel@1 +# TODO: move TcpKeepalivePacketData to services.net and delete +rule android.net.TcpKeepalivePacketDataParcelable* android.net.networkstack.TcpKeepalivePacketDataParcelable@1
\ No newline at end of file diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java index 96d1a287ef09..97d26c7c9c1f 100644 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java +++ b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java @@ -18,7 +18,7 @@ package android.net.dhcp; import android.annotation.NonNull; import android.annotation.Nullable; -import android.net.shared.FdEventsReader; +import android.net.util.FdEventsReader; import android.os.Handler; import android.system.Os; diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java index 9e5991298834..b1f6d246563e 100644 --- a/packages/NetworkStack/src/android/net/ip/IpClient.java +++ b/packages/NetworkStack/src/android/net/ip/IpClient.java @@ -46,6 +46,7 @@ import android.net.shared.ProvisioningConfiguration; import android.net.util.InterfaceParams; import android.net.util.SharedLog; import android.os.ConditionVariable; +import android.os.IBinder; import android.os.Message; import android.os.RemoteException; import android.os.SystemClock; @@ -380,6 +381,13 @@ public class IpClient extends StateMachine { public InterfaceParams getInterfaceParams(String ifname) { return InterfaceParams.getByName(ifname); } + + /** + * Get a INetd connector. + */ + public INetd getNetd(Context context) { + return INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)); + } } public IpClient(Context context, String ifName, IIpClientCallbacks callback, @@ -413,7 +421,7 @@ public class IpClient extends StateMachine { // TODO: Consider creating, constructing, and passing in some kind of // InterfaceController.Dependencies class. - mNetd = mContext.getSystemService(INetd.class); + mNetd = deps.getNetd(mContext); mInterfaceCtrl = new InterfaceController(mInterfaceName, mNetd, mLog); mLinkObserver = new IpClientLinkObserver( diff --git a/core/java/android/net/shared/FdEventsReader.java b/packages/NetworkStack/src/android/net/util/FdEventsReader.java index bffbfb115886..1380ea720d40 100644 --- a/core/java/android/net/shared/FdEventsReader.java +++ b/packages/NetworkStack/src/android/net/util/FdEventsReader.java @@ -14,14 +14,13 @@ * limitations under the License. */ -package android.net.shared; +package android.net.util; import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR; import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT; import android.annotation.NonNull; import android.annotation.Nullable; -import android.net.util.SocketUtils; import android.os.Handler; import android.os.Looper; import android.os.MessageQueue; diff --git a/packages/NetworkStack/src/android/net/util/PacketReader.java b/packages/NetworkStack/src/android/net/util/PacketReader.java index 94b1e9f2e14e..4aec6b6753a6 100644 --- a/packages/NetworkStack/src/android/net/util/PacketReader.java +++ b/packages/NetworkStack/src/android/net/util/PacketReader.java @@ -18,7 +18,6 @@ package android.net.util; import static java.lang.Math.max; -import android.net.shared.FdEventsReader; import android.os.Handler; import android.system.Os; diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java index cedcb84e9d08..90db207c9902 100644 --- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java +++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java @@ -114,7 +114,8 @@ public class NetworkStackService extends Service { NetworkStackConnector(Context context) { mContext = context; - mNetd = (INetd) context.getSystemService(Context.NETD_SERVICE); + mNetd = INetd.Stub.asInterface( + (IBinder) context.getSystemService(Context.NETD_SERVICE)); mObserverRegistry = new NetworkObserverRegistry(); mCm = context.getSystemService(ConnectivityManager.class); @@ -246,6 +247,12 @@ public class NetworkStackService extends Service { } @Override + public void notifyCaptivePortalAppFinished(int response) { + checkNetworkStackCallingPermission(); + mNm.notifyCaptivePortalAppFinished(response); + } + + @Override public void forceReevaluation(int uid) { checkNetworkStackCallingPermission(); mNm.forceReevaluation(uid); diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java index 2e72d8296a37..ec4a47930fad 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java @@ -39,9 +39,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.net.CaptivePortal; import android.net.ConnectivityManager; -import android.net.ICaptivePortal; import android.net.INetworkMonitor; import android.net.INetworkMonitorCallbacks; import android.net.LinkProperties; @@ -67,15 +65,9 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; -import android.telephony.CellIdentityCdma; -import android.telephony.CellIdentityGsm; -import android.telephony.CellIdentityLte; -import android.telephony.CellIdentityWcdma; -import android.telephony.CellInfo; -import android.telephony.CellInfoCdma; -import android.telephony.CellInfoGsm; -import android.telephony.CellInfoLte; -import android.telephony.CellInfoWcdma; +import android.telephony.AccessNetworkConstants; +import android.telephony.NetworkRegistrationState; +import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -466,6 +458,13 @@ public class NetworkMonitor extends StateMachine { sendMessage(CMD_LAUNCH_CAPTIVE_PORTAL_APP); } + /** + * Notify that the captive portal app was closed with the provided response code. + */ + public void notifyCaptivePortalAppFinished(int response) { + sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response); + } + @Override protected void log(String s) { if (DBG) Log.d(TAG + "/" + mNetwork.toString(), s); @@ -677,29 +676,8 @@ public class NetworkMonitor extends StateMachine { case CMD_LAUNCH_CAPTIVE_PORTAL_APP: final Bundle appExtras = new Bundle(); // OneAddressPerFamilyNetwork is not parcelable across processes. - appExtras.putParcelable( - ConnectivityManager.EXTRA_NETWORK, new Network(mNetwork)); - appExtras.putParcelable(ConnectivityManager.EXTRA_CAPTIVE_PORTAL, - new CaptivePortal(new ICaptivePortal.Stub() { - @Override - public void appResponse(int response) { - if (response == APP_RETURN_WANTED_AS_IS) { - mContext.enforceCallingPermission( - PERMISSION_NETWORK_SETTINGS, - "CaptivePortal"); - } - sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response); - } - - @Override - public void logEvent(int eventId, String packageName) - throws RemoteException { - mContext.enforceCallingPermission( - PERMISSION_NETWORK_SETTINGS, - "CaptivePortal"); - mCallback.logCaptivePortalLoginEvent(eventId, packageName); - } - })); + final Network network = new Network(mNetwork); + appExtras.putParcelable(ConnectivityManager.EXTRA_NETWORK, network); final CaptivePortalProbeResult probeRes = mLastPortalProbeResult; appExtras.putString(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl); if (probeRes.probeSpec != null) { @@ -708,7 +686,7 @@ public class NetworkMonitor extends StateMachine { } appExtras.putString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT, mCaptivePortalUserAgent); - mCm.startCaptivePortalApp(appExtras); + mCm.startCaptivePortalApp(network, appExtras); return HANDLED; default: return NOT_HANDLED; @@ -1312,6 +1290,7 @@ public class NetworkMonitor extends StateMachine { urlConnection.setInstanceFollowRedirects(probeType == ValidationProbeEvent.PROBE_PAC); urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS); urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS); + urlConnection.setRequestProperty("Connection", "close"); urlConnection.setUseCaches(false); if (mCaptivePortalUserAgent != null) { urlConnection.setRequestProperty("User-Agent", mCaptivePortalUserAgent); @@ -1485,10 +1464,6 @@ public class NetworkMonitor extends StateMachine { */ private void sendNetworkConditionsBroadcast(boolean responseReceived, boolean isCaptivePortal, long requestTimestampMs, long responseTimestampMs) { - if (!mWifiManager.isScanAlwaysAvailable()) { - return; - } - if (!mSystemReady) { return; } @@ -1496,6 +1471,10 @@ public class NetworkMonitor extends StateMachine { Intent latencyBroadcast = new Intent(NetworkMonitorUtils.ACTION_NETWORK_CONDITIONS_MEASURED); if (mNetworkCapabilities.hasTransport(TRANSPORT_WIFI)) { + if (!mWifiManager.isScanAlwaysAvailable()) { + return; + } + WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo(); if (currentWifiInfo != null) { // NOTE: getSSID()'s behavior changed in API 17; before that, SSIDs were not @@ -1515,39 +1494,21 @@ public class NetworkMonitor extends StateMachine { } latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_WIFI); } else if (mNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { + // TODO(b/123893112): Support multi-sim. latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_NETWORK_TYPE, mTelephonyManager.getNetworkType()); - List<CellInfo> info = mTelephonyManager.getAllCellInfo(); - if (info == null) return; - int numRegisteredCellInfo = 0; - for (CellInfo cellInfo : info) { - if (cellInfo.isRegistered()) { - numRegisteredCellInfo++; - if (numRegisteredCellInfo > 1) { - if (VDBG) { - logw("more than one registered CellInfo." - + " Can't tell which is active. Bailing."); - } - return; - } - if (cellInfo instanceof CellInfoCdma) { - CellIdentityCdma cellId = ((CellInfoCdma) cellInfo).getCellIdentity(); - latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId); - } else if (cellInfo instanceof CellInfoGsm) { - CellIdentityGsm cellId = ((CellInfoGsm) cellInfo).getCellIdentity(); - latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId); - } else if (cellInfo instanceof CellInfoLte) { - CellIdentityLte cellId = ((CellInfoLte) cellInfo).getCellIdentity(); - latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId); - } else if (cellInfo instanceof CellInfoWcdma) { - CellIdentityWcdma cellId = ((CellInfoWcdma) cellInfo).getCellIdentity(); - latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId); - } else { - if (VDBG) logw("Registered cellinfo is unrecognized"); - return; - } - } + final ServiceState dataSs = mTelephonyManager.getServiceState(); + if (dataSs == null) { + logw("failed to retrieve ServiceState"); + return; } + // See if the data sub is registered for PS services on cell. + final NetworkRegistrationState nrs = dataSs.getNetworkRegistrationState( + NetworkRegistrationState.DOMAIN_PS, + AccessNetworkConstants.TransportType.WWAN); + latencyBroadcast.putExtra( + NetworkMonitorUtils.EXTRA_CELL_ID, + nrs == null ? null : nrs.getCellIdentity()); latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_MOBILE); } else { return; diff --git a/packages/NetworkStack/tests/Android.bp b/packages/NetworkStack/tests/Android.bp index 4a09b3e205a6..5c7b514834cb 100644 --- a/packages/NetworkStack/tests/Android.bp +++ b/packages/NetworkStack/tests/Android.bp @@ -18,6 +18,7 @@ android_test { name: "NetworkStackTests", certificate: "platform", srcs: ["src/**/*.java"], + test_suites: ["device-tests"], resource_dirs: ["res"], static_libs: [ "android-support-test", diff --git a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java index a4a100000d12..af71ac534bf7 100644 --- a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java +++ b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java @@ -22,6 +22,7 @@ import static android.system.OsConstants.ETH_P_ARP; import static android.system.OsConstants.ETH_P_IP; import static android.system.OsConstants.ETH_P_IPV6; import static android.system.OsConstants.IPPROTO_ICMPV6; +import static android.system.OsConstants.IPPROTO_TCP; import static android.system.OsConstants.IPPROTO_UDP; import static android.system.OsConstants.SOCK_STREAM; @@ -1017,6 +1018,7 @@ public class ApfTest { private static final int IPV4_TCP_SEQ_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 4; private static final int IPV4_TCP_ACK_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 8; private static final int IPV4_TCP_HEADER_LENGTH_OFFSET = IPV4_TCP_HEADER_OFFSET + 12; + private static final int IPV4_TCP_HEADER_FLAG_OFFSET = IPV4_TCP_HEADER_OFFSET + 13; private static final byte[] IPV4_BROADCAST_ADDRESS = {(byte) 255, (byte) 255, (byte) 255, (byte) 255}; @@ -1568,7 +1570,7 @@ public class ApfTest { // Verify IPv4 packet from another address is passed assertPass(program, ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort, - anotherDstPort, anotherSeqNum, anotherAckNum)); + anotherDstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */)); // Remove IPv4 keepalive filter apfFilter.removeKeepalivePacketFilter(slot1); @@ -1613,15 +1615,15 @@ public class ApfTest { // dst: 10.0.0.5, port: 12345 assertDrop(program, ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum, seqNum + 1)); + dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */)); // Verify IPv4 non-keepalive ack packet from the same source address is passed assertPass(program, ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum + 100, seqNum)); + dstPort, srcPort, ackNum + 100, seqNum, 0 /* dataLength */)); // Verify IPv4 packet from another address is passed assertPass(program, ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort, - anotherDstPort, anotherSeqNum, anotherAckNum)); + anotherDstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */)); // Verify IPv6 keepalive ack packet is dropped // src: 2404:0:0:0:0:0:faf2, port: 54321 @@ -1650,13 +1652,13 @@ public class ApfTest { // Verify IPv4, IPv6 packets are passed assertPass(program, ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum, seqNum + 1)); + dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */)); assertPass(program, ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, dstPort, srcPort, ackNum, seqNum + 1)); assertPass(program, ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, srcPort, - dstPort, anotherSeqNum, anotherAckNum)); + dstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */)); assertPass(program, ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, srcPort, dstPort, anotherSeqNum, anotherAckNum)); @@ -1664,28 +1666,30 @@ public class ApfTest { apfFilter.shutdown(); } - private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport, - int dport, int seq, int ack) { - ByteBuffer packet = ByteBuffer.wrap(new byte[100]); + private static byte[] ipv4Packet(byte[] sip, byte[] dip, int sport, + int dport, int seq, int ack, int dataLength) { + final int totalLength = dataLength + IPV4_HEADER_LEN + IPV4_TCP_HEADER_LEN; + + ByteBuffer packet = ByteBuffer.wrap(new byte[totalLength + ETH_HEADER_LEN]); + + // ether type packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP); + + // IPv4 header packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45); + packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength); + packet.put(IPV4_PROTOCOL_OFFSET, (byte) IPPROTO_TCP); put(packet, IPV4_SRC_ADDR_OFFSET, sip); - put(packet, IPV4_DEST_ADDR_OFFSET, tip); + put(packet, IPV4_DEST_ADDR_OFFSET, dip); packet.putShort(IPV4_TCP_SRC_PORT_OFFSET, (short) sport); packet.putShort(IPV4_TCP_DEST_PORT_OFFSET, (short) dport); packet.putInt(IPV4_TCP_SEQ_NUM_OFFSET, seq); packet.putInt(IPV4_TCP_ACK_NUM_OFFSET, ack); - return packet.array(); - } - private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport, - int dport, int seq, int ack, int dataLength) { - final int totalLength = dataLength + IPV4_HEADER_LEN + IPV4_TCP_HEADER_LEN; - - ByteBuffer packet = ByteBuffer.wrap(ipv4Packet(sip, tip, sport, dport, seq, ack)); - packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength); - // TCP header length 5, reserved 3 bits, NS=0 + // TCP header length 5(20 bytes), reserved 3 bits, NS=0 packet.put(IPV4_TCP_HEADER_LENGTH_OFFSET, (byte) 0x50); + // TCP flags: ACK set + packet.put(IPV4_TCP_HEADER_FLAG_OFFSET, (byte) 0x10); return packet.array(); } diff --git a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java index 7e57d1eb00b0..aaaff0279fed 100644 --- a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java +++ b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java @@ -104,8 +104,8 @@ public class IpClientTest { when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm); when(mContext.getSystemService(eq(ConnectivityManager.class))).thenReturn(mCm); - when(mContext.getSystemService(INetd.class)).thenReturn(mNetd); when(mContext.getResources()).thenReturn(mResources); + when(mDependencies.getNetd(any())).thenReturn(mNetd); when(mResources.getInteger(R.integer.config_networkAvoidBadWifi)) .thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE); diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java index b98b0f798fe3..9a16bb77182e 100644 --- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java +++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java @@ -16,8 +16,7 @@ package com.android.server.connectivity; -import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN; -import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL; +import static android.net.CaptivePortal.APP_RETURN_DISMISSED; import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID; import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; @@ -41,8 +40,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; -import android.content.Intent; -import android.net.CaptivePortal; import android.net.ConnectivityManager; import android.net.INetworkMonitorCallbacks; import android.net.InetAddresses; @@ -54,10 +51,10 @@ import android.net.captiveportal.CaptivePortalProbeResult; import android.net.metrics.IpConnectivityLog; import android.net.util.SharedLog; import android.net.wifi.WifiManager; +import android.os.Bundle; import android.os.ConditionVariable; import android.os.Handler; import android.os.SystemClock; -import android.os.UserHandle; import android.provider.Settings; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -487,19 +484,23 @@ public class NetworkMonitorTest { // Check that startCaptivePortalApp sends the expected intent. nm.launchCaptivePortalApp(); - final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mContext, timeout(HANDLER_TIMEOUT_MS).times(1)) - .startActivityAsUser(intentCaptor.capture(), eq(UserHandle.CURRENT)); - final Intent intent = intentCaptor.getValue(); - assertEquals(ACTION_CAPTIVE_PORTAL_SIGN_IN, intent.getAction()); - final Network network = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK); - assertEquals(TEST_NETID, network.netId); + final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); + final ArgumentCaptor<Network> networkCaptor = ArgumentCaptor.forClass(Network.class); + verify(mCm, timeout(HANDLER_TIMEOUT_MS).times(1)) + .startCaptivePortalApp(networkCaptor.capture(), bundleCaptor.capture()); + final Bundle bundle = bundleCaptor.getValue(); + final Network bundleNetwork = bundle.getParcelable(ConnectivityManager.EXTRA_NETWORK); + assertEquals(TEST_NETID, bundleNetwork.netId); + // network is passed both in bundle and as parameter, as the bundle is opaque to the + // framework and only intended for the captive portal app, but the framework needs + // the network to identify the right NetworkMonitor. + assertEquals(TEST_NETID, networkCaptor.getValue().netId); // Have the app report that the captive portal is dismissed, and check that we revalidate. setStatus(mHttpsConnection, 204); setStatus(mHttpConnection, 204); - final CaptivePortal captivePortal = intent.getParcelableExtra(EXTRA_CAPTIVE_PORTAL); - captivePortal.reportCaptivePortalDismissed(); + + nm.notifyCaptivePortalAppFinished(APP_RETURN_DISMISSED); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null); } diff --git a/packages/NetworkStackPermissionStub/Android.bp b/packages/NetworkStackPermissionStub/Android.bp index 94870c919dfa..dd70cf56b51b 100644 --- a/packages/NetworkStackPermissionStub/Android.bp +++ b/packages/NetworkStackPermissionStub/Android.bp @@ -21,7 +21,7 @@ android_app { // a classes.dex. srcs: ["src/**/*.java"], platform_apis: true, - certificate: "platform", + certificate: "networkstack", privileged: true, manifest: "AndroidManifest.xml", } diff --git a/packages/NetworkStackPermissionStub/AndroidManifest.xml b/packages/NetworkStackPermissionStub/AndroidManifest.xml index 2ccf5ff1a01a..a8742d7ab34f 100644 --- a/packages/NetworkStackPermissionStub/AndroidManifest.xml +++ b/packages/NetworkStackPermissionStub/AndroidManifest.xml @@ -17,7 +17,8 @@ */ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.mainline.networkstack.permissionstub"> + package="com.android.networkstack.permissionstub" + android:sharedUserId="android.uid.networkstack"> <!-- This package only exists to define the below permissions, and enforce that they are only granted to apps sharing the same signature. diff --git a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml index 96045120fec4..013d2d004471 100644 --- a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml +++ b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml @@ -23,7 +23,7 @@ android:layout_marginEnd="16dp" android:gravity="center" android:clickable="true" - android:background="?android:attr/selectableItemBackground" + android:background="@*android:drawable/btn_borderless_material" android:orientation="vertical"> <ImageView diff --git a/packages/SettingsLib/res/drawable/ic_info_outline_24.xml b/packages/SettingsLib/res/drawable/ic_info_outline_24.xml new file mode 100644 index 000000000000..317e43b95cdd --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_info_outline_24.xml @@ -0,0 +1,27 @@ +<?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. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?android:attr/colorControlNormal"> + <path + android:fillColor="#FF000000" + android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/> +</vector> diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 0988cab78a49..cfc76b70275f 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-toegang"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-oudio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD oudio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Gehoortoestel"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Gekoppel aan gehoortoestel"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Gehoortoestelle"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Gekoppel aan gehoortoestelle"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Gekoppel aan media-oudio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Gekoppel aan foonoudio"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Gekoppel aan lêeroordragbediener"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Gebruik vir foonoudio"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Gebruik vir lêeroordrag"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Gebruik vir invoer"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Gebruik vir gehoortoestel"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Gebruik vir gehoortoestelle"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Bind saam"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"BIND SAAM"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Kanselleer"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android-bedryfstelsel"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Verwyderde programme"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Verwyderde programme en gebruikers"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Stelselopdaterings"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-verbinding"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Wi-Fi-warmkol"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-verbinding"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 301d6099a6a7..6e6d58fb4470 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"የሲም መዳረሻ"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"ኤችዲ ኦዲዮ፦ <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"ኤችዲ ኦዲዮ"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"መስሚያ አጋዥ መሣሪያ"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"ከመስሚያ አጋዥ መሣሪያ ጋር ተገናኝቷል"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"አጋዥ መስሚያዎች"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"ከአጋዥ መስሚያዎች ጋር ተገናኝቷል"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"ወደ ማህደረ መረጃ አውዲዮ ተያይዟል"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ወደ ስልክ አውዲዮ ተያይዟል"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ወደ ፋይል ዝውውር አገልጋይ ተያይዟል"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ለስልክ ድምፅ ተጠቀም"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ለፋይል ዝውውር ተጠቀም"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ለውፅአት ተጠቀም"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ለመስሚያ አጋዥ መሣሪያ ተጠቀም"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"ለአጋዥ መስሚያዎች ይጠቀሙ"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"አጣምር"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"አጣምር"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ይቅር"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android ስርዓተ ክወና"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"የተወገዱ መተግበሪያዎች"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"የተወገዱ መተግበሪያዎች እና ተጠቃሚዎች"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"የሥርዓት ዝማኔዎች"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB መሰካት"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ተጓጓዥ ድረስ ነጥቦች"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ብሉቱዝ ማያያዝ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 91de9e3f693c..18d47ace8608 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"الوصول إلى شريحة SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"صوت عالي الدقة: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"صوت عالي الدقة"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"سماعة الأذن الطبية"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"تم توصيل سماعة الأذن الطبية"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"سماعات الأذن الطبية"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"تمّ التوصيل بسماعات الأذن الطبية"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"متصل بالإعدادات الصوتية للوسائط"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"متصل بالإعدادات الصوتية للهاتف"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"متصل بخادم نقل الملف"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"الاستخدام لإعدادات الهاتف الصوتية"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"استخدامه لنقل الملفات"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"استخدام للإدخال"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"استخدام سماعة الأذن الطبية"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"استخدام سماعات الأذن الطبية"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"إقران"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"إقران"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"إلغاء"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"نظام التشغيل Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"التطبيقات المزالة"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"التطبيقات والمستخدمون الذين تمت إزالتهم"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"تحديثات النظام"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"ربط USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"نقطة اتصال محمولة"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ربط البلوتوث"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index e726ae46e3a2..f865563200e1 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -38,27 +38,20 @@ <string name="connected_via_network_scorer" msgid="5713793306870815341">"%1$s মাধ্যমেদি স্বয়ংক্ৰিয়ভাৱে সংযোগ কৰা হৈছে"</string> <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"নেটৱৰ্ক ৰেটিং প্ৰদানকাৰীৰ জৰিয়তে স্বয়ং সংয়োগ কৰা হ’ল"</string> <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s-ৰ মাধ্যমেদি সংযোগ কৰা হৈছে"</string> - <!-- no translation found for connected_via_app (5571999941988929520) --> - <skip /> + <string name="connected_via_app" msgid="5571999941988929520">"<xliff:g id="NAME">%1$s</xliff:g>ৰ জৰিয়তে সংযুক্ত"</string> <string name="available_via_passpoint" msgid="1617440946846329613">"%1$sৰ মাধ্যমেৰে উপলব্ধ"</string> - <!-- no translation found for tap_to_sign_up (6449724763052579434) --> - <skip /> + <string name="tap_to_sign_up" msgid="6449724763052579434">"ছাইন আপ কৰিবলৈ টিপক"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"সংযোজিত, ইণ্টাৰনেট নাই"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"ইণ্টাৰনেট সংযোগ নাই"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"ছাইন ইন কৰা দৰকাৰী"</string> <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"একচেছ পইণ্ট কিছু সময়ৰ বাবে পূৰ্ণ হৈ আছে"</string> <string name="connected_via_carrier" msgid="7583780074526041912">"%1$sৰ যোগেৰে সংযোজিত"</string> <string name="available_via_carrier" msgid="1469036129740799053">"%1$sৰ মাধ্যমেৰে উপলব্ধ"</string> - <!-- no translation found for osu_opening_provider (5488997661548640424) --> - <skip /> - <!-- no translation found for osu_connect_failed (2187750899158158934) --> - <skip /> - <!-- no translation found for osu_completing_sign_up (9037638564719197082) --> - <skip /> - <!-- no translation found for osu_sign_up_failed (7296159750352873260) --> - <skip /> - <!-- no translation found for osu_sign_up_complete (8207626049093289203) --> - <skip /> + <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> খুলি থকা হৈছে"</string> + <string name="osu_connect_failed" msgid="2187750899158158934">"সংযোগ কৰিব পৰা নগ’ল"</string> + <string name="osu_completing_sign_up" msgid="9037638564719197082">"ছাইন আপ সম্পূৰ্ণ কৰি থকা হৈছে…"</string> + <string name="osu_sign_up_failed" msgid="7296159750352873260">"ছাইন আপ সম্পূৰ্ণ কৰিব পৰা নগ’ল। আকৌ চেষ্টা কৰিবলৈ টিপক।"</string> + <string name="osu_sign_up_complete" msgid="8207626049093289203">"ছাইন আপ সম্পূৰ্ণ হৈছে সংযোগ কৰি থকা হৈছে…"</string> <string name="speed_label_very_slow" msgid="1867055264243608530">"অতি লেহেম"</string> <string name="speed_label_slow" msgid="813109590815810235">"লেহেমীয়া"</string> <string name="speed_label_okay" msgid="2331665440671174858">"ঠিক"</string> @@ -94,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"ছিম প্ৰৱেশ"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"এইচ্ছডি অডি\'অ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"এইচ্ছডি অডিঅ’"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"শ্ৰৱণ যন্ত্ৰ"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"শ্ৰৱণ যন্ত্ৰৰ লগত সংযোগ কৰা হ’ল"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"শ্ৰৱণ যন্ত্ৰ"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"শ্ৰৱণ যন্ত্ৰলৈ সংযোগ কৰা হৈছে"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"মিডিয়া অডিঅ’লৈ সংযোগ হৈছে"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ফ’ন অডিঅ\'ৰ লগত সংযোগ কৰা হ’ল"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ফাইল ট্ৰান্সফাৰ ছাৰ্ভাৰৰ সৈতে সংযোজিত হৈ আছে"</string> @@ -112,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ফ\'ন অডিঅ\'ৰ বাবে ব্যৱহাৰ কৰক"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ফাইল স্থানান্তৰ কৰিবলৈ ব্যৱহাৰ কৰক"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ইনপুটৰ বাবে ব্যৱহাৰ কৰক"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"শ্ৰৱণ যন্ত্ৰৰ বাবে ব্যৱহাৰ কৰক"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"শ্ৰৱণ যন্ত্ৰৰ বাবে ব্যৱহাৰ কৰক"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"যোৰা লগাওক"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"যোৰা লগাওক"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"বাতিল কৰক"</string> @@ -143,8 +136,10 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"আঁতৰোৱা এপ্সমূহ"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"আঁতৰোৱা এপ্ আৰু ব্যৱহাৰকাৰীসমূহ"</string> + <!-- no translation found for data_usage_ota (5377889154805560860) --> + <skip /> <string name="tether_settings_title_usb" msgid="6688416425801386511">"ইউএছবি টেডাৰিং"</string> - <string name="tether_settings_title_wifi" msgid="3277144155960302049">"প\'ৰ্টেবল হ\'টস্প\'ট"</string> + <string name="tether_settings_title_wifi" msgid="3277144155960302049">"প\'ৰ্টেবল হটস্পট"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ব্লুটুথ টেডাৰিং"</string> <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"টেডাৰ কৰি থকা হৈছে"</string> <string name="tether_settings_title_all" msgid="8356136101061143841">"টেডাৰিং আৰু প\'ৰ্টেবল হটস্পট"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 4dd4817cee8a..8e6e07d17d09 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Girişi"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Qulaqlıq"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Qulaqlığa qoşuldunuz"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Eşitmə Aparatı"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Eşitmə Aparatlarına qoşuldu"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Media audioya birləşdirilib"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Telefon audiosuna qoşulu"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Fayl transfer serverinə qoşulu"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Telefon audiosu istifadə edin"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Fayl transferi üçün istifadə edin"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Daxiletmə üçün istifadə edin"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Qulaqlıq üçün istifadə edin"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Eşitmə Aparatları üçün istifadə edin"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Birləşdir"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"CÜTLƏNDİR"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Ləğv et"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Silinmiş tətbiqlər"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Tətbiqləri və istifadəçiləri silin"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Sistem güncəllənməsi"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB Birləşmə"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Portativ hotspot"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth birləşmə"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index dc5430114ba7..1cdd1fd30984 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Pristup SIM kartici"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD zvuk: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD zvuk"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Slušni aparat"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Povezano sa slušnim aparatom"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Slušni aparati"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Povezano sa slušnim aparatima"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Povezano sa zvukom medija"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Povezano sa zvukom telefona"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Povezano sa serverom za prenos datoteka"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Korišćenje za audio telefona"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Korišćenje za prenos datoteka"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Koristi za ulaz"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Koristi za slušni aparat"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Koristi za slušne aparate"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Upari"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"UPARI"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Otkaži"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Uklonjene aplikacije"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Uklonjene aplikacije i korisnici"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Ažuriranja sistema"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB Internet povezivanje"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Prenosni hotspot"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth privezivanje"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 2fa2692a3bbf..1a60c3d1a531 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Доступ да SIM-карты"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Аўдыя ў HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Аўдыя ў HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Слыхавы апарат"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Падключана да слыхавога апарата"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Слыхавыя апараты"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Падключана да слыхавых апаратаў"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Падключана да аўдыё медыа"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Падключана да аўдыё тэлефона"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Падключаны да серверу перадачы файлаў"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Выкарыстоўваць для аўдыё тэлефона"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Выкарыстоўваць для перадачы файлаў"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Выкарыстоўваць для ўводу"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Выкарыстоўваць для слыхавога апарата"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Выкарыстоўваць для слыхавых апаратаў"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Падлучыць"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"СПАЛУЧЫЦЬ"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Скасаваць"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"АС Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Выдаленыя прыкладанні"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Выдаленыя прыкладанні і карыстальнiкi"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Абнаўленні сістэмы"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-мадэм"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Партатыўная кропка доступу"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-мадэм"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 1454118c46c0..aaedce4bf1a2 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Достъп до SIM картата"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Висококачествено аудио: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Висококачествено аудио"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Слухов апарат"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Има връзка със слуховия апарат"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Слухови апарати"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Установена е връзка със слухов апарат"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Установена е връзка с медийно аудио"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Връзка със звука на телефона"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Установена е връзка със сървър за трансфер на файлове"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Използване на телефон за аудио"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Използване на за пренос на файлове"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Да се използва за въвеждане"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Използване за слухов апарат"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Използване за слухови апарати"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Сдвояване"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"СДВОЯВАНЕ"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Отказ"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android (ОС)"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Премахнати приложения"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Премахнати приложения и потребители"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Системни актуализации"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Тетъринг през USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Преносима точка за достъп"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Тетъринг през Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index dbf397f10c2a..ef8dd3de56a9 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -38,27 +38,20 @@ <string name="connected_via_network_scorer" msgid="5713793306870815341">"স্বয়ংক্রিয়ভাবে %1$s এর মাধ্যমে কানেক্ট হয়েছে"</string> <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"নেটওয়ার্কের রেটিং প্রদানকারীর মাধ্যমে অটোমেটিক কানেক্ট"</string> <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s মাধ্যমে কানেক্ট হয়েছে"</string> - <!-- no translation found for connected_via_app (5571999941988929520) --> - <skip /> + <string name="connected_via_app" msgid="5571999941988929520">"<xliff:g id="NAME">%1$s</xliff:g>-এর মাধ্যমে কানেক্ট করা আছে"</string> <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s এর মাধ্যমে উপলব্ধ"</string> - <!-- no translation found for tap_to_sign_up (6449724763052579434) --> - <skip /> + <string name="tap_to_sign_up" msgid="6449724763052579434">"সাইন-আপ করতে ট্যাপ করুন"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"কানেক্ট, ইন্টারনেট নেই"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"ইন্টারনেট কানেকশন নেই"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"সাইন-ইন করা দরকার"</string> <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"এই মুহূর্তে অ্যাক্সেস পয়েন্টের কোনও কানেকশন ফাঁকা নেই"</string> <string name="connected_via_carrier" msgid="7583780074526041912">"%1$s এর মাধ্যমে কানেক্ট হয়েছে"</string> <string name="available_via_carrier" msgid="1469036129740799053">"%1$s এর মাধ্যমে পাওয়া যাচ্ছে"</string> - <!-- no translation found for osu_opening_provider (5488997661548640424) --> - <skip /> - <!-- no translation found for osu_connect_failed (2187750899158158934) --> - <skip /> - <!-- no translation found for osu_completing_sign_up (9037638564719197082) --> - <skip /> - <!-- no translation found for osu_sign_up_failed (7296159750352873260) --> - <skip /> - <!-- no translation found for osu_sign_up_complete (8207626049093289203) --> - <skip /> + <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> খোলা হচ্ছে"</string> + <string name="osu_connect_failed" msgid="2187750899158158934">"কানেক্ট করা যায়নি"</string> + <string name="osu_completing_sign_up" msgid="9037638564719197082">"সাইন-আপ সম্পূর্ণ করা হচ্ছে…"</string> + <string name="osu_sign_up_failed" msgid="7296159750352873260">"সাইন-আপ করা যায়নি। আবার চেষ্টা করতে ট্যাপ করুন।"</string> + <string name="osu_sign_up_complete" msgid="8207626049093289203">"সাইন-আপ করা হয়ে গেছে। কানেক্ট করা হচ্ছে…"</string> <string name="speed_label_very_slow" msgid="1867055264243608530">"খুব ধীরে"</string> <string name="speed_label_slow" msgid="813109590815810235">"ধীরে"</string> <string name="speed_label_okay" msgid="2331665440671174858">"ঠিক আছে"</string> @@ -94,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"সিম -এর অ্যাক্সেস"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD অডিও: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD অডিও"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"হিয়ারিং এড"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"শ্রবণ যন্ত্রের সাথে কানেক্ট রয়েছে"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"হিয়ারিং এড"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"হিয়ারিং এডের সাথে কানেক্ট করা হয়েছে"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"মিডিয়া অডিওতে কানেক্ট রয়েছে"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ফোন অডিওতে কানেক্ট"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ফাইল স্থানান্তর সার্ভারের সঙ্গে কানেক্ট"</string> @@ -112,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ফোন অডিওয়ের জন্য ব্যবহার করুন"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ফাইল স্থানান্তরের জন্য ব্যবহার করুন"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ইনপুটের জন্য ব্যবহার করুন"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"শ্রবণ যন্ত্রের জন্য ব্যবহার করুন"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"হিয়ারিং এডের জন্য ব্যবহার করুন"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"যুক্ত করুন"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"যুক্ত করুন"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"বাতিল করুন"</string> @@ -143,6 +136,8 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"সরানো অ্যাপ্লিকেশানগুলি"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"সরানো অ্যাপ্লিকেশানগুলি এবং ব্যবহারকারীগণ"</string> + <!-- no translation found for data_usage_ota (5377889154805560860) --> + <skip /> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB টিথারিং"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"পোর্টেবল হটস্পট"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ব্লুটুথ টিথারিং"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 272e7971e5bf..4193d5250570 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Pristup SIM-u"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Slušni aparat"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Povezano na slušni aparat"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Slušni aparat"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Povezan na slušne aparate"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Povezano sa zvukom medija"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Povezano na zvuk telefona"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Povezano sa serverom za prijenos podataka"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Koristi za zvuk telefona"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Koristi za prijenos fajlova"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Koristi kao ulaz"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Koristi za slušni aparat"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Korištenje za slušne aparate"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Upari"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"UPARI"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Otkaži"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Uklonjene aplikacije"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Uklonjene aplikacije i korisnici"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Ažuriranja sistema"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Povezivanje mobitela USB-om"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Prijenosna pristupna tačka"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Dijeljenje Bluetooth veze"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 5d2dd65f9824..9c3e4d94cf26 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Accés a la SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Àudio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Àudio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Audiòfon"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"S\'ha connectat a l\'audiòfon"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Audiòfons"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"S\'ha connectat als audiòfons"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connectat a l\'àudio del mitjà"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connectat a àudio del telèfon"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connectat al servidor de transferència de fitxers"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utilitza-ho per a l\'àudio del telèfon"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utilitza per a la transferència de fitxers"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utilitza per a entrada"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Utilitza per a l\'audiòfon"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Utilitza per als audiòfons"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Vincula"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"VINCULA"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancel·la"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicacions eliminades"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplicacions i usuaris eliminats"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Actualitzacions del sistema"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Compartició de xarxa per USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Punt d\'accés Wi-Fi"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Compartició de xarxa per Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 19a042ed2f6d..b6507bccce1d 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Přístup k SIM kartě"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD zvuk: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD zvuk"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Naslouchátko"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Připojeno k naslouchátku"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Naslouchátka"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Připojeno k naslouchátkům"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Připojeno ke zvukovému médiu"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Připojeno k náhlavní soupravě"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Připojeno k serveru pro přenos dat"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Umožňuje připojení náhlavní soupravy"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Použít pro přenos souborů"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Použít pro vstup"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Použít pro naslouchátko"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Použít pro naslouchátka"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Párovat"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PÁROVAT"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Zrušit"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"OS Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Odebrané aplikace"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Odebrané aplikace a odebraní uživatelé"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Aktualizace systému"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Připojení přes USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Přenosný hotspot"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Připojení přes Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index bde67fde480f..a870cf883590 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-adgang"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-lyd: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD-lyd"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Høreapparat"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Der er oprettet forbindelse til høreapparat"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Høreapparater"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Forbundet til høreapparater"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Forbundet til medielyd"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Forbundet til telefonlyd"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Forbundet til filoverførselsserver"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Brug til telefonlyd"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Brug til filoverførsel"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Brug til input"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Anvend til høreapparat"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Brug til høreapparater"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Par"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ACCEPTÉR PARRING"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Annuller"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Fjernede apps"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Fjernede apps og brugere"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Systemopdateringer"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Netdeling via USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Netdeling via Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 543e8f86ee71..c1d502e30717 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Zugriff auf SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-Audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD-Audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Hörhilfe"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Mit Hörhilfe verbunden"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Hörhilfen"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Mit Hörhilfen verbunden"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Verbunden mit Medien-Audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Verbunden mit Telefon-Audio"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Mit Dateiübertragungsserver verbunden"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Für Telefon-Audio verwenden"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Für Dateiübertragung verwenden"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Für Eingabe verwenden"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Für Hörhilfe verwenden"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Für Hörhilfen verwenden"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Koppeln"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"KOPPELN"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Abbrechen"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Entfernte Apps"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Entfernte Apps und Nutzer"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Systemupdates"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-Tethering"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Mobiler Hotspot"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-Tethering"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index f7ec12be8651..22c1b26dc2fa 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Πρόσβαση SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Ήχος HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Ήχος HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Βοήθημα ακοής"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Συνδέθηκε σε βοήθημα ακοής"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Βοηθήματα ακοής"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Έγινε σύνδεση σε βοηθήματα ακοής"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Συνδέθηκε σε ήχο πολυμέσων"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Συνδεδεμένο στον ήχο τηλεφώνου"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Συνδεδεμένο σε διακομιστή μεταφοράς αρχείων"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Χρήση για ήχο τηλεφώνου"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Χρήση για τη μεταφορά αρχείων"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Χρήση για είσοδο"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Χρήση για βοήθημα ακοής"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Χρήση για βοηθήματα ακοής"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Σύζευξη"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ΣΥΖΕΥΞΗ"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Ακύρωση"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Λειτουργικό σύστημα Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Εφαρμογές που καταργήθηκαν"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Εφαρμογές και χρήστες που έχουν καταργηθεί"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Ενημερώσεις συστήματος"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Πρόσδεση USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Φορητό σημείο πρόσβασης"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Πρόσδεση Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index b9d309398d67..d4f4e5e1104a 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Access"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Hearing Aid"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Connected to Hearing Aid"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Hearing Aids"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Connected to Hearing Aids"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connected to media audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connected to phone audio"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connected to file-transfer server"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Use for phone audio"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Use for file transfer"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Use for input"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Use for Hearing Aid"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Use for Hearing Aids"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Pair"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAIR"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancel"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Removed apps"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Removed apps and users"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"System updates"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB tethering"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Portable hotspot"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth tethering"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index b9d309398d67..d4f4e5e1104a 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Access"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Hearing Aid"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Connected to Hearing Aid"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Hearing Aids"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Connected to Hearing Aids"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connected to media audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connected to phone audio"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connected to file-transfer server"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Use for phone audio"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Use for file transfer"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Use for input"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Use for Hearing Aid"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Use for Hearing Aids"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Pair"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAIR"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancel"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Removed apps"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Removed apps and users"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"System updates"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB tethering"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Portable hotspot"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth tethering"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index b9d309398d67..d4f4e5e1104a 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Access"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Hearing Aid"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Connected to Hearing Aid"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Hearing Aids"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Connected to Hearing Aids"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connected to media audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connected to phone audio"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connected to file-transfer server"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Use for phone audio"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Use for file transfer"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Use for input"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Use for Hearing Aid"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Use for Hearing Aids"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Pair"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAIR"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancel"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Removed apps"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Removed apps and users"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"System updates"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB tethering"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Portable hotspot"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth tethering"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index b9d309398d67..d4f4e5e1104a 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Access"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Hearing Aid"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Connected to Hearing Aid"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Hearing Aids"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Connected to Hearing Aids"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connected to media audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connected to phone audio"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connected to file-transfer server"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Use for phone audio"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Use for file transfer"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Use for input"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Use for Hearing Aid"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Use for Hearing Aids"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Pair"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAIR"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancel"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Removed apps"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Removed apps and users"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"System updates"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB tethering"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Portable hotspot"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth tethering"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index 2d3e8301cb1e..98b63eec19dc 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Access"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Hearing Aid"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Connected to Hearing Aid"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Hearing Aids"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Connected to Hearing Aids"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connected to media audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connected to phone audio"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connected to file transfer server"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Use for phone audio"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Use for file transfer"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Use for input"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Use for Hearing Aid"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Use for Hearing Aids"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Pair"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAIR"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancel"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Removed apps"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Removed apps and users"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"System updates"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB tethering"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Portable hotspot"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth tethering"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 00f85e665112..e803abdac612 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acceso SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio en HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio en HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Audífonos"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Conectado a un audífono"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Audífonos"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Conectado a audífonos"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Conectado al audio multimedia"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Conectado al audio del dispositivo"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Conectado al servidor de transferencia de archivo"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utilizar para el audio del dispositivo"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utilizar para la transferencia de archivos"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utilizar para entrada"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Usar con audífonos"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Usar para audífonos"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Vincular"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SINCRONIZAR"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancelar"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"SO Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicaciones eliminadas"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplicaciones y usuarios eliminados"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Actualizaciones del sistema"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Conexión mediante USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot portátil"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Conexión mediante Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index b21b7d1ead5f..293eff121b2a 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acceso a tarjeta SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Audífonos"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Conectado a audífono"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Audífonos"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Conectado a audífonos"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Conectado al audio del medio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Conectado al audio del teléfono"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Conectado con el servidor de transferencia de archivos"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utilizar para audio del teléfono"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Uso de la transferencia de archivos"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Usar para entrada"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Usar con audífonos"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Usar con audífonos"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Vincular"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"VINCULAR"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancelar"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"SO Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicaciones eliminadas"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Usuarios y aplicaciones eliminados"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Actualizaciones del sistema"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Compartir conexión por USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Zona Wi-Fi portátil"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Compartir conexión por Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index fab137519725..14ac8272432f 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-kaardi juurdepääs"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-heli: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD-heli"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Kuuldeaparaat"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Kuuldeaparaadiga ühendatud"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Kuuldeaparaadid"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Kuuldeaparaatidega ühendatud"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Ühendatud meediumiheliga"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Ühendatud telefoniheliga"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Ühendatud failiedastuse serveriga"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Kasuta telefoniheli jaoks"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Kasutage failide edastamiseks"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Kasutage sisendi jaoks"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Kuuldeaparaadiga kasutamiseks"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Kasuta kuulmisaparaatide puhul"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Seo"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SEO"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Tühista"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Eemaldatud rakendused"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Eemaldatud rakendused ja kasutajad"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Süsteemivärskendused"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB jagamine"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Mobiilne kuumkoht"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Jagamine Bluetoothiga"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 7d3e1f3049da..04710c7bb0cb 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM txartelerako sarbidea"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Kalitate handiko audioa: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Kalitate handiko audioa"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Audiofonoa"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Audiofonora konektatuta"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Audifonoak"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Audifonoetara konektatuta"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Euskarriaren audiora konektatuta"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Telefonoaren audiora konektatuta"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Fitxategi-transferentziako zerbitzarira konektatuta"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Erabili telefonoaren audiorako"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Erabili fitxategi-transferentziarako"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Erabili idazketarako"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Erabili audiofonorako"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Erabili audifonoak"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Parekatu"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAREKATU"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Utzi"</string> @@ -121,8 +121,8 @@ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Aurikularra"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Idazteko gailua"</string> <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth gailua"</string> - <string name="bluetooth_hearingaid_left_pairing_message" msgid="7378813500862148102">"Ezkerreko audiofonoa parekatzen…"</string> - <string name="bluetooth_hearingaid_right_pairing_message" msgid="1550373802309160891">"Eskuineko audiofonoa parekatzen…"</string> + <string name="bluetooth_hearingaid_left_pairing_message" msgid="7378813500862148102">"Ezkerreko audifonoa parekatzen…"</string> + <string name="bluetooth_hearingaid_right_pairing_message" msgid="1550373802309160891">"Eskuineko audifonoa parekatzen…"</string> <string name="bluetooth_hearingaid_left_battery_level" msgid="8797811465352097562">"Ezkerrekoa. Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> <string name="bluetooth_hearingaid_right_battery_level" msgid="7309476148173459677">"Eskuinekoa. Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> <string name="accessibility_wifi_off" msgid="1166761729660614716">"Desaktibatuta dago Wi-Fi konexioa."</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android sistema eragilea"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Kendutako aplikazioak"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Kendutako aplikazioak eta erabiltzaileak"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Sistemaren eguneratzeak"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Konexioa partekatzea (USB)"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Sare publiko eramangarria"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Konexioa partekatzea (Bluetooth)"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 293c463334fd..8f5509255f51 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"دسترسی سیمکارت"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"صدای HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"صدای HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"سمعک"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"متصل به سمعک"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"سمعکها"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"متصل به سمعکها"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"به رسانه صوتی متصل شد"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"به تلفن صوتی متصل شد"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"به سرور انتقال فایل متصل شد"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"استفاده برای تلفن صوتی"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"استفاده برای انتقال فایل"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"استفاده برای چاپ"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"استفاده برای سمعک"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"استفاده برای سمعکها"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"مرتبطسازی"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"مرتبطسازی"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"لغو"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"سیستم عامل Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"برنامههای حذف شده"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"برنامهها و کاربران حذف شده"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"بهروزرسانیهای سیستم"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"اتصال داده با سیم USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"نقطه اتصال قابل حمل"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"اتصال اینترنت با تلفن همراه بلوتوث"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index f335eb930c1c..5802a03f1070 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-kortin käyttö"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-ääni: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD-ääni"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Kuulolaite"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Kuulolaite yhdistetty"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Kuulolaitteet"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Yhdistetty kuulolaitteisiin"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Yhdistetty median ääneen"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Yhdistetty puhelimen ääneen"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Yhdistetty tiedostonsiirtopalvelimeen"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Käytä puhelimen äänille"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Käytä tiedostojen siirtoon"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Käytä syöttöön"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Käytä kuulolaitteen kanssa"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Käytä kuulolaitteilla"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Muodosta laitepari"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"MUODOSTA LAITEPARI"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Peruuta"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android-käyttöjärjestelmä"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Poistetut sovellukset"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Poistetut sovellukset ja käyttäjät"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Järjestelmäpäivitykset"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Jaettu yhteys USB:n kautta"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Kannettava yhteyspiste"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Jaettu Bluetooth-yhteys"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 49591039178c..ec8d6b086418 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Accès à la carte SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio HD : <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Prothèse auditive"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Connecté à la prothèse auditive"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Prothèses auditives"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Connecté aux prothèses auditives"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connecté aux paramètres audio du média"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connecté à l\'audio du téléphone"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connexion au serveur de transfert de fichiers"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utiliser pour les paramètres audio du téléphone"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utiliser pour le transfert de fichiers"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utiliser comme entrée"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Utiliser avec la prothèse auditive"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Utiliser avec les prothèses auditives"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Associer"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ASSOCIER"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Annuler"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Système d\'exploitation Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Applications supprimées"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Applications et utilisateurs supprimés"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Mises à jour système"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Partage de connexion par USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Point d\'accès Wi-Fi mobile"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Partage connexion Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 1f071de1ac6b..0b620d80cbb2 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Accès à la carte SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio HD : <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Assistance auditive"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Connecté à la prothèse auditive"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Appareils auditifs"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Connexion établie avec les appareils auditifs"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Connecté aux paramètres audio du média"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Connecté aux paramètres audio du téléphone"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Connexion au serveur de transfert de fichiers"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utiliser pour les paramètres audio du téléphone"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utiliser pour le transfert de fichiers"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utiliser comme entrée"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Utiliser pour l\'assistance auditive"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Utiliser pour les appareils auditifs"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Associer"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ASSOCIER"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Annuler"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Plate-forme Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Applications supprimées"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Applications et utilisateurs supprimés"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Mises à jour du système"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Partage connexion Bluetooth par USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Point d\'accès Wi-Fi mobile"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Partage connexion Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 06faf00036dd..b4d7fcb9270f 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acceso á SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio en HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio en HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Audiófonos"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Conectouse ao audiófono"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Audiófonos"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Conectado a audiófonos"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Conectado ao audio multimedia"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Conectado ao audio do teléfono"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Conectado ao servidor de transferencia de ficheiros"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utilízase para o audio do teléfono"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utilízase para a transferencia de ficheiros"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utilízase para a entrada"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Usar con audiófonos"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Utilizar para audiófonos"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Sincronizar"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SINCRONIZAR"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancelar"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"SO Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicacións eliminadas"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplicacións e usuarios eliminados"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Actualizacións do sistema"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Conexión compart. por USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Zona wifi portátil"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Conexión por Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 232dea9e1f1a..97b9036157a3 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"સિમ ઍક્સેસ"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ઑડિઓ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ઑડિઓ"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"સાંભળવામાં સહાય આપતું યંત્ર"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"સાંભળવામાં સહાય આપતા યંત્ર સાથે કનેક્ટ કરેલ"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"શ્રવણ યંત્રો"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"શ્રવણ યંત્રો સાથે કનેક્ટ કરેલું છે"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"મીડિયા ઑડિઓ સાથે કનેક્ટ કર્યુ"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ફોન ઑડિઓ સાથે કનેક્ટ થયાં"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ફાઇલ સ્થાનાંતરણ સેવાથી કનેક્ટ થયાં"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ફોન ઑડિઓ માટે ઉપયોગ કરો"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ફાઇલ સ્થાનાંતર માટે ઉપયોગ કરો"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ઇનપુટ માટે ઉપયોગ કરો"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"સાંભળવામાં સહાય આપતા યંત્ર માટે ઉપયોગ કરો"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"શ્રવણ યંત્રો માટે ઉપયોગ કરો"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"જોડી"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"જોડાણ બનાવો"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"રદ કરો"</string> @@ -136,6 +136,8 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"દૂર કરેલી ઍપ્લિકેશનો"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"દૂર કરેલી ઍપ્લિકેશનો અને વપરાશકર્તાઓ"</string> + <!-- no translation found for data_usage_ota (5377889154805560860) --> + <skip /> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ટિથરિંગ"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"પોર્ટેબલ હૉટસ્પૉટ"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"બ્લૂટૂથ ટિથરિંગ"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 2f03eb1b9c3a..8b1ca93d9acd 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"सिम ऐक्सेस"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ऑडियो: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ऑडियो"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"सुनने में मददगार डिवाइस"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"सुनने में मददगार डिवाइस से जाेड़ा गया"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"सुनने में मदद करने वाले डिवाइस"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"सुनने में मदद करने वाले डिवाइस से कनेक्ट है"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"मीडिया ऑडियो से कनेक्ट किया गया"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"फ़ोन ऑडियो से कनेक्ट किया गया"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"फ़ाइल स्थानांतरण सर्वर से कनेक्ट किया गया"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"फ़ोन ऑडियो के लिए उपयोग करें"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"फ़ाइल स्थानांतरण के लिए उपयोग करें"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"इनपुट के लिए उपयोग करें"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"सुनने में मददगार डिवाइस के लिए इस्तेमाल करें"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"सुनने में मदद करने वाले डिवाइस के लिए इस्तेमाल करें"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"जोड़ें"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"जोड़ें"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"रद्द करें"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"निकाले गए ऐप्स"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ऐप्स और उपयोगकर्ताओं को निकालें"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"सिस्टम अपडेट"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"यूएसबी से टेदरिंग"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"पोर्टेबल हॉटस्पॉट"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ब्लूटूथ टेदरिंग"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index bb14b066276b..b4cbbdbf3af9 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Pristup SIM-u"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Slušni aparat"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Povezano sa slušnim aparatom"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Slušni aparati"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Povezano sa Slušnim aparatima"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Povezano s medijskim zvukom"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Povezano sa telefonskim zvukom"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Povezano s poslužiteljem za prijenos datoteka"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Koristi za telefonski zvuk"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Koristi za prijenos datoteke"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Upotrijebi za ulaz"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Upotrebljavaj za slušni aparat"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Upotrijebi za Slušne aparate"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Upari"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"UPARI"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Odustani"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Uklonjene aplikacije"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Uklonjene aplikacije i korisnici"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Ažuriranja sustava"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB dijeljenje veze"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Prijen. pristupna točka"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Dijeljenje Bluetoothom veze"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 14d8c78ed6b8..589eb59d4299 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-elérés"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Hallókészülék"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Csatlakoztatva a hallókészülékhez"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Hallókészülékek"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Hallókészülékhez csatlakoztatva"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Csatlakoztatva az eszköz hangjához"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Csatlakoztatva a telefon hangjához"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Csatlakozva a fájlküldő szerverhez"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Felhasználás a telefon hangjához"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Felhasználás fájlátvitelre"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Használat beviteli eszközként"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Használat hallókészülékhez"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Hallókészülékkel való használat"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Párosítás"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PÁROSÍTÁS"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Mégse"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Eltávolított alkalmazások"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Eltávolított alkalmazások és felhasználók"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Rendszerfrissítések"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-megosztás"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hordozható hotspot"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth megosztása"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 28ebe4febea9..736b4c3d88c9 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM քարտի հասանելիություն"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD աուդիո՝ <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD աուդիո"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Լսողական ապարատ"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Միացված է լսողական ապարատին"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Լսողական ապարատ"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Լսողական ապարատը միացված է"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Միացված է մեդիա աուդիոյին"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Միացված է հեռախոսի ձայնային տվյալներին"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Միացված է ֆայլերի փոխանցման սերվերին"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Օգտագործել հեռախոսի աուդիոյի համար"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Օգտագործել ֆայլի փոխանցման համար"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Օգտագործել ներմուծման համար"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Օգտագործել լսողական ապարատի համար"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Օգտագործել լսողական ապարատի համար"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Զուգավորել"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"Զուգավորել"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Չեղարկել"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Հեռացված ծրագրեր"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Հեռացված հավելվածներն ու օգտատերերը"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Համակարգի թարմացումներ"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB մոդեմ"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Դյուրակիր թեժ կետ"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth մոդեմ"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 182e52338891..c5d8f30d332f 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Akses SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Alat Bantu Dengar"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Terhubung ke Alat Bantu Dengar"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Alat Bantu Dengar"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Terhubung ke Alat Bantu Dengar"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Tersambung ke media audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Tersambung ke audio ponsel"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Sambungkan ke server transfer file"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Gunakan untuk audio ponsel"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Gunakan untuk transfer file"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Gunakan untuk masukan"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Gunakan untuk Alat Bantu Dengar"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Gunakan untuk Alat Bantu Dengar"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Sandingkan"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SANDINGKAN"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Batal"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"OS Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplikasi dihapus"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplikasi dan pengguna yang dihapus"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Update sistem"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Tethering USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot portabel"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Tethering bluetooth"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 577b7ae4dadb..58505f55e6e5 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Aðgangur að SIM-korti"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-hljóð: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD-hljóð"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Heyrnatæki"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Tengt við heyrnartæki"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Heyrnartæki"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Tengt við heyrnartæki"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Tengt við hljóðspilun efnis"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Tengt við hljóð símans"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Tengt við skráaflutningsþjón"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Nota fyrir hljóð símans"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Nota við skráaflutning"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Nota fyrir inntak"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Nota fyrir heyrnartæki"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Nota fyrir heyrnartæki"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Para"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PARA"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Hætta við"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android stýrikerfið"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Fjarlægð forrit"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Fjarlægð forrit og notendur"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Kerfisuppfærslur"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-tjóðrun"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Heitur reitur"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-tjóðrun"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 9fc32949db38..6d863aa2a982 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Accesso alla SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Apparecchio acustico"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Connesso all\'apparecchio acustico"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Apparecchi acustici"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Connessione con gli apparecchi acustici stabilita"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Collegato ad audio media"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Collegato ad audio telefono"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Collegato al server di trasferimento file"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Usa per audio telefono"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Usa per trasferimento file"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utilizza per l\'input"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Usa per l\'apparecchio acustico"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Utilizza per gli apparecchi acustici"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Accoppia"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ACCOPPIA"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Annulla"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Sistema operativo Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Applicazioni rimosse"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"App e utenti rimossi"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Aggiornamenti di sistema"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Tethering USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot portatile"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Tethering Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 8ee11e4b2069..6e37c9a6eeda 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"גישה ל-SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"אודיו באיכות HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"אודיו באיכות HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"מכשיר שמיעה"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"מחובר למכשיר שמיעה"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"מכשירי שמיעה"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"מחובר אל מכשירי שמיעה"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"מחובר לאודיו של מדיה"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"מחובר לאודיו של הטלפון"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"מחובר לשרת העברת קבצים"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"השתמש עבור האודיו של הטלפון"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"השתמש להעברת קבצים"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"השתמש לקלט"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"יש להשתמש עבור מכשיר שמיעה"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"שימוש בשביל מכשירי שמיעה"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"התאם"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"התאם"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ביטול"</string> @@ -136,6 +136,8 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"אפליקציות שהוסרו"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"אפליקציות ומשתמשים שהוסרו"</string> + <!-- no translation found for data_usage_ota (5377889154805560860) --> + <skip /> <string name="tether_settings_title_usb" msgid="6688416425801386511">"שיתוף אינטרנט דרך USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"נקודה לשיתוף אינטרנט"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"שיתוף אינטרנט דרך Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 4efd61b37383..233e8e80b646 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIMアクセス"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD オーディオ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD オーディオ"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"補聴器"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"補聴器に接続済み"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"補聴器"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"補聴器に接続"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"メディアの音声に接続"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"携帯電話の音声に接続"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ファイル転送サーバーに接続"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"携帯電話の音声に使用"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ファイル転送に使用"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"入力に使用"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"補聴器に使用"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"補聴器に使用"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ペア設定する"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ペア設定する"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"キャンセル"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"削除したアプリケーション"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"削除されたアプリとユーザー"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"システム アップデート"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB テザリング"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ポータブルアクセスポイント"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth テザリング"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index c613a06b2544..09bea2a0795f 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM წვდომა"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD აუდიო: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD აუდიო"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"სმენის აპარატი"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"დაკავშირებულია სმენის აპარატთან"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"სმენის მოწყობილობები"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"დაკავშირებულია სმენის მოწყობილობებთან"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"დაკავშირებულია აუდიო მულტიმედიურ სისტემასთან"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"დაკავშირებულია ტელეფონის აუდიო მოწყობილობასთან"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"დაკავშირებულია ფაილების გადაცემის სერვერთან"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"გამოიყენეთ ტელეფონის აუდიომოწყობილობაში"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ფაილების ტრანსფერისათვის გამოყენება"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"შეტანისთვის გამოყენება"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"სმენის აპარატის გამოყენება"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"გამოყენება სმენის მოწყობილობებისთვის"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"დაწყვილება"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"დაწყვილება"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"გაუქმება"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"აპების წაშლა"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"წაშლილი აპები და მომხმარებლები"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"სისტემის განახლებები"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ტეტერინგი"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"პორტატული უსადენო ქსელი"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth ტეტერინგი"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 8d6d76ecd30d..8670ec96012e 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM картасына кіру"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD форматты аудиомазмұн: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD форматты аудиомазмұн"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Есту аппараты"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Есту аппаратына жалғанған"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Есту аппараттары"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Есту аппараттарына жалғанған"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Медиа аудиосына жалғанған"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Телефон аудиосына қосылған"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Файл жіберу серверіне жалғанған"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Телефон аудиосы үшін қолдану"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Файлды жіберу үшін қолдану"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Кіріс үшін қолдану"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Есту аппаратына пайдалану"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Есту аппараттары үшін пайдалану"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Жұптау"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ЖҰПТАУ"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Бас тарту"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android операциялық жүйесі"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Алынған қолданбалар"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Алынған қолданбалар және пайдаланушылар"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Жүйелік жаңарту"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB тетеринг"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Алынбалы хот-спот"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth модем"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 990320108698..6bf8a0f3266f 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"ការចូលដំណើរការស៊ីម"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"សំឡេងកម្រិត HD៖ <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"សំឡេងកម្រិត HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"ឧបករណ៍ជំនួយការស្ដាប់"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"បានភ្ជាប់ទៅឧបករណ៍ជំនួយការស្តាប់"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"ឧបករណ៍ជំនួយការស្ដាប់"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"បានភ្ជាប់ទៅឧបករណ៍ជំនួយការស្ដាប់"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"បានភ្ជាប់ទៅអូឌីយ៉ូមេឌៀ"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"តភ្ជាប់ទៅអូឌីយ៉ូទូរស័ព្ទ"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"បានតភ្ជាប់ទៅម៉ាស៊ីនមេផ្ទេរឯកសារ"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ប្រើសម្រាប់អូឌីយ៉ូទូរស័ព្ទ"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ប្រើសម្រាប់ផ្ទេរឯកសារ"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ប្រើសម្រាប់បញ្ចូល"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ប្រើសម្រាប់ឧបករណ៍ជំនួយការស្តាប់"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"ប្រើសម្រាប់ឧបករណ៍ជំនួយការស្តាប់"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ផ្គូផ្គង"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ផ្គូផ្គង"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"បោះបង់"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"ប្រព័ន្ធប្រតិបត្តិការ Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"កម្មវិធីដែលបានលុប"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"បានលុបកម្មវិធី និងអ្នកប្រើ"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"បច្ចុប្បន្នភាពប្រព័ន្ធ"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"ការភ្ជាប់តាម USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ហតស្ពតចល័ត"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ការភ្ជាប់ប៊្លូធូស"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 888b93fb17ad..41b0fbda408f 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -38,27 +38,20 @@ <string name="connected_via_network_scorer" msgid="5713793306870815341">"%1$s ಮೂಲಕ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string> <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"ನೆಟ್ವರ್ಕ್ ರೇಟಿಂಗ್ ಒದಗಿಸುವವರ ಮೂಲಕ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string> <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s ಮೂಲಕ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string> - <!-- no translation found for connected_via_app (5571999941988929520) --> - <skip /> + <string name="connected_via_app" msgid="5571999941988929520">"<xliff:g id="NAME">%1$s</xliff:g> ಆ್ಯಪ್ ಮೂಲಕ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string> <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ಮೂಲಕ ಲಭ್ಯವಿದೆ"</string> - <!-- no translation found for tap_to_sign_up (6449724763052579434) --> - <skip /> + <string name="tap_to_sign_up" msgid="6449724763052579434">"ಸೈನ್ ಅಪ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ, ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"ಸೈನ್ ಇನ್ ಮಾಡುವ ಅಗತ್ಯವಿದೆ"</string> <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"ಪ್ರವೇಶ ಕೇಂದ್ರ ತಾತ್ಕಾಲಿಕವಾಗಿ ಭರ್ತಿಯಾಗಿದೆ"</string> <string name="connected_via_carrier" msgid="7583780074526041912">"%1$s ಮೂಲಕ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string> <string name="available_via_carrier" msgid="1469036129740799053">"%1$s ಮೂಲಕ ಲಭ್ಯವಿದೆ"</string> - <!-- no translation found for osu_opening_provider (5488997661548640424) --> - <skip /> - <!-- no translation found for osu_connect_failed (2187750899158158934) --> - <skip /> - <!-- no translation found for osu_completing_sign_up (9037638564719197082) --> - <skip /> - <!-- no translation found for osu_sign_up_failed (7296159750352873260) --> - <skip /> - <!-- no translation found for osu_sign_up_complete (8207626049093289203) --> - <skip /> + <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯಲಾಗುತ್ತಿದೆ"</string> + <string name="osu_connect_failed" msgid="2187750899158158934">"ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ"</string> + <string name="osu_completing_sign_up" msgid="9037638564719197082">"ಸೈನ್-ಅಪ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಲಾಗುತ್ತಿದೆ…"</string> + <string name="osu_sign_up_failed" msgid="7296159750352873260">"ಸೈನ್-ಅಪ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ಮತ್ತೊಮ್ಮೆ ಪ್ರಯತ್ನಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string> + <string name="osu_sign_up_complete" msgid="8207626049093289203">"ಸೈನ್-ಅಪ್ ಪೂರ್ಣಗೊಂಡಿದೆ. ಸಂಪರ್ಕಿಸಲಾಗುತ್ತಿದೆ…"</string> <string name="speed_label_very_slow" msgid="1867055264243608530">"ತುಂಬಾ ನಿಧಾನವಾಗಿದೆ"</string> <string name="speed_label_slow" msgid="813109590815810235">"ನಿಧಾನ"</string> <string name="speed_label_okay" msgid="2331665440671174858">"ಸರಿ"</string> @@ -94,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"ಸಿಮ್ ಪ್ರವೇಶ"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ಆಡಿಯೋ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ಆಡಿಯೋ"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"ಶ್ರವಣ ಸಾಧನ"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"ಶ್ರವಣ ಸಾಧನಕ್ಕೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"ಶ್ರವಣ ಸಾಧನಗಳು"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"ಶ್ರವಣ ಸಾಧನಗಳಿಗೆ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"ಮಾಧ್ಯಮ ಆಡಿಯೋಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ಫೋನ್ ಆಡಿಯೋಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ಫೈಲ್ ವರ್ಗಾವಣೆ ಸರ್ವರ್ಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string> @@ -112,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ಫೋನ್ ಆಡಿಯೋಗಾಗಿ ಬಳಕೆ"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ಫೈಲ್ ವರ್ಗಾವಣೆಗಾಗಿ ಬಳಸು"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ಇನ್ಪುಟ್ಗಾಗಿ ಬಳಸು"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ಶ್ರವಣ ಸಾಧನಕ್ಕಾಗಿ ಬಳಸಿ"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"ಶ್ರವಣ ಸಾಧನಗಳಿಗಾಗಿ ಬಳಸಿ"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ಜೋಡಿ"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ಜೋಡಿ ಮಾಡು"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ರದ್ದುಮಾಡಿ"</string> @@ -143,6 +136,8 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ತೆಗೆದುಹಾಕಲಾದ ಅಪ್ಲಿಕೇಶನ್ಗಳು"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ಬಳಕೆದಾರರನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string> + <!-- no translation found for data_usage_ota (5377889154805560860) --> + <skip /> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ಟೆಥರಿಂಗ್"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ಪೋರ್ಟಬಲ್ ಹಾಟ್ಸ್ಪಾಟ್"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ಬ್ಲೂಟೂತ್ ಟೆಥರಿಂಗ್"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index d7e9488cea42..877866c7c320 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM 액세스"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD 오디오: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD 오디오"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"보청기"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"보청기에 연결됨"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"보청기"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"보청기에 연결됨"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"미디어 오디오에 연결됨"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"휴대전화 오디오에 연결됨"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"파일 전송 서버에 연결됨"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"휴대전화 오디오에 사용"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"파일 전송에 사용"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"입력에 사용"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"보청기에 사용"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"보청기로 사용"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"페어링"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"페어링"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"취소"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"삭제된 앱"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"삭제된 앱 및 사용자"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"시스템 업데이트"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB 테더링"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"휴대용 핫스팟"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"블루투스 테더링"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 6898c61e5b85..9e4a9a8a1250 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM картаны пайдалануу мүмкүнчүлүгү"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD форматындагы аудио: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD форматындагы аудио"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Угуу аппараты"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Угуу аппаратына туташты"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Угуу аппараттары"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Угуу аппараттарына туташып турат"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Медиа аудиого туташты"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Телефон аудиосуна туташты"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Файл өткөрүү серверине туташты"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Телефон аудиосу үчүн колдонулсун"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Файл өткөрүү үчүн колдонулсун"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Киргизүү үчүн колдонулсун"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Угуу аппараты үчүн колдонуу"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Угуу аппараттары үчүн колдонуу"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Жупташтыруу"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ЖУПТАШТЫРУУ"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Жок"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Алынып салынган колдонмолор"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Өчүрүлгөн колдонмолор жана колдонуучулар"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Тутум жаңыртуулары"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB модем"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Ташыма кошулуу чекити"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth модем"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index acbaf701ace2..7e6aaacea44e 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"ການເຂົ້າເຖິງ SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"ສຽງ HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"ສຽງ HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"ເຄື່ອງຊ່ວຍຟັງ"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"ເຊື່ອມຕໍ່ຫາເຄື່ອງຊ່ວຍຟັງແລ້ວ"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"ອຸປະກອນຊ່ວຍຟັງ"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"ເຊື່ອມຕໍ່ຫາອຸປະກອນຊ່ວຍຟັງແລ້ວ"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"ເຊື່ອມຕໍ່ກັບສື່ດ້ານສຽງແລ້ວ"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ເຊື່ອມຕໍ່ກັບສຽງໂທລະສັບແລ້ວ"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ເຊື່ອມຕໍ່ກັບເຊີບເວີໂອນຍ້າຍໄຟລ໌ແລ້ວ"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ໃຊ້ສຳລັບລະບົບສຽງຂອງໂທລະສັບ"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ໃຊ້ເພື່ອໂອນຍ້າຍໄຟລ໌"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ໃຊ້ສຳລັບການປ້ອນຂໍ້ມູນ"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ໃຊ້ກັບເຄື່ອງຊ່ວຍຟັງ"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"ໃຊ້ສຳລັບອຸປະກອນຊ່ວຍຟັງ"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ຈັບຄູ່"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ຈັບຄູ່"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ຍົກເລີກ"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ແອັບຯທີ່ຖືກລຶບອອກແລ້ວ"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ລຶບແອັບຯ ແລະຜູ່ໃຊ້ແລ້ວ"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"ການອັບເດດລະບົບ"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"ການປ່ອຍສັນຍານຜ່ານ USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ຮັອດສະປອດເຄື່ອນທີ່"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ປ່ອຍສັນຍານຜ່ານ Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 89334517aa7d..71b8cf0ff375 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM prieiga"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD garsas: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD garsas"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Klausos aparatas"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Prisijungta prie klausos aparato"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Klausos aparatai"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Prisijungta prie klausos aparatų"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Prijungta prie medijos garso įrašo"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Prijungta prie telefono garso"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Prijungta prie failų perkėlimo serverio"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Naudoti telefono garso įrašui"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Naudoti failų perkėlimui"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Naudoti įvedant"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Klausos aparato naudojimas"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Naudoti su klausos aparatais"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Susieti"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SUSIETI"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Atšaukti"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"„Android“ OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Pašalintos programos"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Pašalintos programos ir naudotojai"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Sistemos naujiniai"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB susiejimas"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Perkeliama aktyvioji sritis"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"„Bluetooth“ susiejimas"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index ac41ae082c1e..82ec0ae5b1cd 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Piekļuve SIM kartei"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Dzirdes aparāts"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Izveidots savienojums ar dzirdes aparātu"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Dzirdes aparāti"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Izveidots savienojums ar dzirdes aparātiem"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Savienots ar multivides audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Savienots ar tālruņa audio"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Savienots ar failu pārsūtīšanas serveri"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Izmantot tālruņa skaņai"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Izmantot faila pārsūtīšanai"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Izmantot ievadei"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Izmantot dzirdes aparātam"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Izmantot dzirdes aparātiem"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Izveidot pāri"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SAVIENOT PĀRĪ"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Atcelt"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Noņemtās lietotnes"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Noņemtās lietotnes un lietotāji"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Sistēmas atjauninājumi"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB saistīšana"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Pārnēsājams tīklājs"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth saistīšana"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 5b45977a32a6..c99ef681b899 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Пристап до SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD аудио: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD аудио"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Слушно помагало"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Поврзано со слушно помагало"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Слушни помагала"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Поврзано со слушни помагала"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Поврзан со аудио на медиуми"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Поврзан со аудио на телефон"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Поврзан со сервер за пренос на датотеки"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Користи за аудио на телефон"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Користи за пренос на датотеки"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Користи за внес"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Користете како слушно помагало"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Користи за слушни помагала"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Спари"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"СПАРИ"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Откажи"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Оперативен систем Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Отстранети апликации"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Отстранети апликации и корисници"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Ажурирања на системот"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Поврзување со USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Преносл. точка на пристап"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Поврзување со Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index efe6cd6dfbc5..0292cabb73bf 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM ആക്സസ്"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ഓഡിയോ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ഓഡിയോ"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"ശ്രവണ സഹായി"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"ശ്രവണ സഹായിലേക്ക് കണക്റ്റ് ചെയ്തു"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"ശ്രവണ സഹായികൾ"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"ശ്രവണ സഹായികളിലേക്ക് കണക്റ്റ് ചെയ്തു"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"മീഡിയ ഓഡിയോയിലേക്ക് കണക്റ്റുചെയ്തു"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ഫോൺ ഓഡിയോയിൽ കണക്റ്റുചെയ്തു"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ഫയൽ കൈമാറ്റ സെർവറിലേക്ക് കണക്റ്റുചെയ്തു"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ഫോൺ ഓഡിയോയ്ക്കായി ഉപയോഗിക്കുക"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ഫയൽ കൈമാറ്റത്തിനായി ഉപയോഗിക്കുന്നു"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ഇൻപുട്ടിനായി ഉപയോഗിക്കുക"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ശ്രവണ സഹായത്തിനായി ഉപയോഗിക്കുക"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"ശ്രവണ സഹായികൾക്കായി ഉപയോഗിക്കുക"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ജോടിയാക്കുക"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ജോടിയാക്കുക"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"റദ്ദാക്കുക"</string> @@ -136,6 +136,8 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"നീക്കംചെയ്ത അപ്ലിക്കേഷനുകൾ"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"നീക്കംചെയ്ത അപ്ലിക്കേഷനുകളും ഉപയോക്താക്കളും"</string> + <!-- no translation found for data_usage_ota (5377889154805560860) --> + <skip /> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ടെതറിംഗ്"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"പോർട്ടബിൾ ഹോട്ട്സ്പോട്ട്"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ബ്ലൂടൂത്ത് ടെതറിംഗ്"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 9c3db2e99717..10c1415c7630 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -49,8 +49,8 @@ <string name="available_via_carrier" msgid="1469036129740799053">"%1$s-р боломжтой"</string> <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>-г нээж байна"</string> <string name="osu_connect_failed" msgid="2187750899158158934">"Холбогдож чадсангүй"</string> - <string name="osu_completing_sign_up" msgid="9037638564719197082">"Бүртгүүлэлтийг дуусгаж байна…"</string> - <string name="osu_sign_up_failed" msgid="7296159750352873260">"Бүртгүүлэлтийг дуусгаж чадсангүй. Дахин оролдохын тулд товшино уу."</string> + <string name="osu_completing_sign_up" msgid="9037638564719197082">"Бүртгэлийг дуусгаж байна…"</string> + <string name="osu_sign_up_failed" msgid="7296159750352873260">"Бүртгэлийг дуусгаж чадсангүй. Дахин оролдохын тулд товшино уу."</string> <string name="osu_sign_up_complete" msgid="8207626049093289203">"Бүртгүүлж дууслаа. Холбогдож байна…"</string> <string name="speed_label_very_slow" msgid="1867055264243608530">"Маш удаан"</string> <string name="speed_label_slow" msgid="813109590815810235">"Удаан"</string> @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Хандалт"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD аудио: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD аудио"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Сонсголын төхөөрөмж"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Сонсголын төхөөрөмжид холбогдсон"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Сонсголын төхөөрөмж"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Сонсголын төхөөрөмжтэй холбосон"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Медиа аудиод холбогдсон"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Утасны аудид холбогдсон"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Файл дамжуулах серверт холбогдсон"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Утасны аудиод ашиглах"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Файл дамжуулахад ашиглах"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Оруулахад ашиглах"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Сонсголын төхөөрөмжид ашиглах"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Сонсголын төхөөрөмжид ашиглах"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Хослуулах"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ХОСЛУУЛАХ"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Цуцлах"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Андройд OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Арилгасан апп-ууд"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Арилгасан апп-ууд болон хэрэглэгчид"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Системийн шинэчлэлт"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB модем болгох"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Зөөврийн сүлжээний цэг"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth модем болгох"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index a792c4f2de5e..0c2f0eefb0e7 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"सिम प्रवेश"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ऑडिओ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ऑडिओ"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"ऐकण्याची सुविधा"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"ऐकण्याच्या सुविधेशी कनेक्ट केलेले आहे"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"श्रवण यंत्रे"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"श्रवण यंत्रांशी कनेक्ट केले आहे"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"मीडिया ऑडिओवर कनेक्ट केले"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"फोन ऑडिओ वर कनेक्ट केले"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"फाईल स्थानांतर सर्व्हरवर कनेक्ट केले"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"फोन ऑडिओसाठी वापरा"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"फाईल स्थानांतरणासाठी वापरा"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"इनपुट साठी वापरा"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ऐकण्याच्या सुविधेसाठी वापरा"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"श्रवण यंत्रांसाठी वापरा"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"पेअर करा"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"पेअर करा"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"रद्द करा"</string> @@ -136,6 +136,8 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"काढलेले अॅप्स"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"काढलेले अॅप्स आणि वापरकर्ते"</string> + <!-- no translation found for data_usage_ota (5377889154805560860) --> + <skip /> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB टेदरिंग"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"पोर्टेबल हॉटस्पॉट"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ब्लूटूथ टेदरिंग"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 419c6ecd069a..e98418d55fb6 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Akses SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Alat Bantu Pendengaran"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Disambungkan ke Alat Bantu Pendengaran"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Alat Bantu Dengar"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Disambungkan pada Alat Bantu Dengar"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Disambungkan ke audio media"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Disambungkan ke audio telefon"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Bersambung ke pelayan pemindahan fail"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Gunakan untuk audio telefon"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Gunakan untuk pemindahan fail"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Gunakan untuk input"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Gunakan untuk Alat Bantu Pendengaran"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Gunakan untuk Alat Bantu Dengar"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Jadikan pasangan"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"JADIKAN PASANGAN"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Batal"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"OS Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Apl dialih keluar"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Apl dan pengguna yang dialih keluar"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Kemas kini sistem"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Penambatan USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Titik panas mudah alih"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Penambatan Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 3e9e181209b4..ed2aae63f295 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM အသုံးပြုခြင်း"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD အသံ- <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD အသံ"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"နားကြားကိရိယာ"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"နားကြားကိရိယာသို့ ချိတ်ဆက်ထားသည်"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"နားကြားကိရိယာ"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"နားကြားကိရိယာနှင့် ချိတ်ဆက်ပြီးပါပြီ"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"မီဒီယာအသံအား ချိတ်ဆက်ရန်"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ဖုန်းအသံအား ချိတ်ဆက်ရန်"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ဖိုင်လွှဲပြောင်းမည့်ဆာဗာနှင့် ချိတ်ဆက်ထားပြီး"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ဖုန်းအသံအားအသုံးပြုရန်"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ဖိုင်လွဲပြောင်းရန်အတွက်အသုံးပြုရန်"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ထည့်သွင်းရန်အသုံးပြုသည်"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"နားကြားကိရိယာအတွက် အသုံးပြုရန်"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"နားကြားကိရိယာအတွက် အသုံးပြုသည်"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"အတူတွဲပါ"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ချိတ်တွဲရန်"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"မလုပ်တော့"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ဖယ်ရှားထားသော အက်ပ်များ"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ဖယ်ရှားထားသော အပလီကေးရှင်းနှင့် သုံးစွဲသူများ"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"စနစ် အပ်ဒိတ်လုပ်ခြင်းများ"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB သုံး၍ချိတ်ဆက်ခြင်း"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ရွေ့လျားနိုင်သောဟော့စပေါ့"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ဘလူးတုသ်သုံးချိတ်ဆက်ခြင်း"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 65085e8d15d4..93e53e52f968 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Tilgang til SIM-kortet"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-lyd: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD-lyd"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Høreapparat"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Koblet til høreapparat"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Høreapparater"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Koblet til høreapparater"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Koblet til medielyd"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Koblet til telefonlyd"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Koblet til tjener for filoverføring"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Bruk for telefonlyd"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Bruk til filoverføring"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Bruk for inndata"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Bruk for høreapparat"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Bruk for høreapparater"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Sammenkoble"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"KOBLE TIL"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Avbryt"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android-operativsystem"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Fjernede apper"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Fjernede apper og brukere"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Systemoppdateringer"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-internettdeling"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Flyttbar trådløs sone"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-internettdeling"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 16b3b070df7a..7daf9564cc9b 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -38,27 +38,20 @@ <string name="connected_via_network_scorer" msgid="5713793306870815341">"%1$s मार्फत् स्वतः जडान गरिएको"</string> <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"नेटवर्कको दर्जा प्रदायक मार्फत स्वत: जडान गरिएको"</string> <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s मार्फत जडित"</string> - <!-- no translation found for connected_via_app (5571999941988929520) --> - <skip /> + <string name="connected_via_app" msgid="5571999941988929520">"<xliff:g id="NAME">%1$s</xliff:g> मार्फत जडान गरिएको"</string> <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s मार्फत उपलब्ध"</string> - <!-- no translation found for tap_to_sign_up (6449724763052579434) --> - <skip /> + <string name="tap_to_sign_up" msgid="6449724763052579434">"साइन अप गर्न ट्याप गर्नुहोस्"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"जडान गरियो तर इन्टरनेट छैन"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"इन्टरनेटमाथिको पहुँच छैन"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"साइन इन गर्न आवश्यक छ"</string> <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"पहुँचसम्बन्धी स्थान अस्थायी रूपमा भरिएको छ"</string> <string name="connected_via_carrier" msgid="7583780074526041912">"%1$s मार्फत जडान गरियो"</string> <string name="available_via_carrier" msgid="1469036129740799053">"%1$s मार्फत उपलब्ध"</string> - <!-- no translation found for osu_opening_provider (5488997661548640424) --> - <skip /> - <!-- no translation found for osu_connect_failed (2187750899158158934) --> - <skip /> - <!-- no translation found for osu_completing_sign_up (9037638564719197082) --> - <skip /> - <!-- no translation found for osu_sign_up_failed (7296159750352873260) --> - <skip /> - <!-- no translation found for osu_sign_up_complete (8207626049093289203) --> - <skip /> + <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> खोल्दै"</string> + <string name="osu_connect_failed" msgid="2187750899158158934">"जडान गर्न सकिएन"</string> + <string name="osu_completing_sign_up" msgid="9037638564719197082">"साइन अप गर्ने कार्य सम्पन्न गर्दै…"</string> + <string name="osu_sign_up_failed" msgid="7296159750352873260">"साइन अप गर्ने कार्य सम्पन्न गर्न सकिएन। फेरि प्रयास गर्न ट्याप गर्नुहोस्।"</string> + <string name="osu_sign_up_complete" msgid="8207626049093289203">"साइन अप गर्ने कार्य सम्पन्न भयो। जडान गर्दै…"</string> <string name="speed_label_very_slow" msgid="1867055264243608530">"धेरै ढिलो"</string> <string name="speed_label_slow" msgid="813109590815810235">"बिस्तारै"</string> <string name="speed_label_okay" msgid="2331665440671174858">"ठिक छ"</string> @@ -94,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM पहुँच"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD अडियो: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD अडियो"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"सुन्नमा मद्दत गर्ने यन्त्र"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"सुन्नमा मद्दत गर्ने यन्त्रमा जडान गरियो"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"श्रवण यन्त्रहरू"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"श्रवण यन्त्रहरूमा जडान गरियो"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"मिडिया अडियोसँग जडित"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"फोन अडियोमा जडान गरियो"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"फाइल ट्रान्सफर सर्भरमा जडान गरियो"</string> @@ -112,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"फोन अडियोको लागि प्रयोग गर्नुहोस्"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"फाइल ट्रान्सफरका लागि प्रयोग गर्नुहोस्"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"इनपुटको लागि प्रयोग गर्नुहोस्"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"सुन्नमा मद्दत गर्ने यन्त्रका लागि प्रयोग गर्नुहोस्"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"श्रवण यन्त्रहरूका लागि प्रयोग गर्नुहोस्"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"जोडी"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"जोडी"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"रद्द गर्नुहोस्"</string> @@ -143,6 +136,8 @@ <string name="process_kernel_label" msgid="3916858646836739323">"एन्ड्रोइड OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"हटाइएका अनुप्रयोगहरू"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"अनुप्रयोगहरू र प्रयोगकर्ताहरू हटाइयो।"</string> + <!-- no translation found for data_usage_ota (5377889154805560860) --> + <skip /> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB टेथर गर्दै"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"पोर्टेबल हटस्पट"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ब्लुटुथ टेथर गर्दै"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 2f8c2c2c249a..959c1eac1d7d 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Sim-toegang"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD-audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Gehoorapparaat"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Verbonden met gehoorapparaat"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Gehoorapparaten"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Verbonden met gehoorapparaten"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Verbonden met audio van medium"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Verbonden met audio van telefoon"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Verbonden met server voor bestandsoverdracht"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Gebruiken voor audio van telefoon"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Gebruiken voor bestandsoverdracht"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Gebruiken voor invoer"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Gebruiken voor gehoorapparaat"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Gebruiken voor gehoorapparaten"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Koppelen"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"KOPPELEN"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Annuleren"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android-besturingssysteem"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Verwijderde apps"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Verwijderde apps en gebruikers"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Systeemupdates"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-tethering"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Draagbare hotspot"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-tethering"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 95ab7bbab027..54ddf845847e 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -38,27 +38,20 @@ <string name="connected_via_network_scorer" msgid="5713793306870815341">"%1$s ମାଧ୍ୟମରେ ଅଟୋମେଟିକାଲୀ ସଂଯୁକ୍ତ"</string> <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"ନେଟୱର୍କ ମୂଲ୍ୟାୟନ ପ୍ରଦାତାଙ୍କ ମାଧ୍ୟମରେ ଅଟୋମେଟିକାଲ୍ୟ ସଂଯୁକ୍ତ"</string> <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s ମାଧ୍ୟମରେ ସଂଯୁକ୍ତ"</string> - <!-- no translation found for connected_via_app (5571999941988929520) --> - <skip /> + <string name="connected_via_app" msgid="5571999941988929520">"<xliff:g id="NAME">%1$s</xliff:g> ଦ୍ବାରା ସଂଯୋଗ କରାଯାଇଛି"</string> <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ମାଧ୍ୟମରେ ଉପଲବ୍ଧ"</string> - <!-- no translation found for tap_to_sign_up (6449724763052579434) --> - <skip /> + <string name="tap_to_sign_up" msgid="6449724763052579434">"ସାଇନ୍ ଅପ୍ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"ସଂଯୁକ୍ତ, ଇଣ୍ଟର୍ନେଟ୍ ନାହିଁ"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"କୌଣସି ଇଣ୍ଟରନେଟ୍ ନାହିଁ"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"ସାଇନ୍-ଇନ୍ ଆବଶ୍ୟକ"</string> <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"ଆକ୍ସେସ୍ ପଏଣ୍ଟ ସାମୟିକ ଭାବେ ପୂର୍ଣ୍ଣ"</string> <string name="connected_via_carrier" msgid="7583780074526041912">"%1$s ମାଧ୍ୟମରେ ସଂଯୁକ୍ତ"</string> <string name="available_via_carrier" msgid="1469036129740799053">"%1$s ମାଧ୍ୟମରେ ଉପଲବ୍ଧ"</string> - <!-- no translation found for osu_opening_provider (5488997661548640424) --> - <skip /> - <!-- no translation found for osu_connect_failed (2187750899158158934) --> - <skip /> - <!-- no translation found for osu_completing_sign_up (9037638564719197082) --> - <skip /> - <!-- no translation found for osu_sign_up_failed (7296159750352873260) --> - <skip /> - <!-- no translation found for osu_sign_up_complete (8207626049093289203) --> - <skip /> + <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> ଖୋଲୁଛି"</string> + <string name="osu_connect_failed" msgid="2187750899158158934">"ସଂଯୋଗ କରିହେଲା ନାହିଁ"</string> + <string name="osu_completing_sign_up" msgid="9037638564719197082">"ସାଇନ୍ ଅପ୍ ଶେଷ ହେଉଛି…"</string> + <string name="osu_sign_up_failed" msgid="7296159750352873260">"ସାଇନ୍ ଅପ୍ ଶେଷ ହୋଇପାରିଲା ନାହିଁ। ପୁଣି ଥରେ ଚେଷ୍ଟା କରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ।"</string> + <string name="osu_sign_up_complete" msgid="8207626049093289203">"ସାଇନ୍ ଅପ୍ ଶେଷ ହୋଇଛି। ସଂଯୋଗ କରୁଛି…"</string> <string name="speed_label_very_slow" msgid="1867055264243608530">"ବହୁତ ମନ୍ଥର"</string> <string name="speed_label_slow" msgid="813109590815810235">"କମ୍ ବେଗ"</string> <string name="speed_label_okay" msgid="2331665440671174858">"ଠିକ୍ ଅଛି"</string> @@ -94,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"ସିମ୍ ଆକ୍ସେସ୍"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ଅଡିଓ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ଅଡିଓ"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"ଶ୍ରବଣ ଯନ୍ତ୍ର"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"ଶ୍ରବଣ ଯନ୍ତ୍ର ସହିତ ସଂଯୁକ୍ତ ହେଲା"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"ଶ୍ରବଣ ଯନ୍ତ୍ର"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"ଶ୍ରବଣ ଯନ୍ତ୍ରକୁ ସଂଯୋଗ ହୋଇଛି"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"ମିଡିଆ ଅଡିଓ ସହ ସଂଯୁକ୍ତ"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ଫୋନ୍ ଅଡିଓ ସହିତ ସଂଯୁକ୍ତ"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ଫାଇଲ୍ ଟ୍ରାନ୍ସଫର୍ ସର୍ଭର୍ ସହ ସଂଯୁକ୍ତ"</string> @@ -112,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ଫୋନ୍ ଅଡିଓ ପାଇଁ ବ୍ୟବହାର କର"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ଫାଇଲ୍ ଟ୍ରାନ୍ସଫର୍ ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ଇନ୍ପୁଟ୍ ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ଶ୍ରବଣ ଯନ୍ତ୍ର ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"ଶ୍ରବଣ ଯନ୍ତ୍ର ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ପେୟାର୍"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ପେୟାର୍"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"କ୍ୟାନ୍ସଲ୍ କରନ୍ତୁ"</string> @@ -143,6 +136,8 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"କଢ଼ାଯାଇଥିବା ଆପ୍ଗୁଡ଼ିକ"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ଆପ୍ ଏବଂ ଉପଯୋଗକର୍ତ୍ତା ବାହାର କରାଗଲା"</string> + <!-- no translation found for data_usage_ota (5377889154805560860) --> + <skip /> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ଟିଥରିଙ୍ଗ"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ପୋର୍ଟବଲ୍ ହଟସ୍ପଟ୍"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ବ୍ଲୁଟୂଥ ଟିଥରିଙ୍ଗ"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 1ddd862828d2..dcf3e3cb46ec 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -38,27 +38,20 @@ <string name="connected_via_network_scorer" msgid="5713793306870815341">"%1$s ਰਾਹੀਂ ਆਪਣੇ-ਆਪ ਕਨੈਕਟ ਹੋਇਆ"</string> <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"ਨੈੱਟਵਰਕ ਰੇਟਿੰਗ ਪ੍ਰਦਾਨਕ ਰਾਹੀਂ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਕਨੈਕਟ ਹੋਇਆ"</string> <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ"</string> - <!-- no translation found for connected_via_app (5571999941988929520) --> - <skip /> + <string name="connected_via_app" msgid="5571999941988929520">"<xliff:g id="NAME">%1$s</xliff:g> ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string> <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ਰਾਹੀਂ ਉਪਲਬਧ"</string> - <!-- no translation found for tap_to_sign_up (6449724763052579434) --> - <skip /> + <string name="tap_to_sign_up" msgid="6449724763052579434">"ਸਾਈਨ-ਅੱਪ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"ਕਨੈਕਟ ਕੀਤਾ, ਕੋਈ ਇੰਟਰਨੈੱਟ ਨਹੀਂ"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"ਇੰਟਰਨੈੱਟ ਨਹੀਂ"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"ਸਾਈਨ-ਇਨ ਲੋੜੀਂਦਾ ਹੈ"</string> <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"ਐਕਸੈੱਸ ਪੁਆਇੰਟ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਸੰਪੂਰਨ ਰੁਝੇਂਵੇਂ ਵਿੱਚ ਹੈ"</string> <string name="connected_via_carrier" msgid="7583780074526041912">"%1$s ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ"</string> <string name="available_via_carrier" msgid="1469036129740799053">"%1$s ਰਾਹੀਂ ਉਪਲਬਧ"</string> - <!-- no translation found for osu_opening_provider (5488997661548640424) --> - <skip /> - <!-- no translation found for osu_connect_failed (2187750899158158934) --> - <skip /> - <!-- no translation found for osu_completing_sign_up (9037638564719197082) --> - <skip /> - <!-- no translation found for osu_sign_up_failed (7296159750352873260) --> - <skip /> - <!-- no translation found for osu_sign_up_complete (8207626049093289203) --> - <skip /> + <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> ਖੋਲ੍ਹਿਆ ਜਾ ਰਿਹਾ ਹੈ"</string> + <string name="osu_connect_failed" msgid="2187750899158158934">"ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string> + <string name="osu_completing_sign_up" msgid="9037638564719197082">"ਸਾਈਨ-ਅੱਪ ਮੁਕੰਮਲ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string> + <string name="osu_sign_up_failed" msgid="7296159750352873260">"ਸਾਈਨ-ਅੱਪ ਮੁਕੰਮਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string> + <string name="osu_sign_up_complete" msgid="8207626049093289203">"ਸਾਈਨ-ਅੱਪ ਮੁਕੰਮਲ ਹੋਇਆ ਕਨੈਕਟ ਹੋ ਰਿਹਾ ਹੈ…"</string> <string name="speed_label_very_slow" msgid="1867055264243608530">"ਬਹੁਤ ਹੌਲੀ"</string> <string name="speed_label_slow" msgid="813109590815810235">"ਹੌਲੀ"</string> <string name="speed_label_okay" msgid="2331665440671174858">"ਠੀਕ ਹੈ"</string> @@ -94,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"ਸਿਮ ਪਹੁੰਚ"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ਆਡੀਓ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ਆਡੀਓ"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"ਸੁਣਨ ਦਾ ਸਾਧਨ"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"ਸੁਣਨ ਦੇ ਸਾਧਨ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"ਸੁਣਨ ਦੇ ਸਾਧਨ"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"ਸੁਣਨ ਦੇ ਸਾਧਨਾਂ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"ਮੀਡੀਆ ਆਡੀਓ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ਫ਼ੋਨ ਔਡੀਓ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ਫਾਈਲ ਟ੍ਰਾਂਸਫ਼ਰ ਸਰਵਰ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string> @@ -112,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ਫ਼ੋਨ ਔਡੀਓ ਲਈ ਵਰਤੋ"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ਫਾਈਲ ਟ੍ਰਾਂਸਫਰ ਲਈ ਵਰਤੋ"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ਇਨਪੁਟ ਲਈ ਵਰਤੋ"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ਸੁਣਨ ਦੇ ਸਾਧਨ ਲਈ ਵਰਤੋ"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"ਸੁਣਨ ਦੇ ਸਾਧਨਾਂ ਲਈ ਵਰਤੋ"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ਪੇਅਰ ਕਰੋ"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ਪੇਅਰ ਕਰੋ"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ਰੱਦ ਕਰੋ"</string> @@ -143,6 +136,8 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ਹਟਾਏ ਗਏ ਐਪਸ"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ਹਟਾਏ ਗਏ ਐਪਸ ਅਤੇ ਉਪਭੋਗਤਾ"</string> + <!-- no translation found for data_usage_ota (5377889154805560860) --> + <skip /> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ਟੈਦਰਿੰਗ"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ਪੋਰਟੇਬਲ ਹੌਟਸਪੌਟ"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ਬਲੂਟੁੱਥ ਟੈਦਰਿੰਗ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index e2109c8c35b0..505e3959244d 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Dostęp do karty SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Dźwięk HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Dźwięk HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Aparat słuchowy"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Po połączeniu z aparatem słuchowym"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Aparaty słuchowe"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Połączono z aparatami słuchowymi"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Połączono z funkcją audio multimediów"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Połączono z funkcją audio telefonu"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Połączono z serwerem transferu plików"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Użyj dla funkcji audio telefonu"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Użyj do transferu plików"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Użyj do wprowadzania"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Użyj dla funkcji aparatu słuchowego"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Użyj z aparatami słuchowymi"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Sparuj"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SPARUJ"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Anuluj"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"System operacyjny Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Usunięte aplikacje"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Usunięte aplikacje i użytkownicy"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Aktualizacje systemu"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Tethering przez USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Przenośny punkt dostępu"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Tethering przez Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 19e22e420026..c0f31a6a5f73 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acesso ao chip"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Áudio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Áudio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Aparelho auditivo"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Conectado a um aparelho auditivo"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Aparelhos auditivos"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Conectado a aparelhos auditivos"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Conectado ao áudio da mídia"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Conectado ao áudio do smartphone"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Conectado ao servidor de transferência de arquivo"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Usar para áudio do smartphone"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Usado para transferência de arquivo"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Usar para entrada"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Usar para aparelho auditivo"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Usar para aparelhos auditivos"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Parear"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAREAR"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancelar"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Sistema operacional Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Apps removidos"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Apps e usuários removidos"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Atualizações do sistema"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Tethering USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Ponto de acesso portátil"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Tethering Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 1f1072c08ffa..08c3cc82c259 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acesso ao SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Áudio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Áudio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Aparelho auditivo"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Associar ao aparelho auditivo"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Aparelhos auditivos"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Ligado a aparelhos auditivos"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Ligado ao áudio de multimédia"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Ligado ao áudio do telefone"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Ligado ao servidor de transferência de ficheiros"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utilizar para áudio do telefone"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utilizar para transferência de ficheiros"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utilizar para entrada"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Utilizar com o aparelho auditivo"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Utilizar para aparelhos auditivos"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Sincr."</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SINCRONIZAR"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancelar"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"SO Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicações removidas"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplicações e utilizadores removidos"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Atualizações do sistema"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Ligação USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot portátil"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Ligação Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 19e22e420026..c0f31a6a5f73 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acesso ao chip"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Áudio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Áudio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Aparelho auditivo"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Conectado a um aparelho auditivo"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Aparelhos auditivos"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Conectado a aparelhos auditivos"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Conectado ao áudio da mídia"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Conectado ao áudio do smartphone"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Conectado ao servidor de transferência de arquivo"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Usar para áudio do smartphone"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Usado para transferência de arquivo"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Usar para entrada"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Usar para aparelho auditivo"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Usar para aparelhos auditivos"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Parear"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PAREAR"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancelar"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Sistema operacional Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Apps removidos"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Apps e usuários removidos"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Atualizações do sistema"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Tethering USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Ponto de acesso portátil"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Tethering Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 7fa75a8862cf..015eb9240fc5 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acces la SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Aparat auditiv"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Conectat la aparatul auditiv"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Aparate auditive"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Conectat la aparatul auditiv"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Conectat la profilul pentru conținut media audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Conectat la componenta audio a telefonului"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Conectat la serverul de transfer de fișiere"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utilizați pentru componenta audio a telefonului"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utilizați pentru transferul de fișiere"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utilizați pentru introducere date"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Utilizați pentru aparatul auditiv"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Folosiți pentru aparatele auditive"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Asociați"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"CONECTAȚI"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Anulați"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Sistem de operare Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicații eliminate"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplicații și utilizatori eliminați"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Actualizări de sistem"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Tethering prin USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot portabil"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Tethering prin Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 6cdfd3c3f318..b0b592caf6c7 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Доступ к SIM-карте"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD Audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD Audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Слуховой аппарат"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Подключен к слуховому аппарату"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Слуховые аппараты"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Слуховой аппарат подключен"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Подключено к мультимедийному аудиоустройству"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Подключено к аудиоустройству телефона"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Установлено подключение к серверу передачи файлов"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Использовать для аудиоустройства телефона"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Используется для передачи файлов"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Использовать для ввода"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Использовать для слухового аппарата"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Использовать для слухового аппарата"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Добавить"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ДОБАВИТЬ"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Отмена"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"ОС Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Удаленные приложения"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Удаленные приложения и пользователи"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Обновления системы"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-модем"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Точка доступа Wi-Fi"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-модем"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index f85289c64527..ca7a96227d99 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM ප්රවේශය"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ශ්රව්යය: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ශ්රව්යය"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"ශ්රවණාධාරකය"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"ශ්රවණාධාරකයට සම්බන්ධයි"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"ශ්රවණාධාරක"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"ශ්රවණාධාරක වෙත සම්බන්ධ කළා"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"මාධ්ය ශ්රව්යට සම්බන්ධ විය"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"දුරකතනයේ ශ්රව්යට සම්බන්ධ විය"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ගොනු හුවමාරු සේවාදායකය සමග සම්බන්ධ විය"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"දුරකථන ශ්රව්ය සඳහා භාවිතා කෙරේ"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ගොනු හුවමාරුව සඳහා භාවිතා කරන්න"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ආදානය සඳහා භාවිතා කරන්න"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ශ්රවණාධාරකය සඳහා භාවිත කරන්න"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"ශ්රවණාධාර සඳහා භාවිත කරන්න"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"යුගල කරන්න"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"යුගල කරන්න"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"අවලංගු කරන්න"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ඉවත් කළ යෙදුම්"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"යෙදුම් සහ පරිශීලකයින් ඉවත් කරන ලදි"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"පද්ධති යාවත්කාලීන"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ටෙදරින්"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ජංගම හොට්ස්පොට්"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"බ්ලූටූත් ටෙදරින්"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index eae5be7952c3..dd7efdd50a59 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Prístup k SIM karte"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD zvuk: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD zvuk"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Načúvacia pomôcka"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Pripojené k načúvacej pomôcke"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Načúvacie pomôcky"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Pripojené k načúvacím pomôckam"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Pripojené ku zvukovému médiu"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Pripojené ku zvuku telefónu"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Pripojené na server pre prenos údajov"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Použiť pre zvuk telefónu"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Použiť na prenos súborov"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Použiť pre vstup"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Použitie načúvacej pomôcky"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Použiť pre načúvacie pomôcky"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Párovať"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PÁROVAŤ"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Zrušiť"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"OS Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Odstránené aplikácie"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Odstránené aplikácie a používatelia"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Aktualizácie systému"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Pripojenie cez USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Prenosný prístupový bod"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Pripojenie cez Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index bef9a3e5aadd..fada686f00d1 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Dostop do kartice SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Zvok visoke kakovosti: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Zvok visoke kakovosti"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Slušni pripomoček"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Povezava s slušnim pripomočkom je vzpostavljena"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Slušni pripomočki"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Povezava s slušnimi pripomočki je vzpostavljena"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Povezan s profilom za predstavnostni zvok"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Povezava s profilom za zvok telefona vzpostavljena"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Povezava s strežnikom za prenos datotek je vzpostavljena"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Uporabi za zvok telefona"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Uporabi za prenos datotek"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Uporabi za vnos"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Uporaba za slušni pripomoček"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Uporabi za slušne pripomočke"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Seznani"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SEZNANI"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Prekliči"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"OS Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Odstranjene aplikacije"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Odstranjene aplikacije in uporabniki"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Posodobitve sistema"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Internet prek USB-ja"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Prenosna dostopna točka"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Internet prek Bluetootha"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 8bb67f9a348f..2132767193c5 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Qasje në kartën SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Audio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Aparati i dëgjimit"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Lidhur me aparatin e dëgjimit"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Aparatet e dëgjimit"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Lidhur me aparatet e dëgjimit"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"U lidh me audion e medias"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"U lidh me audion e telefonit"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"U lidh me serverin e transferimit të skedarëve"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Përdor për audion e telefonit"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Përdor për transferimin e skedarëve"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Përdore për hyrjen"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Përdore për aparatin e dëgjimit"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Përdore për aparatet e dëgjimit"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Çifto"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ÇIFTO"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Anulo"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Sistemi operativ Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplikacionet e hequra"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplikacionet dhe përdoruesit e hequr"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Përditësimet e sistemit"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Ndarje përmes USB-së"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Zona e qasjes e lëvizshme"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Ndarje interneti përmes Bluetooth-it"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 4378a463c839..6cf4b208c544 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Приступ SIM картици"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD звук: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD звук"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Слушни апарат"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Повезано са слушним апаратом"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Слушни апарати"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Повезано са слушним апаратима"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Повезано са звуком медија"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Повезано са звуком телефона"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Повезано са сервером за пренос датотека"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Коришћење за аудио телефона"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Коришћење за пренос датотека"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Користи за улаз"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Користи за слушни апарат"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Користи за слушне апарате"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Упари"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"УПАРИ"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Откажи"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android ОС"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Уклоњене апликације"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Уклоњене апликације и корисници"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Ажурирања система"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB Интернет повезивање"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Преносни хотспот"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth привезивање"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 3fcb6dc80713..f2950dcf2808 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-åtkomst"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-ljud: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD-ljud"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Hörapparat"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Ansluten till hörapparat"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Hörapparater"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Ansluten till hörapparater"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Ansluten till medialjud"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Ansluten till telefonens ljud"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Ansluten till filöverföringsserver"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Använd för telefonens ljud"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Använd för filöverföring"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Använd för inmatning"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Använd med hörapparat"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Använd med hörapparater"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Parkoppling"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PARKOPPLA"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Avbryt"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Operativsystemet Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Borttagna appar"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Borttagna appar och användare"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Systemuppdateringar"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Internetdelning via USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Mobil surfzon"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Delning via Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 47c7bb2187ef..d8a74a14c40f 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Ufikiaji wa SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Sauti ya HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Sauti ya HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Visaidizi vya Kusikia"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Imeunganishwa kwenye Visaidizi vya Kusikia"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Vifaa vya Kusaidia Kusikia"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Imeunganishwa kwenye Vifaa vya Kusaidia Kusikia"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Imeunganishwa kwenye sikika ya njia ya mawasiliano"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Imeunganishwa kwenye sauti ya simu"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Imeunganishwa kwenye seva ya kuhamisha faili"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Tumia kwa sauti ya simu"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Tumia kwa hali faili"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Tumia kwa kuingiza"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Tumia katika Visaidizi vya Kusikia"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Tumia kwenye Vifaa vya Kusaidia Kusikia"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Oanisha"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"OANISHA"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Ghairi"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"OS ya Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Programu zilizoondolewa"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Watumiaji na programu ziilizoondolewa"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Masasisho ya mfumo"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Shiriki intaneti kwa USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Intaneti ya kusambazwa"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Shiriki intaneti kwa Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 63c6cd9b11f2..71716cec6886 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -38,27 +38,20 @@ <string name="connected_via_network_scorer" msgid="5713793306870815341">"%1$s மூலம் தானாக இணைக்கப்பட்டது"</string> <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"நெட்வொர்க் மதிப்பீடு வழங்குநரால் தானாக இணைக்கப்பட்டது"</string> <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s வழியாக இணைக்கப்பட்டது"</string> - <!-- no translation found for connected_via_app (5571999941988929520) --> - <skip /> + <string name="connected_via_app" msgid="5571999941988929520">"<xliff:g id="NAME">%1$s</xliff:g> மூலம் இணைக்கப்பட்டது"</string> <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s வழியாகக் கிடைக்கிறது"</string> - <!-- no translation found for tap_to_sign_up (6449724763052579434) --> - <skip /> + <string name="tap_to_sign_up" msgid="6449724763052579434">"பதிவு செய்யத் தட்டவும்"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"இணைக்கப்பட்டுள்ளது, ஆனால் இண்டர்நெட் இல்லை"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"இணைய இணைப்பு இல்லை"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"உள்நுழைய வேண்டும்"</string> <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"தற்காலிகமாக அணுகல் புள்ளி நிரம்பியுள்ளது"</string> <string name="connected_via_carrier" msgid="7583780074526041912">"%1$s வழியாக இணைக்கப்பட்டது"</string> <string name="available_via_carrier" msgid="1469036129740799053">"%1$s வழியாகக் கிடைக்கிறது"</string> - <!-- no translation found for osu_opening_provider (5488997661548640424) --> - <skip /> - <!-- no translation found for osu_connect_failed (2187750899158158934) --> - <skip /> - <!-- no translation found for osu_completing_sign_up (9037638564719197082) --> - <skip /> - <!-- no translation found for osu_sign_up_failed (7296159750352873260) --> - <skip /> - <!-- no translation found for osu_sign_up_complete (8207626049093289203) --> - <skip /> + <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> திறக்கப்படுகிறது"</string> + <string name="osu_connect_failed" msgid="2187750899158158934">"இணைக்க முடியவில்லை"</string> + <string name="osu_completing_sign_up" msgid="9037638564719197082">"பதிவு செய்வது நிறைவடைகிறது…"</string> + <string name="osu_sign_up_failed" msgid="7296159750352873260">"பதிவு செய்வதை நிறைவுசெய்ய இயலவில்லை மீண்டும் முயற்சிக்கத் தட்டவும்."</string> + <string name="osu_sign_up_complete" msgid="8207626049093289203">"பதிவு செய்வது நிறைவடைந்தது. இணைக்கிறது…"</string> <string name="speed_label_very_slow" msgid="1867055264243608530">"மிகவும் வேகம் குறைவானது"</string> <string name="speed_label_slow" msgid="813109590815810235">"வேகம் குறைவு"</string> <string name="speed_label_okay" msgid="2331665440671174858">"சரி"</string> @@ -94,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"சிம் அணுகல்"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ஆடியோ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ஆடியோ"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"செவித்துணைக் கருவி"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"செவித்துணைக் கருவியுடன் இணைக்கப்பட்டிருக்கிறது"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"செவித்துணை கருவிகள்"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"செவித்துணை கருவிகளுடன் இணைக்கப்பட்டது"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"மீடியா ஆடியோவுடன் இணைக்கப்பட்டது"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"மொபைல் ஆடியோவுடன் இணைக்கப்பட்டது"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"கோப்பைப் பரிமாற்றும் சேவையகத்துடன் இணைக்கப்பட்டது"</string> @@ -112,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"மொபைல் ஆடியோவைப் பயன்படுத்து"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"கோப்பு பரிமாற்றத்திற்காகப் பயன்படுத்து"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"உள்ளீட்டுக்குப் பயன்படுத்து"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"செவித்துணைக் கருவிக்காகப் பயன்படுத்து"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"செவித்துணை கருவிகளுக்குப் பயன்படுத்தவும்"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"இணை"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"இணை"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ரத்துசெய்"</string> @@ -143,6 +136,8 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"அகற்றப்பட்ட பயன்பாடுகள்"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"அகற்றப்பட்ட பயன்பாடுகள் மற்றும் பயனர்கள்"</string> + <!-- no translation found for data_usage_ota (5377889154805560860) --> + <skip /> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB டெதெரிங்"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"போர்ட்டபிள் ஹாட்ஸ்பாட்"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"புளூடூத் டெதெரிங்"</string> @@ -203,7 +198,7 @@ <string name="vpn_settings_not_available" msgid="956841430176985598">"இவரால் VPN அமைப்புகளை மாற்ற முடியாது"</string> <string name="tethering_settings_not_available" msgid="6765770438438291012">"இவரால் இணைப்புமுறை அமைப்புகளை மாற்ற முடியாது"</string> <string name="apn_settings_not_available" msgid="7873729032165324000">"இவரால் ஆக்சஸ் பாயிண்ட் நேம் அமைப்புகளை மாற்ற முடியாது"</string> - <string name="enable_adb" msgid="7982306934419797485">"USB பிழைத்திருத்தம்"</string> + <string name="enable_adb" msgid="7982306934419797485">"USB பிழைதிருத்தம்"</string> <string name="enable_adb_summary" msgid="4881186971746056635">"USB இணைக்கப்பட்டிருக்கும்போது பிழைத்திருத்தப் பயன்முறையை அமை"</string> <string name="clear_adb_keys" msgid="4038889221503122743">"USB பிழைத்திருத்த அங்கீகரிப்புகளை நிராகரி"</string> <string name="bugreport_in_power" msgid="7923901846375587241">"பிழைப் புகாருக்கான ஷார்ட்கட்"</string> @@ -263,8 +258,8 @@ <string name="debug_view_attributes" msgid="6485448367803310384">"காட்சி பண்புக்கூறு சோதனையை இயக்கு"</string> <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"வைஃபை இயங்கும் போதும் (வேகமான நெட்வொர்க் மாற்றத்திற்கு), மொபைல் டேட்டாவை எப்போதும் இயக்கத்தில் வைக்கும்."</string> <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"வன்பொருள் விரைவுப்படுத்துதல் இணைப்பு முறை கிடைக்கும் போது, அதைப் பயன்படுத்தும்"</string> - <string name="adb_warning_title" msgid="6234463310896563253">"USB பிழைத்திருத்தத்தை அனுமதிக்கவா?"</string> - <string name="adb_warning_message" msgid="7316799925425402244">"USB பிழைத்திருத்தம் மேம்படுத்தல் நோக்கங்களுக்காக மட்டுமே. அதை உங்கள் கணினி மற்றும் சாதனத்திற்கு இடையில் தரவை நகலெடுக்கவும், அறிவிப்பு இல்லாமல் உங்கள் சாதனத்தில் பயன்பாடுகளை நிறுவவும், பதிவு தரவைப் படிக்கவும் பயன்படுத்தவும்."</string> + <string name="adb_warning_title" msgid="6234463310896563253">"USB பிழைதிருத்தத்தை அனுமதிக்கவா?"</string> + <string name="adb_warning_message" msgid="7316799925425402244">"USB பிழைதிருத்தம் மேம்படுத்தல் நோக்கங்களுக்காக மட்டுமே. அதை உங்கள் கணினி மற்றும் சாதனத்திற்கு இடையில் தரவை நகலெடுக்கவும், அறிவிப்பு இல்லாமல் உங்கள் சாதனத்தில் பயன்பாடுகளை நிறுவவும், பதிவு தரவைப் படிக்கவும் பயன்படுத்தவும்."</string> <string name="adb_keys_warning_message" msgid="5659849457135841625">"நீங்கள் ஏற்கனவே அனுமதித்த எல்லா கணினிகளிலிருந்தும் USB பிழைத்திருத்தத்திற்கான அணுகலைத் திரும்பப்பெற வேண்டுமா?"</string> <string name="dev_settings_warning_title" msgid="7244607768088540165">"மேம்பட்ட அமைப்புகளை அனுமதிக்கவா?"</string> <string name="dev_settings_warning_message" msgid="2298337781139097964">"இந்த அமைப்பு மேம்பட்டப் பயன்பாட்டிற்காக மட்டுமே. உங்கள் சாதனம் மற்றும் அதில் உள்ள பயன்பாடுகளைச் சிதைக்கும் அல்லது தவறாகச் செயல்படும் வகையில் பாதிப்பை ஏற்படுத்தும்."</string> @@ -276,10 +271,10 @@ <string name="enable_terminal_summary" msgid="67667852659359206">"அக ஷெல் அணுகலை வழங்கும் இறுதிப் பயன்பாட்டை இயக்கு"</string> <string name="hdcp_checking_title" msgid="8605478913544273282">"HDCP சரிபார்ப்பு"</string> <string name="hdcp_checking_dialog_title" msgid="5141305530923283">"HDCP சரிபார்க்கும் செயல்பாடுகளை அமை"</string> - <string name="debug_debugging_category" msgid="6781250159513471316">"பிழைத்திருத்தம்"</string> + <string name="debug_debugging_category" msgid="6781250159513471316">"பிழைதிருத்தம்"</string> <string name="debug_app" msgid="8349591734751384446">"பிழைத்திருத்தப் பயன்பாட்டைத் தேர்ந்தெடுக்கவும்"</string> <string name="debug_app_not_set" msgid="718752499586403499">"பிழைத்திருத்தப் பயன்பாடு அமைக்கப்படவில்லை"</string> - <string name="debug_app_set" msgid="2063077997870280017">"பிழைத்திருத்தும் பயன்பாடு: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="debug_app_set" msgid="2063077997870280017">"பிழைதிருத்தும் பயன்பாடு: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="select_application" msgid="5156029161289091703">"பயன்பாட்டைத் தேர்ந்தெடுக்கவும்"</string> <string name="no_application" msgid="2813387563129153880">"ஒன்றுமில்லை"</string> <string name="wait_for_debugger" msgid="1202370874528893091">"பிழைதிருத்திக்குக் காத்திருக்கவும்"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index d8ec167c8e50..e4dd85f8d248 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM యాక్సెస్"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ఆడియో: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ఆడియో"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"వినికిడి పరికరం"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"వినికిడి పరికరానికి కనెక్ట్ చేస్తోంది"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"వినికిడి మద్దతు ఉపకరణాలు"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"వినికిడి మద్దతు ఉపకరణాలకు కనెక్ట్ చేయబడింది"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"మీడియా ఆడియోకు కనెక్ట్ చేయబడింది"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ఫోన్ ఆడియోకు కనెక్ట్ చేయబడింది"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ఫైల్ బదిలీ సర్వర్కు కనెక్ట్ చేయబడింది"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ఫోన్ ఆడియో కోసం ఉపయోగించు"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ఫైల్ బదిలీ కోసం ఉపయోగించు"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ఇన్పుట్ కోసం ఉపయోగించు"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"వినికిడి పరికరం కోసం ఉపయోగించు"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"వినికిడి మద్దతు ఉపకరణాలకు ఉపయోగించండి"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"జత చేయి"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"జత చేయి"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"రద్దు చేయి"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"తీసివేయబడిన అనువర్తనాలు"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"తీసివేయబడిన అనువర్తనాలు మరియు వినియోగదారులు"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"సిస్టమ్ అప్డేట్లు"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB టీథరింగ్"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"పోర్టబుల్ హాట్స్పాట్"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"బ్లూటూత్ టీథరింగ్"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 1f4c727dd227..cf8362fe4d37 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"การเข้าถึงซิม"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"เสียง HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"เสียง HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"เครื่องช่วยการได้ยิน"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"เชื่อมต่อเครื่องช่วยการได้ยินแล้ว"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"เครื่องช่วยการได้ยิน"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"เชื่อมต่อกับเครื่องช่วยการได้ยินแล้ว"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"เชื่อมต่อกับระบบเสียงของสื่อแล้ว"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"เชื่อมต่อกับระบบเสียงของโทรศัพท์แล้ว"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"เชื่อมต่อกับเซิร์ฟเวอร์สำหรับโอนไฟล์แล้ว"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"ใช้สำหรับระบบเสียงของโทรศัพท์"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"ใช้สำหรับการโอนไฟล์"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ใช้สำหรับการป้อนข้อมูล"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"ใช้สำหรับเครื่องช่วยการได้ยิน"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"ใช้สำหรับเครื่องช่วยการได้ยิน"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"จับคู่อุปกรณ์"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"จับคู่อุปกรณ์"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ยกเลิก"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"ระบบปฏิบัติการของ Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"แอปพลิเคชันที่นำออก"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"แอปพลิเคชันและผู้ใช้ที่นำออก"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"การอัปเดตระบบ"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"ปล่อยสัญญาณผ่าน USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ฮอตสปอตแบบพกพาได้"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ปล่อยสัญญาณบลูทูธ"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 7a31cc9ee3b1..e690800eb9a7 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Access sa SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Hearing Aid"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Nakakonekta sa Hearing Aid"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Mga Hearing Aid"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Nakakonekta sa Mga Hearing Aid"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Konektado sa media audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Nakakonekta sa audio ng telepono"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Nakakonekta sa server sa paglilipat ng file"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Ginagamit para sa audio ng telepono"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Ginagamit para sa paglilipat ng file"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Gamitin para sa input"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Gamitin para sa Hearing Aid"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Gamitin para sa Mga Hearing Aid"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Pares"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"IPARES"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Kanselahin"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Mga inalis na app"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Mga inalis na apps at user"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Mga pag-update ng system"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Pag-tether sa USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Portable na hotspot"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Pag-tether ng Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 28fc38fcd8a5..ea340e8e92e6 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM Erişimi"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ses: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ses"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"İşitme Cihazı"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"İşitme Cihazına bağlandı"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"İşitme Cihazları"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"İşitme Cihazlarına Bağlandı"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Medya sesine bağlanıldı"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Telefon sesine bağlandı"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Dosya aktarım sunucusuna bağlandı"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Telefon sesi için kullan"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Dosya aktarımı için kullan"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Giriş için kullan"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"İşitme Cihazı için kullan"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"İşitme Cihazları için kullan"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Eşle"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"EŞLE"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"İptal"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Kaldırılan uygulamalar"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Kaldırılmış kullanıcılar ve uygulamalar"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Sistem güncellemeleri"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB tethering"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Taşınabilir hotspot"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth tethering"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index c63f7e0e0761..cf90ed16efaf 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Доступ до SIM-карти"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD-аудіо: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD-аудіо"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Слуховий апарат"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Під’єднано до слухового апарата"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Слухові апарати"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Підключено до слухових апаратів"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Підключено до аудіоджерела"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Підключено до звуку телеф."</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Підключ. до сервера передачі файлів"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Викор. для звуку тел."</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Викор. для перед. файлів"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Викор. для введ."</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Використовувати для слухового апарата"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Використовувати для слухових апаратів"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Підключити"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ПІДКЛЮЧИТИСЯ"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Скасувати"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"ОС Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Видалені програми"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Видалені програми та користувачі"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Оновлення системи"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB-модем"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Порт. точка дост."</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth-модем"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 27aa730b6a8b..65ed110d3c46 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -38,27 +38,20 @@ <string name="connected_via_network_scorer" msgid="5713793306870815341">"%1$s کے ذریعے از خود منسلک کردہ"</string> <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"نیٹ ورک درجہ بندی کے فراہم کنندہ کے ذریعے از خود منسلک"</string> <string name="connected_via_passpoint" msgid="2826205693803088747">"منسلک بذریعہ %1$s"</string> - <!-- no translation found for connected_via_app (5571999941988929520) --> - <skip /> + <string name="connected_via_app" msgid="5571999941988929520">"<xliff:g id="NAME">%1$s</xliff:g> کے ذریعے منسلک"</string> <string name="available_via_passpoint" msgid="1617440946846329613">"دستیاب بذریعہ %1$s"</string> - <!-- no translation found for tap_to_sign_up (6449724763052579434) --> - <skip /> + <string name="tap_to_sign_up" msgid="6449724763052579434">"سائن اپ کے لیے تھپتھپائیں"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"منسلک، انٹرنیٹ نہیں ہے"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"انٹرنیٹ نہیں ہے"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"سائن ان درکار ہے"</string> <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"رسائی پوائنٹ عارضی طور پر فُل ہے"</string> <string name="connected_via_carrier" msgid="7583780074526041912">"منسلک بذریعہ %1$s"</string> <string name="available_via_carrier" msgid="1469036129740799053">"دستیاب بذریعہ %1$s"</string> - <!-- no translation found for osu_opening_provider (5488997661548640424) --> - <skip /> - <!-- no translation found for osu_connect_failed (2187750899158158934) --> - <skip /> - <!-- no translation found for osu_completing_sign_up (9037638564719197082) --> - <skip /> - <!-- no translation found for osu_sign_up_failed (7296159750352873260) --> - <skip /> - <!-- no translation found for osu_sign_up_complete (8207626049093289203) --> - <skip /> + <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> کھل رہا ہے"</string> + <string name="osu_connect_failed" msgid="2187750899158158934">"منسلک نہیں کیا جا سکا"</string> + <string name="osu_completing_sign_up" msgid="9037638564719197082">"سائن اپ مکمل ہو رہا ہے…"</string> + <string name="osu_sign_up_failed" msgid="7296159750352873260">"سائن اپ مکمل نہیں ہو سکا۔ دوبارہ کوشش کرنے کے لیے تھپتھپائیں۔"</string> + <string name="osu_sign_up_complete" msgid="8207626049093289203">"سائن اپ مکمل ہو گیا۔ منسلک ہو رہا ہے…"</string> <string name="speed_label_very_slow" msgid="1867055264243608530">"بہت سست"</string> <string name="speed_label_slow" msgid="813109590815810235">"سست"</string> <string name="speed_label_okay" msgid="2331665440671174858">"ٹھیک ہے"</string> @@ -94,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM رسائی"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD آڈیو: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD آڈیو"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"سماعتی آلہ"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"سماعتی آلے سے منسلک"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"سماعتی آلات"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"سماعتی آلات سے منسلک ہے"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"میڈیا آڈیو سے مربوط"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"فون آڈیو سے مربوط"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"فائل منتقلی سرور سے مربوط ہو گیا ہے"</string> @@ -112,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"فون آڈیو کیلئے استعمال کریں"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"فائل منتقل کرنے کیلئے استعمال کریں"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ان پٹ کیلئے استعمال"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"سماعتی آلے کیلئے استعمال کریں"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"سماعتی آلات کے لیے استعمال کریں"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"جوڑا بنائیں"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"جوڑا بنائیں"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"منسوخ کریں"</string> @@ -143,6 +136,8 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ہٹائی گئی ایپس"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ہٹائی گئی ایپس اور صارفین"</string> + <!-- no translation found for data_usage_ota (5377889154805560860) --> + <skip /> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ٹیدرنگ"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"پورٹیبل ہاٹ اسپاٹ"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"بلوٹوتھ ٹیدرنگ"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index bcb4d27d94d4..2851bafd4639 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM-kartaga kirish"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Eshitish apparati"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Eshitish apparatiga ulangan"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Eshitish apparatlari"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Eshitish apparatlariga ulangan"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Audio qurilmasiga ulangan"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Telefon karnayiga ulanildi"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Fayl almashinish serveriga ulanildi"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Dok’dan karnay sifatida foydalanish"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Fayl almashinish uchun foydalanish"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Kiritish qurilmasi sifatida foydalanish"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Eshitish apparati uchun foydalanish"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Eshitish apparatlari uchun foydalanish"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Biriktirish"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ULANISH"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Bekor qilish"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"O‘chirilgan ilovalar"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"O‘chirib tashlangan ilova va foydalanuvchilar"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Tizimni yangilash"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB modem"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Ixcham hotspot"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth modem"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index d03b2f175f60..3fb101e6deec 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Quyền truy cập SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Âm thanh HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Âm thanh HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Trợ thính"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Đã kết nối với thiết bị trợ thính"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Thiết bị trợ thính"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Đã kết nối với Thiết bị trợ thính"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Đã kết nối với âm thanh phương tiện"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Đã kết nối với âm thanh điện thoại"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Đã kết nối với máy chủ chuyển tệp"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Sử dụng cho âm thanh điện thoại"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Sử dụng để chuyển tệp"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Sử dụng để nhập"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Sử dụng cho thiết bị trợ thính"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Dùng cho Thiết bị trợ thính"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Ghép nối"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"GHÉP NỐI"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Hủy"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Hệ điều hành Android"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Ứng dụng đã xóa"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Ứng dụng và người dùng bị xóa"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Bản cập nhật hệ thống"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Chia sẻ kết nối Internet qua USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Điểm phát sóng di động"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Chia sẻ kết nối Internet qua Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 69674472028c..0a318d752ae7 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM 卡存取权限"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD 音频:<xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD 音频"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"助听器"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"已连接到助听器"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"助听器"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"已连接到助听器"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"已连接到媒体音频"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"已连接到手机音频"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"已连接到文件传输服务器"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"用于手机音频"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"用于文件传输"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"用于输入"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"用于助听器"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"用于助听器"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"配对"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"配对"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"取消"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android 操作系统"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"已删除的应用"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"已删除的应用和用户"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"系统更新"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB 网络共享"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"便携式热点"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"蓝牙网络共享"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 5394afc32334..06bee941ff0c 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -38,9 +38,9 @@ <string name="connected_via_network_scorer" msgid="5713793306870815341">"已透過 %1$s 自動連線"</string> <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"已透過網絡評分供應商自動連線"</string> <string name="connected_via_passpoint" msgid="2826205693803088747">"已透過 %1$s 連線"</string> - <string name="connected_via_app" msgid="5571999941988929520">"透過「<xliff:g id="NAME">%1$s</xliff:g>」連線"</string> + <string name="connected_via_app" msgid="5571999941988929520">"已透過「<xliff:g id="NAME">%1$s</xliff:g>」連線"</string> <string name="available_via_passpoint" msgid="1617440946846329613">"可透過 %1$s 連線"</string> - <string name="tap_to_sign_up" msgid="6449724763052579434">"輕觸即可註冊"</string> + <string name="tap_to_sign_up" msgid="6449724763052579434">"輕按即可登入"</string> <string name="wifi_connected_no_internet" msgid="8202906332837777829">"已連線,但沒有互聯網"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"沒有互聯網連線"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"必須登入"</string> @@ -48,10 +48,10 @@ <string name="connected_via_carrier" msgid="7583780074526041912">"已透過 %1$s 連線"</string> <string name="available_via_carrier" msgid="1469036129740799053">"可透過 %1$s 連線"</string> <string name="osu_opening_provider" msgid="5488997661548640424">"正在開啟 <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string> - <string name="osu_connect_failed" msgid="2187750899158158934">"無法連線"</string> - <string name="osu_completing_sign_up" msgid="9037638564719197082">"正在完成註冊程序…"</string> - <string name="osu_sign_up_failed" msgid="7296159750352873260">"無法完成註冊程序。輕觸即可重試。"</string> - <string name="osu_sign_up_complete" msgid="8207626049093289203">"註冊完成。連線中…"</string> + <string name="osu_connect_failed" msgid="2187750899158158934">"無法連接"</string> + <string name="osu_completing_sign_up" msgid="9037638564719197082">"正在完成申請…"</string> + <string name="osu_sign_up_failed" msgid="7296159750352873260">"無法完成申請。輕按即可重試。"</string> + <string name="osu_sign_up_complete" msgid="8207626049093289203">"已完成申請。連接中…"</string> <string name="speed_label_very_slow" msgid="1867055264243608530">"非常慢"</string> <string name="speed_label_slow" msgid="813109590815810235">"慢"</string> <string name="speed_label_okay" msgid="2331665440671174858">"良好"</string> @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM 卡存取"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"高清音訊:<xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"高清音訊"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"助聽器"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"已連線至助聽器"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"助聽器"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"已連接助聽器"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"已連接媒體音頻裝置"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"已連接手機耳機"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"已連線至檔案傳輸伺服器"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"用於手機音效"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"用於傳輸檔案"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"用於輸入"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"用於助聽器"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"用於助聽器"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"配對"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"配對"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"取消"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android 作業系統"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"已移除的應用程式"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"已移除的應用程式和使用者"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"系統更新"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB 網絡共享"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"可攜式熱點"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"藍牙網絡共享"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 107eb6de564f..a5074d9db005 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"SIM 卡存取權"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD 高解析音訊:<xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD 高解析音訊"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"助聽器"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"已連接到助聽器"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"助聽器"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"已連接到助聽器"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"連接至媒體音訊"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"連接至電話音訊"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"已連線到檔案傳輸伺服器"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"用於電話音訊"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"用於傳輸檔案"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"用於輸入"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"用於助聽器"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"用於助聽器"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"配對"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"配對"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"取消"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android 作業系統"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"移除的應用程式"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"已移除的應用程式和使用者"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"系統更新"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB 網路共用"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"可攜式無線基地台"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"藍牙網路共用"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 920df4b81475..589209399839 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -87,8 +87,8 @@ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Ukufinyelela kwe-SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Umsindo we-HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Umsindo we-HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Usizo lokuzwa"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Ixhunywe kokokusiza ukuzwa"</string> + <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Izinsiza zokuzwa"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Kuxhumeke kwizinsiza zokuzwa"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Ixhume emsindweni wemidiya"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Ixhunywe kumsindo wefoni"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Ixhunywe kwiseva yokudlulisa ifayela"</string> @@ -105,7 +105,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Sebenziselwa umsindo wefoni"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Sebenziselwa ukudlulisa ifayela"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Isetshenziselwa okufakwayo"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Sebenzisela usizo lokuzwa"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Sebenzisa izinsiza zokuzwa"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Bhangqa"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"BHANQA"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Khansela"</string> @@ -136,6 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"I-Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Izinhlelo zokusebenza zisusiwe"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Izinhelo zokusebenza nabasebenzisi abasusiwe"</string> + <string name="data_usage_ota" msgid="5377889154805560860">"Izibuyekezo zesistimu"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"Imodemu nge-USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"I-hotspot ephathekayo"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Imodemu nge-Bluetooth"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java b/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java index e92b36a45645..b7f7ad256505 100644 --- a/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java +++ b/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java @@ -23,6 +23,10 @@ import android.content.IntentFilter; import android.net.Uri; import android.os.Process; import android.os.UserHandle; +import android.util.ArraySet; +import android.util.Log; + +import java.util.Set; /** * Utility class that allows Settings to use SystemUI to relay broadcasts related to pinned slices. @@ -38,12 +42,22 @@ public class SliceBroadcastRelay { public static final String EXTRA_URI = "uri"; public static final String EXTRA_RECEIVER = "receiver"; public static final String EXTRA_FILTER = "filter"; + private static final String TAG = "SliceBroadcastRelay"; + + private static final Set<Uri> sRegisteredUris = new ArraySet<>(); - public static void registerReceiver(Context context, Uri registerKey, + /** + * Associate intent filter/sliceUri with corresponding receiver. + */ + public static void registerReceiver(Context context, Uri sliceUri, Class<? extends BroadcastReceiver> receiver, IntentFilter filter) { + + Log.d(TAG, "Registering Uri for broadcast relay: " + sliceUri); + sRegisteredUris.add(sliceUri); + Intent registerBroadcast = new Intent(ACTION_REGISTER); registerBroadcast.setPackage(SYSTEMUI_PACKAGE); - registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(registerKey, + registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(sliceUri, Process.myUserHandle().getIdentifier())); registerBroadcast.putExtra(EXTRA_RECEIVER, new ComponentName(context.getPackageName(), receiver.getName())); @@ -52,12 +66,21 @@ public class SliceBroadcastRelay { context.sendBroadcastAsUser(registerBroadcast, UserHandle.SYSTEM); } - public static void unregisterReceivers(Context context, Uri registerKey) { - Intent registerBroadcast = new Intent(ACTION_UNREGISTER); + /** + * Unregisters all receivers for a given slice uri. + */ + + public static void unregisterReceivers(Context context, Uri sliceUri) { + if (!sRegisteredUris.contains(sliceUri)) { + return; + } + Log.d(TAG, "Unregistering uri broadcast relay: " + sliceUri); + final Intent registerBroadcast = new Intent(ACTION_UNREGISTER); registerBroadcast.setPackage(SYSTEMUI_PACKAGE); - registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(registerKey, + registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(sliceUri, Process.myUserHandle().getIdentifier())); context.sendBroadcastAsUser(registerBroadcast, UserHandle.SYSTEM); + sRegisteredUris.remove(sliceUri); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java index a106846fac26..2a1281027169 100644 --- a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java @@ -56,7 +56,7 @@ public class FooterPreference extends Preference { } private void init() { - setIcon(com.android.internal.R.drawable.ic_info_outline_24); + setIcon(R.drawable.ic_info_outline_24); setKey(KEY_FOOTER); setOrder(ORDER_FOOTER); setSelectable(false); diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index ac2c2c946725..43affcdfefe9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -1065,7 +1065,7 @@ public class AccessPoint implements Comparable<AccessPoint> { } public boolean isSaved() { - return networkId != WifiConfiguration.INVALID_NETWORK_ID; + return mConfig != null; } public Object getTag() { diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 3778a9c1cf83..265d464d9c71 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -22,6 +22,11 @@ android:sharedUserId="android.uid.systemui" coreApp="true"> + <!-- Using OpenGL ES 2.0 --> + <uses-feature + android:glEsVersion="0x00020000" + android:required="true" /> + <!-- SysUI must be the one to define this permission; its name is referenced by the core OS. --> <permission android:name="android.permission.systemui.IDENTITY" @@ -126,6 +131,7 @@ <uses-permission android:name="android.permission.USE_BIOMETRIC_INTERNAL" /> <uses-permission android:name="android.permission.USE_FINGERPRINT" /> <uses-permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT" /> + <uses-permission android:name="android.permission.MANAGE_BIOMETRIC" /> <uses-permission android:name="android.permission.MANAGE_SLICE_PERMISSIONS" /> <uses-permission android:name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS" /> diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml index b673e4f3b081..dd124b713a72 100644 --- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml +++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml @@ -14,12 +14,19 @@ Copyright (C) 2017 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0" - android:tint="?android:attr/colorControlNormal" > + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?android:attr/colorControlNormal"> + + <path + android:fillColor="#FFFFFFFF" + android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L11,14.41V22h1l5.71-5.71L13.41,12L17.71,7.71z M13,5.83 l1.88,1.88L13,9.59V5.83z M14.88,16.29L13,18.17v-3.76L14.88,16.29z" /> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M 5 10.5 C 5.82842712475 10.5 6.5 11.1715728753 6.5 12 C 6.5 12.8284271247 5.82842712475 13.5 5 13.5 C 4.17157287525 13.5 3.5 12.8284271247 3.5 12 C 3.5 11.1715728753 4.17157287525 10.5 5 10.5 Z" /> <path - android:pathData="M13.51,12l3.75,-3.74c0.41,-0.41 0.41,-1.07 0,-1.48l-4.47,-4.47 -0.03,-0.03a1.046,1.046 0,0 0,-1.76 0.76v6.44L6.95,5.43c-0.41,-0.41 -1.06,-0.41 -1.47,0s-0.41,1.06 0,1.47l5.09,5.1 -5.09,5.09c-0.41,0.41 -0.41,1.06 0,1.47s1.06,0.41 1.47,0L11,14.51v6.45a1.04,1.04 0,0 0,1.75 0.76l0.05,-0.05 4.46,-4.46c0.41,-0.41 0.41,-1.07 0,-1.48L13.51,12zM12.99,9.67v-4.3l2.15,2.15 -2.15,2.15zM12.99,18.62v-4.3l2.15,2.15 -2.15,2.15zM6.06,13.06c-0.59,0.59 -1.54,0.59 -2.12,0a1.49,1.49 0,0 1,0 -2.12,1.49 1.49,0 0,1 2.12,0c0.59,0.59 0.59,1.53 0,2.12zM20.06,10.94c0.59,0.59 0.59,1.54 0,2.12 -0.59,0.59 -1.54,0.59 -2.12,0a1.49,1.49 0,0 1,0 -2.12,1.49 1.49,0 0,1 2.12,0z" - android:fillColor="#FFFFFFFF" /> + android:fillColor="#FFFFFFFF" + android:pathData="M 19 10.5 C 19.8284271247 10.5 20.5 11.1715728753 20.5 12 C 20.5 12.8284271247 19.8284271247 13.5 19 13.5 C 18.1715728753 13.5 17.5 12.8284271247 17.5 12 C 17.5 11.1715728753 18.1715728753 10.5 19 10.5 Z" /> </vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml index 8cc6caa8abc8..220c63ccca6d 100644 --- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml +++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml @@ -14,12 +14,13 @@ Copyright (C) 2017 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="64dp" - android:height="64dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0" - android:tint="?android:attr/colorControlNormal"> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?android:attr/colorControlNormal"> + <path android:fillColor="#FFFFFFFF" - android:pathData="M13.5,12l3.8,-3.7c0.4,-0.4 0.4,-1.1 0,-1.5l-4.5,-4.5c-0.4,-0.4 -1.1,-0.4 -1.5,0.1C11.1,2.5 11,2.8 11,3v6.4L6.9,5.4C6.5,5 5.9,5 5.5,5.4s-0.4,1.1 0,1.5l5.1,5.1l-5.1,5.1c-0.4,0.4 -0.4,1.1 0,1.5s1.1,0.4 1.5,0l4.1,-4V21c0,0.6 0.5,1 1,1c0.3,0 0.5,-0.1 0.7,-0.3l0.1,0l4.5,-4.5c0.4,-0.4 0.4,-1.1 0,-1.5L13.5,12zM13,9.7V5.4l2.1,2.2L13,9.7zM13,18.6v-4.3l2.1,2.2L13,18.6z"/> + android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L11,14.41V22h1l5.71-5.71L13.41,12L17.71,7.71z M13,5.83 l1.88,1.88L13,9.59V5.83z M14.88,16.29L13,18.17v-3.76L14.88,16.29z" /> </vector> diff --git a/packages/SystemUI/res/drawable/ic_signal_airplane.xml b/packages/SystemUI/res/drawable/ic_signal_airplane.xml index 0a4d7526a55d..f708ed9cb8a6 100644 --- a/packages/SystemUI/res/drawable/ic_signal_airplane.xml +++ b/packages/SystemUI/res/drawable/ic_signal_airplane.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2017 The Android Open Source Project + Copyright (C) 2019 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,16 +15,12 @@ limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="48dp" - android:height="48dp" - android:viewportWidth="20.5" - android:viewportHeight="20.5"> - <group - android:translateX="1.75" - android:translateY="1.4"> - <path - android:pathData="M16.01,9.87l-6.24,-3.9v-4.7C9.77,0.57 9.21,0 8.5,0S7.23,0.57 7.23,1.28v4.7L0.99,9.88c-0.37,0.23 -0.6,0.64 -0.6,1.08v0.41c0,0.29 0.29,0.5 0.55,0.41l6.27,-1.97v4.7l-1.37,1.02c-0.21,0.16 -0.34,0.41 -0.34,0.68v0.57c0,0.15 0.12,0.23 0.27,0.2 1.67,-0.47 1.12,-0.31 2.73,-0.78 1.03,0.3 1.7,0.49 2.72,0.78 0.15,0.03 0.27,-0.06 0.27,-0.2v-0.57c0,-0.27 -0.13,-0.52 -0.34,-0.68l-1.37,-1.02v-4.7l6.27,1.97c0.28,0.09 0.55,-0.12 0.55,-0.41v-0.41c0.01,-0.45 -0.23,-0.87 -0.59,-1.09z" - android:fillColor="#FFF"/> - </group> -</vector> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> +<path + android:fillColor="#FFFFFF" + android:pathData="M21,16v-2l-8-5V3.5C13,2.67,12.33,2,11.5,2S10,2.67,10,3.5V9l-8,5v2l8-2.5V19l-2,1.5V22l3.5-1l3.5,1v-1.5L13,19v-5.5L21,16z" /> +</vector> diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml index f1158ef11ccc..b7b21fa53b62 100644 --- a/packages/SystemUI/res/drawable/privacy_chip_bg.xml +++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml @@ -16,8 +16,8 @@ --> <shape xmlns:android="http://schemas.android.com/apk/res/android"> - <solid android:color="#4a4a4a" /> + <solid android:color="#242424" /> <!-- 14% of white --> <padding android:paddingTop="@dimen/ongoing_appops_chip_bg_padding" - android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding"/> + android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding" /> <corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" /> </shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/rounded_bg.xml b/packages/SystemUI/res/drawable/rounded_bg.xml index c23a87fbfb79..3de67de95f31 100644 --- a/packages/SystemUI/res/drawable/rounded_bg.xml +++ b/packages/SystemUI/res/drawable/rounded_bg.xml @@ -3,8 +3,8 @@ android:shape="rectangle"> <solid android:color="?android:attr/colorPrimary" /> <corners - android:bottomLeftRadius="@dimen/corner_size" - android:topLeftRadius="@dimen/corner_size" + android:bottomLeftRadius="?android:attr/dialogCornerRadius" + android:topLeftRadius="?android:attr/dialogCornerRadius" android:bottomRightRadius="0dp" android:topRightRadius="0dp" /> diff --git a/packages/SystemUI/res/drawable/rounded_bg_bottom.xml b/packages/SystemUI/res/drawable/rounded_bg_bottom.xml index b3bea635f953..7db59e9bad7c 100644 --- a/packages/SystemUI/res/drawable/rounded_bg_bottom.xml +++ b/packages/SystemUI/res/drawable/rounded_bg_bottom.xml @@ -3,7 +3,7 @@ android:shape="rectangle"> <solid android:color="?android:attr/colorPrimaryDark" /> <corners - android:bottomLeftRadius="@dimen/corner_size" + android:bottomLeftRadius="?android:attr/dialogCornerRadius" android:topLeftRadius="0dp" android:bottomRightRadius="0dp" android:topRightRadius="0dp" diff --git a/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml b/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml index 622226f25de3..382ca20268b7 100644 --- a/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml +++ b/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml @@ -3,9 +3,9 @@ android:shape="rectangle"> <solid android:color="?android:attr/panelColorBackground" /> <corners - android:bottomLeftRadius="@dimen/corner_size" + android:bottomLeftRadius="?android:attr/dialogCornerRadius" android:topLeftRadius="0dp" - android:bottomRightRadius="@dimen/corner_size" + android:bottomRightRadius="?android:attr/dialogCornerRadius" android:topRightRadius="0dp" /> </shape> diff --git a/packages/SystemUI/res/drawable/rounded_bg_full.xml b/packages/SystemUI/res/drawable/rounded_bg_full.xml index 03f18bb5d680..e0d3f63e8f40 100644 --- a/packages/SystemUI/res/drawable/rounded_bg_full.xml +++ b/packages/SystemUI/res/drawable/rounded_bg_full.xml @@ -3,9 +3,9 @@ android:shape="rectangle"> <solid android:color="?android:attr/colorBackgroundFloating" /> <corners - android:bottomLeftRadius="@dimen/corner_size" - android:topLeftRadius="@dimen/corner_size" - android:bottomRightRadius="@dimen/corner_size" - android:topRightRadius="@dimen/corner_size" + android:bottomLeftRadius="?android:attr/dialogCornerRadius" + android:topLeftRadius="?android:attr/dialogCornerRadius" + android:bottomRightRadius="?android:attr/dialogCornerRadius" + android:topRightRadius="?android:attr/dialogCornerRadius" /> </shape> diff --git a/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml b/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml index a4b3c99f7ec6..a62657d14afc 100644 --- a/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml +++ b/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml @@ -17,9 +17,9 @@ android:shape="rectangle"> <solid android:color="?android:attr/colorPrimaryDark" /> <corners - android:bottomLeftRadius="@dimen/corner_size" + android:bottomLeftRadius="?android:attr/dialogCornerRadius" android:topLeftRadius="0dp" - android:bottomRightRadius="@dimen/corner_size" + android:bottomRightRadius="?android:attr/dialogCornerRadius" android:topRightRadius="0dp" /> </shape> diff --git a/packages/SystemUI/res/layout-land/global_actions_grid.xml b/packages/SystemUI/res/layout-land/global_actions_grid.xml new file mode 100644 index 000000000000..911b661d48eb --- /dev/null +++ b/packages/SystemUI/res/layout-land/global_actions_grid.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="utf-8"?> +<com.android.systemui.globalactions.GlobalActionsGridLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@id/global_actions_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal" + android:clipToPadding="false" + android:theme="@style/qs_theme" + android:gravity="top|right" + android:clipChildren="false" +> + + <LinearLayout + android:layout_height="match_parent" + android:layout_width="wrap_content" + android:gravity="top|right" + android:padding="0dp" + android:orientation="vertical" + android:layoutDirection="ltr" + android:layout_marginRight="@dimen/global_actions_grid_container_bottom_margin" + > + <!-- Grid of action items --> + <com.android.systemui.globalactions.ListGridLayout + android:id="@android:id/list" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layoutDirection="ltr" + android:layout_marginTop="@dimen/global_actions_grid_side_margin" + android:translationZ="@dimen/global_actions_translate" + android:paddingLeft="@dimen/global_actions_grid_top_padding" + android:paddingRight="@dimen/global_actions_grid_bottom_padding" + android:paddingTop="@dimen/global_actions_grid_left_padding" + android:paddingBottom="@dimen/global_actions_grid_right_padding" + android:background="?android:attr/colorBackgroundFloating" + > + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + android:layoutDirection="ltr" + android:orientation="horizontal" + /> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + android:layoutDirection="ltr" + android:orientation="horizontal" + /> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + android:layoutDirection="ltr" + android:orientation="horizontal" + /> + </com.android.systemui.globalactions.ListGridLayout> + + <!-- For separated items--> + <LinearLayout + android:id="@+id/separated_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/global_actions_grid_side_margin" + android:layout_marginBottom="@dimen/global_actions_grid_side_margin" + android:paddingTop="@dimen/global_actions_grid_left_padding" + android:paddingLeft="@dimen/global_actions_grid_top_padding" + android:paddingBottom="@dimen/global_actions_grid_right_padding" + android:paddingRight="@dimen/global_actions_grid_bottom_padding" + android:orientation="horizontal" + android:layoutDirection="ltr" + android:background="?android:attr/colorBackgroundFloating" + android:translationZ="@dimen/global_actions_translate" + /> + + </LinearLayout> + +</com.android.systemui.globalactions.GlobalActionsGridLayout> diff --git a/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml b/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml new file mode 100644 index 000000000000..669be1b40567 --- /dev/null +++ b/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="utf-8"?> +<com.android.systemui.globalactions.GlobalActionsGridLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@id/global_actions_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal" + android:clipToPadding="false" + android:theme="@style/qs_theme" + android:gravity="top|left" + android:clipChildren="false" +> + + <LinearLayout + android:layout_height="match_parent" + android:layout_width="wrap_content" + android:gravity="bottom|left" + android:padding="0dp" + android:orientation="vertical" + android:layout_marginLeft="@dimen/global_actions_grid_container_bottom_margin" + > + <!-- For separated items--> + <LinearLayout + android:id="@+id/separated_button" + android:layout_gravity="top|left" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/global_actions_grid_side_margin" + android:layout_marginBottom="@dimen/global_actions_grid_side_margin" + android:paddingTop="@dimen/global_actions_grid_left_padding" + android:paddingLeft="@dimen/global_actions_grid_top_padding" + android:paddingBottom="@dimen/global_actions_grid_right_padding" + android:paddingRight="@dimen/global_actions_grid_bottom_padding" + android:orientation="horizontal" + android:layoutDirection="rtl" + android:background="?android:attr/colorBackgroundFloating" + android:translationZ="@dimen/global_actions_translate" + /> + + <!-- Grid of action items --> + <com.android.systemui.globalactions.ListGridLayout + android:id="@android:id/list" + android:layout_gravity="bottom|left" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_marginTop="@dimen/global_actions_grid_side_margin" + android:translationZ="@dimen/global_actions_translate" + android:paddingLeft="@dimen/global_actions_grid_top_padding" + android:paddingRight="@dimen/global_actions_grid_bottom_padding" + android:paddingTop="@dimen/global_actions_grid_left_padding" + android:paddingBottom="@dimen/global_actions_grid_right_padding" + android:background="?android:attr/colorBackgroundFloating" + > + <LinearLayout + android:layout_gravity="bottom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + android:layoutDirection="rtl" + android:orientation="horizontal" + /> + <LinearLayout + android:layout_gravity="bottom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + android:layoutDirection="rtl" + android:orientation="horizontal" + /> + <LinearLayout + android:layout_gravity="bottom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + android:layoutDirection="rtl" + android:orientation="horizontal" + /> + </com.android.systemui.globalactions.ListGridLayout> + </LinearLayout> + +</com.android.systemui.globalactions.GlobalActionsGridLayout> diff --git a/packages/SystemUI/res/layout/global_actions_grid.xml b/packages/SystemUI/res/layout/global_actions_grid.xml index e6f2376ae76b..1b56fa089281 100644 --- a/packages/SystemUI/res/layout/global_actions_grid.xml +++ b/packages/SystemUI/res/layout/global_actions_grid.xml @@ -12,10 +12,11 @@ > <LinearLayout - android:layout_height="290dp" - android:layout_width="412dp" - android:gravity="bottom" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:gravity="bottom | right" android:padding="0dp" + android:layoutDirection="ltr" android:layout_marginBottom="@dimen/global_actions_grid_container_bottom_margin" > <!-- For separated items--> @@ -34,15 +35,11 @@ android:translationZ="@dimen/global_actions_translate" /> - <Space android:layout_width="match_parent" android:layout_height="2dp" - android:layout_weight="1" /> - <!-- Grid of action items --> <com.android.systemui.globalactions.ListGridLayout android:id="@android:id/list" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:gravity="right" android:orientation="horizontal" android:layoutDirection="rtl" android:layout_marginRight="@dimen/global_actions_grid_side_margin" @@ -56,25 +53,19 @@ <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="bottom|right" android:visibility="gone" - android:gravity="bottom" android:orientation="vertical" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="bottom|right" android:visibility="gone" - android:gravity="bottom" android:orientation="vertical" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="bottom|right" android:visibility="gone" - android:gravity="bottom" android:orientation="vertical" /> </com.android.systemui.globalactions.ListGridLayout> diff --git a/packages/SystemUI/res/layout/global_actions_grid_item.xml b/packages/SystemUI/res/layout/global_actions_grid_item.xml index 0c11cd977256..a8938390690f 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_item.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_item.xml @@ -47,6 +47,7 @@ android:gravity="center" android:textSize="12sp" android:textAppearance="?android:attr/textAppearanceSmall" + android:singleLine="true" /> <TextView @@ -57,5 +58,6 @@ android:gravity="center" android:textColor="?android:attr/textColorTertiary" android:textAppearance="?android:attr/textAppearanceSmall" + android:singleLine="true" /> </LinearLayout> diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml index 58fe81109731..f64a64e619a0 100644 --- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml +++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml @@ -15,6 +15,7 @@ limitations under the License. --> + <com.android.systemui.privacy.OngoingPrivacyChip xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/privacy_chip" @@ -22,47 +23,39 @@ android:layout_width="wrap_content" android:layout_marginLeft="@dimen/ongoing_appops_chip_margin" android:layout_marginRight="@dimen/ongoing_appops_chip_margin" - android:layout_marginTop="@dimen/ongoing_appops_top_chip_margin" - android:layout_marginBottom="@dimen/ongoing_appops_top_chip_margin" - android:gravity="center_vertical|center_horizontal" android:layout_gravity="center_vertical|start" + android:gravity="center_vertical" android:orientation="horizontal" - android:paddingStart="@dimen/ongoing_appops_chip_side_padding" - android:paddingEnd="@dimen/ongoing_appops_chip_side_padding" android:focusable="true"> - <TextView - android:id="@+id/in_use_text" - android:layout_height="match_parent" - android:layout_width="wrap_content" - android:layout_gravity="center_vertical|start" - android:layout_marginEnd="@dimen/ongoing_appops_chip_icon_margin_collapsed" - android:gravity="center_vertical" - android:textAppearance="@style/TextAppearance.StatusBar.Clock" - android:textColor="@color/status_bar_clock_color" - android:text="@string/ongoing_privacy_chip_in_use" - /> - <LinearLayout - android:id="@+id/icons_container" - android:layout_height="match_parent" + android:id="@+id/background" + android:layout_height="@dimen/ongoing_appops_chip_height" android:layout_width="wrap_content" - android:layout_gravity="center_vertical" - android:gravity="center_vertical" - /> + > + <LinearLayout + android:id="@+id/icons_container" + android:layout_height="match_parent" + android:layout_width="wrap_content" + android:layout_marginStart="@dimen/ongoing_appops_chip_items_margin" + android:layout_gravity="center_vertical" + android:gravity="center_vertical" + /> - <TextView - android:id="@+id/text_container" - android:layout_height="match_parent" - android:layout_width="wrap_content" - android:singleLine="true" - android:ellipsize="end" - android:lines="1" - android:layout_gravity="center_vertical|end" - android:gravity="center_vertical" - android:textAppearance="@style/TextAppearance.StatusBar.Clock" - android:textColor="@color/status_bar_clock_color" - android:layout_marginStart="@dimen/ongoing_appops_chip_icon_margin_collapsed" - android:layout_marginEnd="@dimen/ongoing_appops_chip_icon_margin_collapsed" - /> + <TextView + android:id="@+id/text_container" + android:layout_height="match_parent" + android:layout_width="wrap_content" + android:layout_gravity="center_vertical|end" + android:paddingStart="@dimen/ongoing_appops_chip_text_padding" + android:paddingEnd="@dimen/ongoing_appops_chip_text_padding" + android:gravity="center_vertical" + android:singleLine="true" + android:ellipsize="end" + android:lines="1" + android:textAppearance="@style/TextAppearance.StatusBar.Clock" + android:textSize="@dimen/ongoing_appops_chip_text_size" + android:textColor="@color/status_bar_clock_color" + /> + </LinearLayout> </com.android.systemui.privacy.OngoingPrivacyChip>
\ No newline at end of file diff --git a/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl b/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl new file mode 100644 index 000000000000..586cdf3fbaae --- /dev/null +++ b/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl @@ -0,0 +1,27 @@ +precision mediump float; + +uniform sampler2D uTexture; +uniform float uCenterReveal; +uniform float uReveal; +uniform float uAod2Opacity; +varying vec2 vTextureCoordinates; + +vec3 luminosity(vec3 color) { + float lum = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b; + return vec3(lum); +} + +vec4 transform(vec3 diffuse) { + // TODO: Add well comments here, tracking on b/123615467. + vec3 lum = luminosity(diffuse); + diffuse = mix(diffuse, lum, smoothstep(0., uCenterReveal, uReveal)); + float val = mix(uReveal, uCenterReveal, step(uCenterReveal, uReveal)); + diffuse = smoothstep(val, 1.0, diffuse); + diffuse *= uAod2Opacity * (1. - smoothstep(uCenterReveal, 1., uReveal)); + return vec4(diffuse.r, diffuse.g, diffuse.b, 1.); +} + +void main() { + vec4 fragColor = texture2D(uTexture, vTextureCoordinates); + gl_FragColor = transform(fragColor.rgb); +}
\ No newline at end of file diff --git a/packages/SystemUI/res/raw/image_wallpaper_vertex_shader.glsl b/packages/SystemUI/res/raw/image_wallpaper_vertex_shader.glsl new file mode 100644 index 000000000000..4393e2bb0ebf --- /dev/null +++ b/packages/SystemUI/res/raw/image_wallpaper_vertex_shader.glsl @@ -0,0 +1,8 @@ +attribute vec4 aPosition; +attribute vec2 aTextureCoordinates; +varying vec2 vTextureCoordinates; + +void main() { + vTextureCoordinates = aTextureCoordinates; + gl_Position = aPosition; +}
\ No newline at end of file diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index df858f0c54e2..bb0c6f6acb06 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -34,5 +34,4 @@ <bool name="quick_settings_wide">true</bool> <dimen name="qs_detail_margin_top">0dp</dimen> <dimen name="qs_paged_tile_layout_padding_bottom">0dp</dimen> - <dimen name="ongoing_appops_top_chip_margin">2dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 1e1245fe0d86..1c7ee3667c16 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -980,26 +980,32 @@ <dimen name="ongoing_appops_dialog_items_bottom_margin">24dp</dimen> <!-- Top and bottom margin of title in Ongoing App Ops dialog --> <dimen name="ongoing_appops_dialog_title_margin_top_bottom">18dp</dimen> + <!-- Text size for Ongoing App Ops dialog title --> + <dimen name="ongoing_appops_dialog_title_size">20sp</dimen> + <!-- Text size for Ongoing App Ops dialog items --> + <dimen name="ongoing_appops_dialog_item_size">16sp</dimen> <!-- Side margins around the Ongoing App Ops chip--> - <dimen name="ongoing_appops_chip_margin">12dp</dimen> - <!-- Top and bottom margins around the Ongoing App Ops chip --> - <dimen name="ongoing_appops_top_chip_margin">12dp</dimen> + <dimen name="ongoing_appops_chip_margin">0dp</dimen> + <!-- Height of the Ongoing App Ops chip --> + <dimen name="ongoing_appops_chip_height">32dp</dimen> <!-- Start and End padding for Ongoing App Ops chip --> - <dimen name="ongoing_appops_chip_side_padding">6dp</dimen> + <dimen name="ongoing_appops_chip_text_padding">8dp</dimen> <!-- Padding between background of Ongoing App Ops chip and content --> <dimen name="ongoing_appops_chip_bg_padding">0dp</dimen> + <!-- Side padding between background of Ongoing App Ops chip and content --> + <dimen name="ongoing_appops_chip_side_padding">8dp</dimen> <!-- Margin between icons of Ongoing App Ops chip when QQS--> <dimen name="ongoing_appops_chip_icon_margin_collapsed">0dp</dimen> <!-- Margin between icons of Ongoing App Ops chip when QS--> - <dimen name="ongoing_appops_chip_icon_margin_expanded">8dp</dimen> + <dimen name="ongoing_appops_chip_icon_margin_expanded">2dp</dimen> <!-- Icon size of Ongoing App Ops chip --> - <dimen name="ongoing_appops_chip_icon_size">18dp</dimen> + <dimen name="ongoing_appops_chip_icon_size">@*android:dimen/status_bar_icon_size</dimen> <!-- Radius of Ongoing App Ops chip corners --> - <dimen name="ongoing_appops_chip_bg_corner_radius">4dp</dimen> - <!-- Text size for Ongoing App Ops dialog title --> - <dimen name="ongoing_appops_dialog_title_size">20sp</dimen> - <!-- Text size for Ongoing App Ops dialog items --> - <dimen name="ongoing_appops_dialog_item_size">16sp</dimen> + <dimen name="ongoing_appops_chip_bg_corner_radius">16dp</dimen> + <!-- Size of text of Ongoing App Ops chip --> + <dimen name="ongoing_appops_chip_text_size">12sp</dimen> + <!-- Margin between items in Ongoing App Ops chip --> + <dimen name="ongoing_appops_chip_items_margin">8dp</dimen> <!-- How much a bubble is elevated --> <dimen name="bubble_elevation">8dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index b4131d738d13..01595f0705be 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1582,19 +1582,19 @@ <!-- Notification Inline controls: continue receiving notifications prompt, channel level --> <string name="inline_keep_showing">Keep showing these notifications?</string> - <!-- Notification inline controls: block notifications button --> + <!-- Notification inline controls: block notifications button [CHAR_LIMIT=25] --> <string name="inline_stop_button">Stop notifications</string> <!-- Notification inline controls: button to deliver notifications silently from this channel [CHAR_LIMIT=35] --> <string name="inline_deliver_silently_button">Deliver Silently</string> - <!-- Notification inline controls: button to block notifications from this channel [CHAR_LIMIT=35] --> + <!-- Notification inline controls: button to block notifications from this channel [CHAR_LIMIT=20] --> <string name="inline_block_button">Block</string> - <!-- Notification inline controls: keep getting notifications button --> + <!-- Notification inline controls: keep getting notifications button [CHAR_LIMIT=25] --> <string name="inline_keep_button">Keep showing</string> - <!-- Notification inline controls: minimize notifications button --> + <!-- Notification inline controls: minimize notifications button [CHAR_LIMIT=20] --> <string name="inline_minimize_button">Minimize</string> <!-- Notification inline controls: button to show notifications silently, without alerting the user [CHAR_LIMIT=35] --> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index f277c4303ac1..3ac7fd4c61c1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -724,6 +724,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } private void handleFaceAuthFailed() { + setFaceRunningState(BIOMETRIC_STATE_STOPPED); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { diff --git a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java index ce9c637cebf6..3c6f081d0373 100644 --- a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java +++ b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java @@ -21,11 +21,13 @@ import com.android.systemui.appops.AppOpsControllerImpl; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.VolumeDialogController; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.PowerNotificationWarnings; import com.android.systemui.power.PowerUI; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QSTileHost; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl; import com.android.systemui.statusbar.phone.ManagedProfileController; import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl; @@ -194,6 +196,12 @@ public abstract class DependencyBinder { /** */ @Binds + public abstract StatusBarStateController provideStatusBarStateController( + StatusBarStateControllerImpl controllerImpl); + + /** + */ + @Binds public abstract StatusBarIconController provideStatusBarIconController( StatusBarIconControllerImpl controllerImpl); diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java index e28aa9d369cb..2a1d066d356e 100644 --- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java +++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java @@ -23,7 +23,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; -import android.content.res.Configuration; import android.provider.Settings; import android.util.AttributeSet; import android.view.Gravity; @@ -59,7 +58,6 @@ public class HardwareUiLayout extends MultiListLayout implements Tunable { private int mEndPoint; private boolean mEdgeBleed; private boolean mRoundedDivider; - private int mRotation = ROTATION_NONE; private boolean mRotatedBackground; private boolean mSwapOrientation = true; @@ -89,7 +87,7 @@ public class HardwareUiLayout extends MultiListLayout implements Tunable { } @Override - public ViewGroup getParentView(boolean separated, int index) { + public ViewGroup getParentView(boolean separated, int index, boolean reverse) { if (separated) { return getSeparatedView(); } else { @@ -174,7 +172,6 @@ public class HardwareUiLayout extends MultiListLayout implements Tunable { mSeparatedView.setBackground(mSeparatedViewBackground); updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding()); mOldHeight = mList.getMeasuredHeight(); - updateRotation(); } else { return; } @@ -188,25 +185,13 @@ public class HardwareUiLayout extends MultiListLayout implements Tunable { post(() -> updatePosition()); } - @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - updateRotation(); - } - public void setSwapOrientation(boolean swapOrientation) { mSwapOrientation = swapOrientation; } - private void updateRotation() { - int rotation = RotationUtils.getRotation(getContext()); - if (rotation != mRotation) { - rotate(mRotation, rotation); - mRotation = rotation; - } - } - - private void rotate(int from, int to) { + @Override + protected void rotate(int from, int to) { + super.rotate(from, to); if (from != ROTATION_NONE && to != ROTATION_NONE) { // Rather than handling this confusing case, just do 2 rotations. rotate(from, ROTATION_NONE); diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index 2aecc24e83c0..7e645ab77a63 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -28,7 +28,9 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region.Op; import android.hardware.display.DisplayManager; +import android.opengl.GLSurfaceView; import android.os.AsyncTask; +import android.os.Build; import android.os.Handler; import android.os.Trace; import android.service.wallpaper.WallpaperService; @@ -39,6 +41,7 @@ import android.view.Surface; import android.view.SurfaceHolder; import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.glwallpaper.ImageWallpaperRenderer; import java.io.FileDescriptor; import java.io.IOException; @@ -73,10 +76,78 @@ public class ImageWallpaper extends WallpaperService { @Override public Engine onCreateEngine() { - mEngine = new DrawableEngine(); - return mEngine; + if (Build.IS_DEBUGGABLE) { + Log.v(TAG, "We are using GLEngine"); + } + return new GLEngine(this); + } + + class GLEngine extends Engine { + private GLWallpaperSurfaceView mWallpaperSurfaceView; + + GLEngine(Context context) { + mWallpaperSurfaceView = new GLWallpaperSurfaceView(context); + mWallpaperSurfaceView.setRenderer( + new ImageWallpaperRenderer(context, mWallpaperSurfaceView)); + mWallpaperSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + setOffsetNotificationsEnabled(true); + } + + @Override + public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) { + if (mWallpaperSurfaceView != null) { + mWallpaperSurfaceView.notifyAmbientModeChanged(inAmbientMode, animationDuration); + } + } + + @Override + public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep, + float yOffsetStep, int xPixelOffset, int yPixelOffset) { + if (mWallpaperSurfaceView != null) { + mWallpaperSurfaceView.notifyOffsetsChanged(xOffset, yOffset); + } + } + + private class GLWallpaperSurfaceView extends GLSurfaceView implements ImageGLView { + private WallpaperStatusListener mWallpaperChangedListener; + + GLWallpaperSurfaceView(Context context) { + super(context); + setEGLContextClientVersion(2); + } + + @Override + public SurfaceHolder getHolder() { + return getSurfaceHolder(); + } + + @Override + public void setRenderer(Renderer renderer) { + super.setRenderer(renderer); + mWallpaperChangedListener = (WallpaperStatusListener) renderer; + } + + private void notifyAmbientModeChanged(boolean inAmbient, long duration) { + if (mWallpaperChangedListener != null) { + mWallpaperChangedListener.onAmbientModeChanged(inAmbient, duration); + } + } + + private void notifyOffsetsChanged(float xOffset, float yOffset) { + if (mWallpaperChangedListener != null) { + mWallpaperChangedListener.onOffsetsChanged( + xOffset, yOffset, getHolder().getSurfaceFrame()); + } + } + + @Override + public void render() { + requestRender(); + } + } } + // TODO: Remove this engine, tracking on b/123617158. class DrawableEngine extends Engine { private final Runnable mUnloadWallpaperCallback = () -> { unloadWallpaper(false /* forgetSize */); @@ -564,4 +635,35 @@ public class ImageWallpaper extends WallpaperService { } } } + + /** + * A listener to trace status of image wallpaper. + */ + public interface WallpaperStatusListener { + + /** + * Called back while ambient mode changes. + * @param inAmbientMode true if is in ambient mode, false otherwise. + * @param duration the duration of animation. + */ + void onAmbientModeChanged(boolean inAmbientMode, long duration); + + /** + * Called back while wallpaper offsets. + * @param xOffset The offset portion along x. + * @param yOffset The offset portion along y. + */ + void onOffsetsChanged(float xOffset, float yOffset, Rect frame); + } + + /** + * An abstraction for view of GLRenderer. + */ + public interface ImageGLView { + + /** + * Ask the view to render. + */ + void render(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java index 85265f458370..00ff518ce212 100644 --- a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java +++ b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java @@ -17,11 +17,14 @@ package com.android.systemui; import android.content.Context; +import android.content.res.Configuration; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; +import com.android.systemui.util.leak.RotationUtils; + /** * Layout class representing the Global Actions menu which appears when the power button is held. */ @@ -32,8 +35,12 @@ public abstract class MultiListLayout extends LinearLayout { protected int mExpectedSeparatedItemCount; protected int mExpectedListItemCount; + protected int mRotation; + protected RotationListener mRotationListener; + public MultiListLayout(Context context, AttributeSet attrs) { super(context, attrs); + mRotation = RotationUtils.getRotation(context); } protected abstract ViewGroup getSeparatedView(); @@ -50,10 +57,12 @@ public abstract class MultiListLayout extends LinearLayout { * @param separated Whether or not this index refers to a position in the separated or list * container. * @param index The index of the item within the container. + * @param reverse If the MultiListLayout contains sub-lists within the list container, reverse + * the order that they are filled. * @return The parent ViewGroup which will be used to contain the specified item * after it has been added to the layout. */ - public abstract ViewGroup getParentView(boolean separated, int index); + public abstract ViewGroup getParentView(boolean separated, int index, boolean reverse); /** * Sets the divided view, which may have a differently-colored background. @@ -111,6 +120,26 @@ public abstract class MultiListLayout extends LinearLayout { setFocusable(true); } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + int newRotation = RotationUtils.getRotation(mContext); + if (newRotation != mRotation) { + rotate(mRotation, newRotation); + mRotation = newRotation; + } + } + + protected void rotate(int from, int to) { + if (mRotationListener != null) { + mRotationListener.onRotate(from, to); + } + } + + public void setRotationListener(RotationListener listener) { + mRotationListener = listener; + } + /** * Retrieve the MultiListLayout associated with the given view. */ @@ -121,4 +150,8 @@ public abstract class MultiListLayout extends LinearLayout { } return null; } + + interface RotationListener { + void onRotate(int from, int to); + } } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index d27a90332ac5..6bb4fb5ef94a 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -41,8 +41,8 @@ import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl; +import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.ScrimView; -import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotificationData; @@ -142,7 +142,7 @@ public class SystemUIFactory { StatusBar statusBar, StatusBarStateController statusBarStateController, NotificationListener listener) { return new NotificationIconAreaController(context, statusBar, statusBarStateController, - listener); + listener, Dependency.get(NotificationMediaManager.class)); } public KeyguardIndicationController createKeyguardIndicationController(Context context, @@ -154,15 +154,6 @@ public class SystemUIFactory { return new VolumeDialogComponent(systemUi, context); } - /** - * Provides status bar state controller implementation - */ - @Singleton - @Provides - public StatusBarStateController provideStatusBarStateController(Context context) { - return new StatusBarStateControllerImpl(); - } - @Singleton @Provides public NotificationData.KeyguardEnvironment provideKeyguardEnvironment(Context context) { diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index 038f4912e40f..83398cf6a88f 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -122,23 +122,9 @@ public class AssistManager implements ConfigurationChangedReceiver { } @Override - public void onTranscriptionUpdate(String transcription) { + public void onSetUiHints(Bundle hints) { if (VERBOSE) { - Log.v(TAG, "Transcription Updated: \"" + transcription + "\""); - } - } - - @Override - public void onTranscriptionComplete(boolean immediate) { - if (VERBOSE) { - Log.v(TAG, "Transcription complete (immediate=" + immediate + ")"); - } - } - - @Override - public void onVoiceStateChange(int state) { - if (VERBOSE) { - Log.v(TAG, "Voice state is now " + state); + Log.v(TAG, "UI hints received"); } } }); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index a67e1b7032c6..e62c77de0051 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -52,7 +52,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.row.NotificationInflater; +import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; import com.android.systemui.statusbar.phone.StatusBarWindowController; import java.util.List; @@ -150,7 +150,8 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe } @Inject - public BubbleController(Context context, StatusBarWindowController statusBarWindowController) { + public BubbleController(Context context, StatusBarWindowController statusBarWindowController, + BubbleData data) { mContext = context; mNotificationEntryManager = Dependency.get(NotificationEntryManager.class); @@ -171,7 +172,7 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe mTaskStackListener = new BubbleTaskStackListener(); ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); - mBubbleData = BubbleData.getInstance(); + mBubbleData = data; } /** @@ -253,7 +254,7 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe mStackView.updateBubble(notif, updatePosition); } else { if (mStackView == null) { - mStackView = new BubbleStackView(mContext); + mStackView = new BubbleStackView(mContext, mBubbleData); ViewGroup sbv = mStatusBarWindowController.getStatusBarView(); // XXX: Bug when you expand the shade on top of expanded bubble, there is no scrim // between bubble and the shade @@ -314,8 +315,7 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe } @Override - public void onEntryInflated(NotificationEntry entry, - @NotificationInflater.InflationFlag int inflatedFlags) { + public void onEntryInflated(NotificationEntry entry, @InflationFlag int inflatedFlags) { if (!areBubblesEnabled(mContext)) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index 89b0de85e18d..5002f5cce751 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -22,23 +22,19 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.util.Collection; import java.util.HashMap; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Keeps track of active bubbles. */ +@Singleton class BubbleData { private HashMap<String, Bubble> mBubbles = new HashMap<>(); - private static BubbleData sBubbleData = null; - - private BubbleData() {} - - public static BubbleData getInstance() { - if (sBubbleData == null) { - sBubbleData = new BubbleData(); - } - return sBubbleData; - } + @Inject + BubbleData() {} /** * The set of bubbles. diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 3389c46f66b7..492eadd240ed 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -107,8 +107,10 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() { @Override public void onActivityViewReady(ActivityView view) { - mActivityViewReady = true; - mActivityView.startActivity(mBubbleIntent); + if (!mActivityViewReady) { + mActivityViewReady = true; + mActivityView.startActivity(mBubbleIntent); + } } @Override @@ -262,6 +264,12 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList updateHeaderView(); updatePermissionView(); updateExpandedView(); + } + + /** + * Lets activity view know it should be shown / populated. + */ + public void populateActivityView() { mActivityView.setCallback(mStateCallback); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 8bc83d4c852b..ed9b38b85156 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -58,7 +58,7 @@ import java.math.RoundingMode; /** * Renders bubbles in a stack and handles animating expanded and collapsed states. */ -public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.FloatingView { +public class BubbleStackView extends FrameLayout { private static final String TAG = "BubbleStackView"; /** @@ -85,6 +85,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F private final SpringAnimation mExpandedViewXAnim; private final SpringAnimation mExpandedViewYAnim; + private final BubbleData mBubbleData; private PhysicsAnimationLayout mBubbleContainer; private StackAnimationController mStackAnimationController; @@ -92,12 +93,12 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F private FrameLayout mExpandedViewContainer; - private BubbleData mBubbleData; private int mBubbleSize; private int mBubblePadding; private int mExpandedAnimateXDistance; private int mExpandedAnimateYDistance; + private int mStatusBarHeight; private Bubble mExpandedBubble; private boolean mIsExpanded; @@ -140,13 +141,14 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F } }; - public BubbleStackView(Context context) { + public BubbleStackView(Context context, BubbleData data) { super(context); - mBubbleData = BubbleData.getInstance(); + mBubbleData = data; mInflater = LayoutInflater.from(context); - mTouchHandler = new BubbleTouchHandler(context); + mTouchHandler = new BubbleTouchHandler(context, this); setOnTouchListener(mTouchHandler); + mInflater = LayoutInflater.from(context); Resources res = getResources(); mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size); @@ -155,6 +157,8 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_x_distance); mExpandedAnimateYDistance = res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_y_distance); + mStatusBarHeight = + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); mDisplaySize = new Point(); WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); @@ -196,6 +200,8 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)); setClipChildren(false); + + mBubbleContainer.bringToFront(); } @Override @@ -481,9 +487,10 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F if (shouldExpand) { mBubbleContainer.setController(mExpandedAnimationController); mExpandedAnimationController.expandFromStack( - mStackAnimationController.getStackPosition(), () -> { - updatePointerPosition(); - updateAfter.run(); + mStackAnimationController.getStackPosition(), + () -> { + updatePointerPosition(); + updateAfter.run(); }); } else { mBubbleContainer.cancelAllAnimations(); @@ -541,55 +548,49 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F : null; } - @Override - public void setPosition(float x, float y) { - mStackAnimationController.moveFirstBubbleWithStackFollowing(x, y); - } - - @Override - public void setPositionX(float x) { - // Unsupported, use setPosition(x, y). - } - - @Override - public void setPositionY(float y) { - // Unsupported, use setPosition(x, y). - } - - @Override - public PointF getPosition() { + public PointF getStackPosition() { return mStackAnimationController.getStackPosition(); } /** Called when a drag operation on an individual bubble has started. */ - public void onBubbleDragStart(BubbleView bubble) { - // TODO: Save position and snap back if not dismissed. + public void onBubbleDragStart(View bubble) { + mExpandedAnimationController.prepareForBubbleDrag(bubble); } /** Called with the coordinates to which an individual bubble has been dragged. */ - public void onBubbleDragged(BubbleView bubble, float x, float y) { - bubble.setTranslationX(x); - bubble.setTranslationY(y); + public void onBubbleDragged(View bubble, float x, float y) { + if (!mIsExpanded || mIsAnimating) { + return; + } + + mExpandedAnimationController.dragBubbleOut(bubble, x, y); } /** Called when a drag operation on an individual bubble has finished. */ - public void onBubbleDragFinish(BubbleView bubble, float x, float y, float velX, float velY) { - // TODO: Add fling to bottom to dismiss. + public void onBubbleDragFinish( + View bubble, float x, float y, float velX, float velY, boolean dismissed) { + if (!mIsExpanded || mIsAnimating) { + return; + } + + if (dismissed) { + mExpandedAnimationController.prepareForDismissalWithVelocity(bubble, velX, velY); + } else { + mExpandedAnimationController.snapBubbleBack(bubble, velX, velY); + } } void onDragStart() { - if (mIsExpanded) { + if (mIsExpanded || mIsAnimating) { return; } mStackAnimationController.cancelStackPositionAnimations(); mBubbleContainer.setController(mStackAnimationController); - mIsAnimating = false; } void onDragged(float x, float y) { - // TODO: We can drag if animating - just need to reroute inflight anims to drag point. - if (mIsExpanded) { + if (mIsExpanded || mIsAnimating) { return; } @@ -678,7 +679,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F if (getRootWindowInsets() != null) { WindowInsets insets = getRootWindowInsets(); return Math.max( - insets.getSystemWindowInsetTop(), + mStatusBarHeight, insets.getDisplayCutout() != null ? insets.getDisplayCutout().getSafeInsetTop() : 0); @@ -707,6 +708,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F mExpandedViewContainer.removeAllViews(); if (mExpandedBubble != null && mIsExpanded) { mExpandedViewContainer.addView(mExpandedBubble.expandedView); + mExpandedBubble.expandedView.populateActivityView(); mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE); } } @@ -740,9 +742,9 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F private void updatePointerPosition() { if (mExpandedBubble != null) { - float pointerPosition = mExpandedBubble.iconView.getPosition().x + float pointerPosition = mExpandedBubble.iconView.getTranslationX() + (mExpandedBubble.iconView.getWidth() / 2f); - mExpandedBubble.expandedView.setPointerPosition(pointerPosition); + mExpandedBubble.expandedView.setPointerPosition((int) pointerPosition); } } @@ -768,7 +770,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F * @return the normalized x-axis position of the bubble stack rounded to 4 decimal places. */ public float getNormalizedXPosition() { - return new BigDecimal(getPosition().x / mDisplaySize.x) + return new BigDecimal(getStackPosition().x / mDisplaySize.x) .setScale(4, RoundingMode.CEILING.HALF_UP) .floatValue(); } @@ -777,7 +779,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F * @return the normalized y-axis position of the bubble stack rounded to 4 decimal places. */ public float getNormalizedYPosition() { - return new BigDecimal(getPosition().y / mDisplaySize.y) + return new BigDecimal(getStackPosition().y / mDisplaySize.y) .setScale(4, RoundingMode.CEILING.HALF_UP) .floatValue(); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java index 7d3c0f85c0f7..165eb1dda077 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java @@ -34,17 +34,16 @@ import com.android.systemui.pip.phone.PipDismissViewController; * dismissing, and flings. */ class BubbleTouchHandler implements View.OnTouchListener { + /** Velocity required to dismiss a bubble without dragging it into the dismiss target. */ + private static final float DISMISS_MIN_VELOCITY = 4000f; + + private final PointF mTouchDown = new PointF(); + private final PointF mViewPositionOnTouchDown = new PointF(); + private final BubbleStackView mStack; private BubbleController mController = Dependency.get(BubbleController.class); private PipDismissViewController mDismissViewController; - // The position of the bubble on down event - private float mBubbleDownPosX; - private float mBubbleDownPosY; - // The touch position on down event - private float mDownX = -1; - private float mDownY = -1; - private boolean mMovedEnough; private int mTouchSlopSquared; private VelocityTracker mVelocityTracker; @@ -58,65 +57,42 @@ class BubbleTouchHandler implements View.OnTouchListener { } }; - // Bubble being dragged from the row of bubbles when the stack is expanded - private BubbleView mBubbleDraggingOut; - - /** - * Views movable by this touch handler should implement this interface. - */ - public interface FloatingView { - - /** - * Sets the position of the view. - */ - void setPosition(float x, float y); - - /** - * Sets the x position of the view. - */ - void setPositionX(float x); - - /** - * Sets the y position of the view. - */ - void setPositionY(float y); - - /** - * @return the position of the view. - */ - PointF getPosition(); - } + /** View that was initially touched, when we received the first ACTION_DOWN event. */ + private View mTouchedView; - public BubbleTouchHandler(Context context) { + BubbleTouchHandler(Context context, BubbleStackView stackView) { final int touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mTouchSlopSquared = touchSlop * touchSlop; mDismissViewController = new PipDismissViewController(context); + mStack = stackView; } @Override public boolean onTouch(View v, MotionEvent event) { - int action = event.getActionMasked(); - - BubbleStackView stack = (BubbleStackView) v; - View targetView = mBubbleDraggingOut != null - ? mBubbleDraggingOut - : stack.getTargetView(event); - boolean isFloating = targetView instanceof FloatingView; - if (!isFloating || targetView == null || action == MotionEvent.ACTION_OUTSIDE) { - stack.collapseStack(); + final int action = event.getActionMasked(); + + // If we aren't currently in the process of touching a view, figure out what we're touching. + // It'll be the stack, an individual bubble, or nothing. + if (mTouchedView == null) { + mTouchedView = mStack.getTargetView(event); + } + + // If this is an ACTION_OUTSIDE event, or the stack reported that we aren't touching + // anything, collapse the stack. + if (action == MotionEvent.ACTION_OUTSIDE || mTouchedView == null) { + mStack.collapseStack(); cleanUpDismissTarget(); - resetTouches(); + mTouchedView = null; return false; } - FloatingView floatingView = (FloatingView) targetView; - boolean isBubbleStack = floatingView instanceof BubbleStackView; + final boolean isStack = mStack.equals(mTouchedView); + final float rawX = event.getRawX(); + final float rawY = event.getRawY(); - PointF startPos = floatingView.getPosition(); - float rawX = event.getRawX(); - float rawY = event.getRawY(); - float x = mBubbleDownPosX + rawX - mDownX; - float y = mBubbleDownPosY + rawY - mDownY; + // The coordinates of the touch event, in terms of the touched view's position. + final float viewX = mViewPositionOnTouchDown.x + rawX - mTouchDown.x; + final float viewY = mViewPositionOnTouchDown.y + rawY - mTouchDown.y; switch (action) { case MotionEvent.ACTION_DOWN: trackMovement(event); @@ -124,87 +100,83 @@ class BubbleTouchHandler implements View.OnTouchListener { mDismissViewController.createDismissTarget(); mHandler.postDelayed(mShowDismissAffordance, SHOW_TARGET_DELAY); - mBubbleDownPosX = startPos.x; - mBubbleDownPosY = startPos.y; - mDownX = rawX; - mDownY = rawY; - mMovedEnough = false; + mTouchDown.set(rawX, rawY); - if (isBubbleStack) { - stack.onDragStart(); + if (isStack) { + mViewPositionOnTouchDown.set(mStack.getStackPosition()); + mStack.onDragStart(); } else { - stack.onBubbleDragStart((BubbleView) floatingView); + mViewPositionOnTouchDown.set( + mTouchedView.getTranslationX(), mTouchedView.getTranslationY()); + mStack.onBubbleDragStart(mTouchedView); } break; - case MotionEvent.ACTION_MOVE: trackMovement(event); + final float deltaX = rawX - mTouchDown.x; + final float deltaY = rawY - mTouchDown.y; - if (mBubbleDownPosX == -1 || mDownX == -1) { - mBubbleDownPosX = startPos.x; - mBubbleDownPosY = startPos.y; - mDownX = rawX; - mDownY = rawY; - } - final float deltaX = rawX - mDownX; - final float deltaY = rawY - mDownY; if ((deltaX * deltaX) + (deltaY * deltaY) > mTouchSlopSquared && !mMovedEnough) { mMovedEnough = true; } if (mMovedEnough) { - if (floatingView instanceof BubbleView) { - mBubbleDraggingOut = ((BubbleView) floatingView); - stack.onBubbleDragged(mBubbleDraggingOut, x, y); + if (isStack) { + mStack.onDragged(viewX, viewY); } else { - stack.onDragged(x, y); + mStack.onBubbleDragged(mTouchedView, viewX, viewY); } } + // TODO - when we're in the target stick to it / animate in some way? mInDismissTarget = mDismissViewController.updateTarget( - isBubbleStack ? stack.getBubbleAt(0) : (View) floatingView); + isStack ? mStack.getBubbleAt(0) : mTouchedView); break; case MotionEvent.ACTION_CANCEL: - resetTouches(); + mTouchedView = null; cleanUpDismissTarget(); break; case MotionEvent.ACTION_UP: trackMovement(event); - if (mInDismissTarget) { - if (isBubbleStack) { - mController.dismissStack(); - } else { - mController.removeBubble(((BubbleView) floatingView).getKey()); - } + if (mInDismissTarget && isStack) { + mController.dismissStack(); } else if (mMovedEnough) { mVelocityTracker.computeCurrentVelocity(1000); final float velX = mVelocityTracker.getXVelocity(); final float velY = mVelocityTracker.getYVelocity(); - if (isBubbleStack) { - stack.onDragFinish(x, y, velX, velY); + if (isStack) { + mStack.onDragFinish(viewX, viewY, velX, velY); } else { - stack.onBubbleDragFinish(mBubbleDraggingOut, x, y, velX, velY); + final boolean dismissed = mInDismissTarget || velY > DISMISS_MIN_VELOCITY; + mStack.onBubbleDragFinish( + mTouchedView, viewX, viewY, velX, velY, /* dismissed */ dismissed); + if (dismissed) { + mController.removeBubble(((BubbleView) mTouchedView).getKey()); + } } - } else if (floatingView.equals(stack.getExpandedBubbleView())) { - stack.collapseStack(); - } else if (isBubbleStack) { - if (stack.isExpanded()) { - stack.collapseStack(); + } else if (mTouchedView.equals(mStack.getExpandedBubbleView())) { + mStack.collapseStack(); + } else if (isStack) { + if (mStack.isExpanded()) { + mStack.collapseStack(); } else { - stack.expandStack(); + mStack.expandStack(); } } else { - stack.setExpandedBubble(((BubbleView) floatingView).getKey()); + mStack.setExpandedBubble(((BubbleView) mTouchedView).getKey()); } + cleanUpDismissTarget(); mVelocityTracker.recycle(); mVelocityTracker = null; - resetTouches(); + mTouchedView = null; + mMovedEnough = false; break; } + return true; } @@ -216,16 +188,6 @@ class BubbleTouchHandler implements View.OnTouchListener { mDismissViewController.destroyDismissTarget(); } - /** - * Resets anything we care about after a gesture is complete. - */ - private void resetTouches() { - mDownX = -1; - mDownY = -1; - mBubbleDownPosX = -1; - mBubbleDownPosY = -1; - mBubbleDraggingOut = null; - } private void trackMovement(MotionEvent event) { if (mVelocityTracker == null) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java index 1a4b19940e34..b409a3181e2b 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java @@ -20,7 +20,6 @@ import android.annotation.Nullable; import android.app.Notification; import android.content.Context; import android.graphics.Color; -import android.graphics.PointF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; @@ -39,7 +38,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow /** * A floating object on the screen that can post message updates. */ -public class BubbleView extends FrameLayout implements BubbleTouchHandler.FloatingView { +public class BubbleView extends FrameLayout { private static final String TAG = "BubbleView"; // Same value as Launcher3 badge code @@ -217,25 +216,4 @@ public class BubbleView extends FrameLayout implements BubbleTouchHandler.Floati // XXX: should we pull from the drawable, app icon, notif tint? return ColorUtils.blendARGB(defaultTint, Color.WHITE, WHITE_SCRIM_ALPHA); } - - @Override - public void setPosition(float x, float y) { - setPositionX(x); - setPositionY(y); - } - - @Override - public void setPositionX(float x) { - setTranslationX(x); - } - - @Override - public void setPositionY(float y) { - setTranslationY(y); - } - - @Override - public PointF getPosition() { - return new PointF(getTranslationX(), getTranslationY()); - } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java index 164406494250..9fd26b80e385 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java @@ -16,6 +16,7 @@ package com.android.systemui.bubbles.animation; +import android.content.res.Resources; import android.graphics.PointF; import android.view.View; import android.view.WindowInsets; @@ -43,6 +44,9 @@ public class ExpandedAnimationController */ private static final int ANIMATE_TRANSLATION_FACTOR = 4; + /** How much to scale down bubbles when they're animating in/out. */ + private static final float ANIMATE_SCALE_PERCENT = 0.5f; + /** * The stack position from which the bubbles were expanded. Saved in {@link #expandFromStack} * and used to return to stack form in {@link #collapseBackToStack}. @@ -55,16 +59,35 @@ public class ExpandedAnimationController private float mBubblePaddingPx; /** Size of each bubble. */ private float mBubbleSizePx; + /** Height of the status bar. */ + private float mStatusBarHeight; + + /** + * Whether the individual bubble has been dragged out of the row of bubbles far enough to cause + * the rest of the bubbles to animate to fill the gap. + */ + private boolean mBubbleDraggedOutEnough = false; + + /** The bubble currently being dragged out of the row (to potentially be dismissed). */ + private View mBubbleDraggingOut; + + /** + * Drag velocities for the dragging-out bubble when the drag finished. These are used by + * {@link #onChildRemoved} to animate out the bubble while respecting touch velocity. + */ + private float mBubbleDraggingOutVelX; + private float mBubbleDraggingOutVelY; @Override protected void setLayout(PhysicsAnimationLayout layout) { super.setLayout(layout); - mStackOffsetPx = layout.getResources().getDimensionPixelSize( - R.dimen.bubble_stack_offset); - mBubblePaddingPx = layout.getResources().getDimensionPixelSize( - R.dimen.bubble_padding); - mBubbleSizePx = layout.getResources().getDimensionPixelSize( - R.dimen.individual_bubble_size); + + final Resources res = layout.getResources(); + mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); + mBubblePaddingPx = res.getDimensionPixelSize(R.dimen.bubble_padding); + mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size); + mStatusBarHeight = + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); } /** @@ -98,12 +121,93 @@ public class ExpandedAnimationController runAfterTranslationsEnd(after); } + /** Prepares the given bubble to be dragged out. */ + public void prepareForBubbleDrag(View bubble) { + mLayout.cancelAnimationsOnView(bubble); + + mBubbleDraggingOut = bubble; + mBubbleDraggingOut.setTranslationZ(Short.MAX_VALUE); + } + + /** + * Drags an individual bubble to the given coordinates. Bubbles to the right will animate to + * take its place once it's dragged out of the row of bubbles, and animate out of the way if the + * bubble is dragged back into the row. + */ + public void dragBubbleOut(View bubbleView, float x, float y) { + bubbleView.setTranslationX(x); + bubbleView.setTranslationY(y); + + final boolean draggedOutEnough = + y > getExpandedY() + mBubbleSizePx || y < getExpandedY() - mBubbleSizePx; + if (draggedOutEnough != mBubbleDraggedOutEnough) { + animateStackByBubbleWidthsStartingFrom( + /* numBubbleWidths */ draggedOutEnough ? -1 : 0, + /* startIndex */ mLayout.indexOfChild(bubbleView) + 1); + mBubbleDraggedOutEnough = draggedOutEnough; + } + } + + /** + * Snaps a bubble back to its position within the bubble row, and animates the rest of the + * bubbles to accommodate it if it was previously dragged out past the threshold. + */ + public void snapBubbleBack(View bubbleView, float velX, float velY) { + final int index = mLayout.indexOfChild(bubbleView); + + // Snap the bubble back, respecting its current velocity. + mLayout.animateValueForChildAtIndex( + DynamicAnimation.TRANSLATION_X, index, getXForChildAtIndex(index), velX); + mLayout.animateValueForChildAtIndex( + DynamicAnimation.TRANSLATION_Y, index, getExpandedY(), velY); + mLayout.setEndListenerForProperties( + mLayout.new OneTimeMultiplePropertyEndListener() { + @Override + void onAllAnimationsForPropertiesEnd() { + // Reset Z translation once the bubble is done snapping back. + bubbleView.setTranslationZ(0f); + } + }, + DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); + + animateStackByBubbleWidthsStartingFrom( + /* numBubbleWidths */ 0, /* startIndex */ index + 1); + + mBubbleDraggingOut = null; + mBubbleDraggedOutEnough = false; + } + + /** + * Sets configuration variables so that when the given bubble is removed, the animations are + * started with the given velocities. + */ + public void prepareForDismissalWithVelocity(View bubbleView, float velX, float velY) { + mBubbleDraggingOut = bubbleView; + mBubbleDraggingOutVelX = velX; + mBubbleDraggingOutVelY = velY; + mBubbleDraggedOutEnough = false; + } + + /** + * Animates the bubbles, starting at the given index, to the left or right by the given number + * of bubble widths. Passing zero for numBubbleWidths will animate the bubbles to their normal + * positions. + */ + private void animateStackByBubbleWidthsStartingFrom(int numBubbleWidths, int startIndex) { + for (int i = startIndex; i < mLayout.getChildCount(); i++) { + mLayout.animateValueForChildAtIndex( + DynamicAnimation.TRANSLATION_X, + i, + getXForChildAtIndex(i + numBubbleWidths)); + } + } + /** The Y value of the row of expanded bubbles. */ private float getExpandedY() { final WindowInsets insets = mLayout.getRootWindowInsets(); if (insets != null) { return mBubblePaddingPx + Math.max( - insets.getSystemWindowInsetTop(), + mStatusBarHeight, insets.getDisplayCutout() != null ? insets.getDisplayCutout().getSafeInsetTop() : 0); @@ -161,12 +265,7 @@ public class ExpandedAnimationController child.setTranslationX(getXForChildAtIndex(index)); child.setTranslationY(getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR); mLayout.animateValueForChild(DynamicAnimation.TRANSLATION_Y, child, getExpandedY()); - - // Animate the remaining bubbles to the correct X position. - for (int i = index + 1; i < mLayout.getChildCount(); i++) { - mLayout.animateValueForChildAtIndex( - DynamicAnimation.TRANSLATION_X, i, getXForChildAtIndex(i)); - } + animateBubblesAfterIndexToCorrectX(index); } @Override @@ -175,16 +274,36 @@ public class ExpandedAnimationController // TODO: Reverse this when bubbles are at the bottom. mLayout.animateValueForChild( DynamicAnimation.ALPHA, child, 0f, finishRemoval); - mLayout.animateValueForChild( - DynamicAnimation.TRANSLATION_Y, - child, - getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR); - // Animate the remaining bubbles to the correct X position. - for (int i = index; i < mLayout.getChildCount(); i++) { - mLayout.animateValueForChildAtIndex( - DynamicAnimation.TRANSLATION_X, i, getXForChildAtIndex(i)); + // If we're removing the dragged-out bubble, that means it got dismissed. + if (child.equals(mBubbleDraggingOut)) { + // Throw it to the bottom of the screen, towards the center horizontally. + mLayout.animateValueForChild( + DynamicAnimation.TRANSLATION_X, + child, + mLayout.getWidth() / 2f - mBubbleSizePx / 2f, + mBubbleDraggingOutVelX); + mLayout.animateValueForChild( + DynamicAnimation.TRANSLATION_Y, + child, + mLayout.getHeight() + mBubbleSizePx, + mBubbleDraggingOutVelY); + + // Scale it down a bit so it looks like it's disappearing. + mLayout.animateValueForChild(DynamicAnimation.SCALE_X, child, ANIMATE_SCALE_PERCENT); + mLayout.animateValueForChild(DynamicAnimation.SCALE_Y, child, ANIMATE_SCALE_PERCENT); + + mBubbleDraggingOut = null; + } else { + // If we're removing some random bubble just throw it off the top. + mLayout.animateValueForChild( + DynamicAnimation.TRANSLATION_Y, + child, + getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR); } + + // Animate all the other bubbles to their new positions sans this bubble. + animateBubblesAfterIndexToCorrectX(index); } @Override @@ -203,6 +322,23 @@ public class ExpandedAnimationController () -> super.setChildVisibility(child, index, visibility)); } + /** + * Animates the bubbles after the given index to the X position they should be in according to + * {@link #getXForChildAtIndex}. + */ + private void animateBubblesAfterIndexToCorrectX(int start) { + for (int i = start; i < mLayout.getChildCount(); i++) { + final View bubble = mLayout.getChildAt(i); + + // Don't animate the dragging out bubble, or it'll jump around while being dragged. It + // will be snapped to the correct X value after the drag (if it's not dismissed). + if (!bubble.equals(mBubbleDraggingOut)) { + mLayout.animateValueForChild( + DynamicAnimation.TRANSLATION_X, bubble, getXForChildAtIndex(i)); + } + } + } + /** Returns the appropriate X translation value for a bubble at the given index. */ private float getXForChildAtIndex(int index) { return mBubblePaddingPx + (mBubbleSizePx + mBubblePaddingPx) * index; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java index a4ddbf752316..dfdcfc9f3b74 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java @@ -187,6 +187,20 @@ public class PhysicsAnimationLayout extends FrameLayout { } /** + * Sets an end listener that will be called whenever any of the given properties' animations + * end. For example, setting a listener for TRANSLATION_X and TRANSLATION_Y will result in that + * listener being called twice - once when all TRANSLATION_X animations end, and again when all + * TRANSLATION_Y animations end. + */ + public void setEndListenerForProperties( + DynamicAnimation.OnAnimationEndListener endListener, + DynamicAnimation.ViewProperty... properties) { + for (DynamicAnimation.ViewProperty property : properties) { + setEndListenerForProperty(endListener, property); + } + } + + /** * Removes the end listener that would have been called when all child animations for a given * property stopped running. */ @@ -197,7 +211,6 @@ public class PhysicsAnimationLayout extends FrameLayout { @Override public void addView(View child, int index, ViewGroup.LayoutParams params) { super.addView(child, index, params); - setChildrenVisibility(); // Set up animations for the new view, if the controller is set. If it isn't set, we'll be // setting up animations for all children when setController is called. @@ -208,6 +221,8 @@ public class PhysicsAnimationLayout extends FrameLayout { mController.onChildAdded(child, index); } + + setChildrenVisibility(); } @Override @@ -294,6 +309,13 @@ public class PhysicsAnimationLayout extends FrameLayout { } } + /** Cancels all of the physics animations running on the given view. */ + public void cancelAnimationsOnView(View view) { + for (DynamicAnimation.ViewProperty property : mController.getAnimatedProperties()) { + getAnimationFromView(property, view).cancel(); + } + } + /** * Animates the property of the given child view, then runs the callback provided when the * animation ends. @@ -318,6 +340,11 @@ public class PhysicsAnimationLayout extends FrameLayout { }); } + // Set the start velocity if it's something other than the not-set value. + if (startVel != Float.MAX_VALUE) { + animation.setStartVelocity(startVel); + } + animation.animateToFinalPosition(value); } } @@ -337,6 +364,14 @@ public class PhysicsAnimationLayout extends FrameLayout { animateValueForChild(property, view, value, Float.MAX_VALUE, /* after */ null); } + protected void animateValueForChild( + DynamicAnimation.ViewProperty property, + View view, + float value, + float startVel) { + animateValueForChild(property, view, value, startVel, /* after */ null); + } + /** * Animates the property of the child at the given index to the given value, then runs the * callback provided when the animation ends. @@ -414,7 +449,13 @@ public class PhysicsAnimationLayout extends FrameLayout { */ private SpringAnimation getAnimationAtIndex( DynamicAnimation.ViewProperty property, int index) { - return (SpringAnimation) getChildAt(index).getTag(getTagIdForProperty(property)); + return getAnimationFromView(property, getChildAt(index)); + } + + /** Retrieves the animation of the given property from the view via the view tag system. */ + private SpringAnimation getAnimationFromView( + DynamicAnimation.ViewProperty property, View view) { + return (SpringAnimation) view.getTag(getTagIdForProperty(property)); } /** Sets up SpringAnimations of the given property for each child view in the layout. */ @@ -528,4 +569,33 @@ public class PhysicsAnimationLayout extends FrameLayout { } } } + + /** + * One time end listener that waits for every animation on every given property to finish. At + * that point, it calls {@link #onAllAnimationsForPropertiesEnd} and then removes itself as an + * end listener from each property. + */ + public abstract class OneTimeMultiplePropertyEndListener + implements DynamicAnimation.OnAnimationEndListener { + final DynamicAnimation.ViewProperty[] mViewProperties; + + OneTimeMultiplePropertyEndListener(DynamicAnimation.ViewProperty... properties) { + mViewProperties = properties; + } + + @Override + public void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value, + float velocity) { + if (!arePropertiesAnimating(mViewProperties)) { + onAllAnimationsForPropertiesEnd(); + + for (DynamicAnimation.ViewProperty property : mViewProperties) { + removeEndListenerForProperty(property); + } + } + } + + /** Called when every animation for every property has finished. */ + abstract void onAllAnimationsForPropertiesEnd(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java index 0c089a75aece..7dfb21cf384f 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java @@ -16,15 +16,12 @@ package com.android.systemui.bubbles.animation; -import android.content.Context; import android.content.res.Resources; -import android.graphics.Point; import android.graphics.PointF; import android.graphics.RectF; import android.util.Log; import android.view.View; import android.view.WindowInsets; -import android.view.WindowManager; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.FlingAnimation; @@ -88,9 +85,8 @@ public class StackAnimationController extends private int mBubbleOffscreen; /** How far down the screen the stack starts, when there is no pre-existing location. */ private int mStackStartingVerticalOffset; - - private Point mDisplaySize; - private RectF mAllowableStackPositionRegion; + /** Height of the status bar. */ + private float mStatusBarHeight; @Override protected void setLayout(PhysicsAnimationLayout layout) { @@ -103,11 +99,8 @@ public class StackAnimationController extends mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen); mStackStartingVerticalOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_starting_offset_y); - - mDisplaySize = new Point(); - WindowManager wm = - (WindowManager) layout.getContext().getSystemService(Context.WINDOW_SERVICE); - wm.getDefaultDisplay().getSize(mDisplaySize); + mStatusBarHeight = + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); } /** @@ -203,10 +196,9 @@ public class StackAnimationController extends */ public RectF getAllowableStackPositionRegion() { final WindowInsets insets = mLayout.getRootWindowInsets(); - mAllowableStackPositionRegion = new RectF(); - + final RectF allowableRegion = new RectF(); if (insets != null) { - mAllowableStackPositionRegion.left = + allowableRegion.left = -mBubbleOffscreen - mBubblePadding + Math.max( @@ -214,7 +206,7 @@ public class StackAnimationController extends insets.getDisplayCutout() != null ? insets.getDisplayCutout().getSafeInsetLeft() : 0); - mAllowableStackPositionRegion.right = + allowableRegion.right = mLayout.getWidth() - mIndividualBubbleSize + mBubbleOffscreen @@ -222,17 +214,17 @@ public class StackAnimationController extends - Math.max( insets.getSystemWindowInsetRight(), insets.getDisplayCutout() != null - ? insets.getDisplayCutout().getSafeInsetRight() - : 0); + ? insets.getDisplayCutout().getSafeInsetRight() + : 0); - mAllowableStackPositionRegion.top = + allowableRegion.top = mBubblePadding + Math.max( - insets.getSystemWindowInsetTop(), + mStatusBarHeight, insets.getDisplayCutout() != null - ? insets.getDisplayCutout().getSafeInsetTop() - : 0); - mAllowableStackPositionRegion.bottom = + ? insets.getDisplayCutout().getSafeInsetTop() + : 0); + allowableRegion.bottom = mLayout.getHeight() - mIndividualBubbleSize - mBubblePadding @@ -243,7 +235,7 @@ public class StackAnimationController extends : 0); } - return mAllowableStackPositionRegion; + return allowableRegion; } @Override @@ -287,31 +279,14 @@ public class StackAnimationController extends @Override void onChildAdded(View child, int index) { - // If this is the first child added, position the stack in its starting position. if (mLayout.getChildCount() == 1) { - moveStackToStartPosition(); - } - - if (mLayout.indexOfChild(child) == 0) { - child.setTranslationY(mStackPosition.y); - - // Pop in the new bubble. - child.setScaleX(ANIMATE_IN_STARTING_SCALE); - child.setScaleY(ANIMATE_IN_STARTING_SCALE); - mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_X, 0, 1f); - mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_Y, 0, 1f); - - // Fade in the new bubble. - child.setAlpha(0); - mLayout.animateValueForChildAtIndex(DynamicAnimation.ALPHA, 0, 1f); - - // Start the new bubble 4x the normal offset distance in the opposite direction. We'll - // animate in from this position. Since the animations are chained, when the new bubble - // flies in from the side, it will push the other ones out of the way. - float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X); - child.setTranslationX(mStackPosition.x - ANIMATE_TRANSLATION_FACTOR * xOffset); - mLayout.animateValueForChildAtIndex( - DynamicAnimation.TRANSLATION_X, 0, mStackPosition.x); + // If this is the first child added, position the stack in its starting position before + // animating in. + moveStackToStartPosition(() -> animateInBubble(child)); + } else if (mLayout.indexOfChild(child) == 0) { + // Otherwise, animate the bubble in if it's the newest bubble. If we're adding a bubble + // to the back of the stack, it'll be largely invisible so don't bother animating it in. + animateInBubble(child); } } @@ -334,10 +309,14 @@ public class StackAnimationController extends } /** Moves the stack, without any animation, to the starting position. */ - private void moveStackToStartPosition() { - mLayout.post(() -> setStackPosition( - getAllowableStackPositionRegion().right, - getAllowableStackPositionRegion().top + mStackStartingVerticalOffset)); + private void moveStackToStartPosition(Runnable after) { + // Post to ensure that the layout's width and height have been calculated. + mLayout.post(() -> { + setStackPosition( + getAllowableStackPositionRegion().right, + getAllowableStackPositionRegion().top + mStackStartingVerticalOffset); + after.run(); + }); } /** @@ -379,6 +358,29 @@ public class StackAnimationController extends } } + /** Animates in the given bubble. */ + private void animateInBubble(View child) { + child.setTranslationY(mStackPosition.y); + + // Pop in the new bubble. + child.setScaleX(ANIMATE_IN_STARTING_SCALE); + child.setScaleY(ANIMATE_IN_STARTING_SCALE); + mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_X, 0, 1f); + mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_Y, 0, 1f); + + // Fade in the new bubble. + child.setAlpha(0); + mLayout.animateValueForChildAtIndex(DynamicAnimation.ALPHA, 0, 1f); + + // Start the new bubble 4x the normal offset distance in the opposite direction. We'll + // animate in from this position. Since the animations are chained, when the new bubble + // flies in from the side, it will push the other ones out of the way. + float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X); + child.setTranslationX(mStackPosition.x - ANIMATE_TRANSLATION_FACTOR * xOffset); + mLayout.animateValueForChildAtIndex( + DynamicAnimation.TRANSLATION_X, 0, mStackPosition.x); + } + /** * Springs the first bubble to the given final position, with the rest of the stack 'following'. */ diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 5efdc2f61d76..2d1dba6f79c8 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -131,7 +131,7 @@ public class DozeSensors { new PluginSensor( new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN), Settings.Secure.DOZE_WAKE_SCREEN_GESTURE, - mConfig.wakeScreenGestureAvailable(), + mConfig.wakeScreenGestureAvailable() && alwaysOn, DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, false /* reports touch coordinates */, false /* touchscreen */), diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index f5ac0d39d61f..7c9b2864f7f2 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -33,6 +33,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.UserInfo; +import android.content.res.Configuration; import android.database.ContentObserver; import android.graphics.Point; import android.graphics.drawable.Drawable; @@ -91,6 +92,7 @@ import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.EmergencyDialerConstants; +import com.android.systemui.util.leak.RotationUtils; import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator; import java.util.ArrayList; @@ -159,6 +161,8 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final ScreenshotHelper mScreenshotHelper; private final ScreenRecordHelper mScreenRecordHelper; + private int mLastRotation; + /** * @param context everything needs a context :( */ @@ -201,6 +205,8 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, mScreenshotHelper = new ScreenshotHelper(context); mScreenRecordHelper = new ScreenRecordHelper(context); + mLastRotation = RotationUtils.getRotation(mContext); + Dependency.get(ConfigurationController.class).addCallback(this); } @@ -426,6 +432,15 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, mContext.getTheme().applyStyle(mContext.getThemeResId(), true); } + @Override + public void onConfigChanged(Configuration newConfig) { + int rotation = RotationUtils.getRotation(mContext); + if (rotation != mLastRotation) { + mDialog.onRotate(); + } + mLastRotation = rotation; + } + public void destroy() { Dependency.get(ConfigurationController.class).removeCallback(this); } @@ -1091,7 +1106,7 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, } protected int getActionLayoutId(Context context) { - if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.GLOBAL_ACTIONS_GRID_ENABLED)) { + if (isGridEnabled(context)) { return com.android.systemui.R.layout.global_actions_grid_item; } return com.android.systemui.R.layout.global_actions_item; @@ -1465,7 +1480,7 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final Context mContext; private final MyAdapter mAdapter; - private final MultiListLayout mGlobalActionsLayout; + private MultiListLayout mGlobalActionsLayout; private final OnClickListener mClickListener; private final OnItemLongClickListener mLongClickListener; private final GradientDrawable mGradientDrawable; @@ -1505,8 +1520,13 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, window.setBackgroundDrawable(mGradientDrawable); window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY); + initializeLayout(); - setContentView(getGlobalActionsLayoutId(context)); + setTitle(R.string.global_actions); + } + + private void initializeLayout() { + setContentView(getGlobalActionsLayoutId(mContext)); mGlobalActionsLayout = (MultiListLayout) findViewById(com.android.systemui.R.id.global_actions_view); mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss()); @@ -1520,11 +1540,20 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, return true; } }); - setTitle(R.string.global_actions); + } + + public void onRotate() { + if (mShowing && isGridEnabled(mContext)) { + initializeLayout(); + updateList(); + } } private int getGlobalActionsLayoutId(Context context) { - if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.GLOBAL_ACTIONS_GRID_ENABLED)) { + if (isGridEnabled(context)) { + if (RotationUtils.getRotation(context) == RotationUtils.ROTATION_SEASCAPE) { + return com.android.systemui.R.layout.global_actions_grid_seascape; + } return com.android.systemui.R.layout.global_actions_grid; } return com.android.systemui.R.layout.global_actions_wrapped; @@ -1543,10 +1572,20 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, int separatedIndex = separatedActions.indexOf(action); ViewGroup parent; if (separatedIndex != -1) { - parent = mGlobalActionsLayout.getParentView(true, separatedIndex); + parent = mGlobalActionsLayout.getParentView(true, separatedIndex, false); } else { + boolean reverse = false; + + // If we're using the grid layout and we're in seascape, reverse the order + // of sublists to make sure they render in the correct positions, + // since we can't reverse vertical LinearLayouts through the layout xml. + + if (isGridEnabled(mContext) && RotationUtils.getRotation(mContext) + == RotationUtils.ROTATION_SEASCAPE) { + reverse = true; + } int listIndex = listActions.indexOf(action); - parent = mGlobalActionsLayout.getParentView(false, listIndex); + parent = mGlobalActionsLayout.getParentView(false, listIndex, reverse); } View v = mAdapter.getView(i, null, parent); final int pos = i; @@ -1665,4 +1704,11 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, mKeyguardShowing = keyguardShowing; } } + + /** + * Determines whether or not the Global Actions Dialog should use the newer grid-style layout. + */ + public static boolean isGridEnabled(Context context) { + return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.GLOBAL_ACTIONS_GRID_ENABLED); + } } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java index 0e49b5f3cd2a..1d042776efc9 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java @@ -83,11 +83,11 @@ public class GlobalActionsGridLayout extends MultiListLayout { } @Override - public ViewGroup getParentView(boolean separated, int index) { + public ViewGroup getParentView(boolean separated, int index, boolean reverseOrder) { if (separated) { return getSeparatedView(); } else { - return getListView().getParentView(index); + return getListView().getParentView(index, reverseOrder); } } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java index 37755155751f..d5dcd74c7ea8 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java @@ -28,16 +28,13 @@ import android.widget.LinearLayout; * * * Try to maintain a 'square' grid (equal number of columns and rows) based on the expected item * count. + * * Determine the position and parent of any item by its index and the total item count. * * Display and hide sub-lists as needed, depending on the expected item count. - * * Favor bias toward having more rows or columns depending on the orientation of the device - * (TODO(123344999): Implement this, currently always favors adding more rows.) - * * Change the orientation (horizontal vs. vertical) of the container and sub-lists to act as rows - * or columns depending on the orientation of the device. - * (TODO(123344999): Implement this, currently always columns.) * * While we could implement this behavior with a GridLayout, it would take significantly more * time and effort, and would require more substantial refactoring of the existing code in - * GlobalActionsDialog, since it would require manipulation of the child items themselves. + * GlobalActionsDialog, since it would require manipulation of layout properties on the child items + * themselves. * */ @@ -65,14 +62,25 @@ public class ListGridLayout extends LinearLayout { /** * Get the parent view associated with the item which should be placed at the given position. */ - public ViewGroup getParentView(int index) { - ViewGroup firstParent = (ViewGroup) getChildAt(0); + public ViewGroup getParentView(int index, boolean reverseSublists) { if (mRows == 0) { - return firstParent; + return null; } + int column = getParentViewIndex(index, reverseSublists); + return (ViewGroup) getChildAt(column); + } + + private int reverseSublistIndex(int index) { + return getChildCount() - (index + 1); + } + + private int getParentViewIndex(int index, boolean reverseSublists) { int column = (int) Math.floor(index / mRows); - ViewGroup parent = (ViewGroup) getChildAt(column); - return parent != null ? parent : firstParent; + int columnCount = getChildCount(); + if (reverseSublists) { + column = reverseSublistIndex(column); + } + return column; } /** diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLProgram.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLProgram.java new file mode 100644 index 000000000000..d03b00bcfc85 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLProgram.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.glwallpaper; + +import static android.opengl.GLES20.GL_FRAGMENT_SHADER; +import static android.opengl.GLES20.GL_VERTEX_SHADER; +import static android.opengl.GLES20.glAttachShader; +import static android.opengl.GLES20.glCompileShader; +import static android.opengl.GLES20.glCreateProgram; +import static android.opengl.GLES20.glCreateShader; +import static android.opengl.GLES20.glGetAttribLocation; +import static android.opengl.GLES20.glGetUniformLocation; +import static android.opengl.GLES20.glLinkProgram; +import static android.opengl.GLES20.glShaderSource; +import static android.opengl.GLES20.glUseProgram; + +import android.content.Context; +import android.content.res.Resources; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +/** + * This class takes charge of linking shader codes and then return a handle for OpenGL ES program. + */ +class ImageGLProgram { + private static final String TAG = ImageGLProgram.class.getSimpleName(); + + private Context mContext; + private int mProgramHandle; + + ImageGLProgram(Context context) { + mContext = context.getApplicationContext(); + } + + private int loadShaderProgram(int vertexId, int fragmentId) { + final String vertexSrc = getShaderResource(vertexId); + final String fragmentSrc = getShaderResource(fragmentId); + final int vertexHandle = getShaderHandle(GL_VERTEX_SHADER, vertexSrc); + final int fragmentHandle = getShaderHandle(GL_FRAGMENT_SHADER, fragmentSrc); + return getProgramHandle(vertexHandle, fragmentHandle); + } + + private String getShaderResource(int shaderId) { + Resources res = mContext.getResources(); + StringBuilder code = new StringBuilder(); + + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(res.openRawResource(shaderId)))) { + String nextLine; + while ((nextLine = reader.readLine()) != null) { + code.append(nextLine).append("\n"); + } + } catch (IOException | Resources.NotFoundException ex) { + Log.d(TAG, "Can not read the shader source", ex); + code = null; + } + + return code == null ? "" : code.toString(); + } + + private int getShaderHandle(int type, String src) { + final int shader = glCreateShader(type); + if (shader == 0) { + Log.d(TAG, "Create shader failed, type=" + type); + return 0; + } + glShaderSource(shader, src); + glCompileShader(shader); + return shader; + } + + private int getProgramHandle(int vertexHandle, int fragmentHandle) { + final int program = glCreateProgram(); + if (program == 0) { + Log.d(TAG, "Can not create OpenGL ES program"); + return 0; + } + + glAttachShader(program, vertexHandle); + glAttachShader(program, fragmentHandle); + glLinkProgram(program); + return program; + } + + boolean useGLProgram(int vertexResId, int fragmentResId) { + mProgramHandle = loadShaderProgram(vertexResId, fragmentResId); + glUseProgram(mProgramHandle); + return true; + } + + int getAttributeHandle(String name) { + return glGetAttribLocation(mProgramHandle, name); + } + + int getUniformHandle(String name) { + return glGetUniformLocation(mProgramHandle, name); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java new file mode 100644 index 000000000000..19d85b155cba --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java @@ -0,0 +1,259 @@ +/* + * 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.glwallpaper; + +import static android.opengl.GLES20.GL_FLOAT; +import static android.opengl.GLES20.GL_LINEAR; +import static android.opengl.GLES20.GL_TEXTURE0; +import static android.opengl.GLES20.GL_TEXTURE_2D; +import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER; +import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER; +import static android.opengl.GLES20.GL_TRIANGLES; +import static android.opengl.GLES20.glActiveTexture; +import static android.opengl.GLES20.glBindTexture; +import static android.opengl.GLES20.glDrawArrays; +import static android.opengl.GLES20.glEnableVertexAttribArray; +import static android.opengl.GLES20.glGenTextures; +import static android.opengl.GLES20.glTexParameteri; +import static android.opengl.GLES20.glUniform1i; +import static android.opengl.GLES20.glVertexAttribPointer; + +import android.graphics.Bitmap; +import android.opengl.GLUtils; +import android.os.Build; +import android.util.Log; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +/** + * This class takes charge of the geometry data like vertices and texture coordinates. + * It delivers these data to opengl runtime and triggers draw calls if necessary. + */ +class ImageGLWallpaper { + private static final String TAG = ImageGLWallpaper.class.getSimpleName(); + + static final String A_POSITION = "aPosition"; + static final String A_TEXTURE_COORDINATES = "aTextureCoordinates"; + static final String U_CENTER_REVEAL = "uCenterReveal"; + static final String U_REVEAL = "uReveal"; + static final String U_AOD2OPACITY = "uAod2Opacity"; + static final String U_TEXTURE = "uTexture"; + + private static final int HANDLE_UNDEFINED = -1; + private static final int POSITION_COMPONENT_COUNT = 2; + private static final int TEXTURE_COMPONENT_COUNT = 2; + private static final int BYTES_PER_FLOAT = 4; + + // Vertices to define the square with 2 triangles. + private static final float[] VERTICES = { + -1.0f, -1.0f, + +1.0f, -1.0f, + +1.0f, +1.0f, + +1.0f, +1.0f, + -1.0f, +1.0f, + -1.0f, -1.0f + }; + + // Texture coordinates that maps to vertices. + private static final float[] TEXTURES = { + 0f, 1f, + 1f, 1f, + 1f, 0f, + 1f, 0f, + 0f, 0f, + 0f, 1f + }; + + private final FloatBuffer mVertexBuffer; + private final FloatBuffer mTextureBuffer; + private final ImageGLProgram mProgram; + + private int mAttrPosition; + private int mAttrTextureCoordinates; + private int mUniAod2Opacity; + private int mUniCenterReveal; + private int mUniReveal; + private int mUniTexture; + private int mTextureId; + + ImageGLWallpaper(ImageGLProgram program) { + mProgram = program; + + // Create an float array in opengles runtime (native) and put vertex data. + mVertexBuffer = ByteBuffer.allocateDirect(VERTICES.length * BYTES_PER_FLOAT) + .order(ByteOrder.nativeOrder()) + .asFloatBuffer(); + mVertexBuffer.put(VERTICES); + mVertexBuffer.position(0); + + // Create an float array in opengles runtime (native) and put texture data. + mTextureBuffer = ByteBuffer.allocateDirect(TEXTURES.length * BYTES_PER_FLOAT) + .order(ByteOrder.nativeOrder()) + .asFloatBuffer(); + mTextureBuffer.put(TEXTURES); + mTextureBuffer.position(0); + } + + void setup() { + setupAttributes(); + setupUniforms(); + } + + private void setupAttributes() { + mAttrPosition = mProgram.getAttributeHandle(A_POSITION); + mVertexBuffer.position(0); + glVertexAttribPointer(mAttrPosition, POSITION_COMPONENT_COUNT, GL_FLOAT, + false, 0, mVertexBuffer); + glEnableVertexAttribArray(mAttrPosition); + + mAttrTextureCoordinates = mProgram.getAttributeHandle(A_TEXTURE_COORDINATES); + mTextureBuffer.position(0); + glVertexAttribPointer(mAttrTextureCoordinates, TEXTURE_COMPONENT_COUNT, GL_FLOAT, + false, 0, mTextureBuffer); + glEnableVertexAttribArray(mAttrTextureCoordinates); + } + + private void setupUniforms() { + mUniAod2Opacity = mProgram.getUniformHandle(U_AOD2OPACITY); + mUniCenterReveal = mProgram.getUniformHandle(U_CENTER_REVEAL); + mUniReveal = mProgram.getUniformHandle(U_REVEAL); + mUniTexture = mProgram.getUniformHandle(U_TEXTURE); + } + + int getHandle(String name) { + switch (name) { + case A_POSITION: + return mAttrPosition; + case A_TEXTURE_COORDINATES: + return mAttrTextureCoordinates; + case U_AOD2OPACITY: + return mUniAod2Opacity; + case U_CENTER_REVEAL: + return mUniCenterReveal; + case U_REVEAL: + return mUniReveal; + case U_TEXTURE: + return mUniTexture; + default: + return HANDLE_UNDEFINED; + } + } + + void draw() { + glDrawArrays(GL_TRIANGLES, 0, VERTICES.length / 2); + } + + void setupTexture(Bitmap bitmap) { + final int[] tids = new int[1]; + + if (bitmap == null) { + Log.w(TAG, "setupTexture: invalid bitmap"); + return; + } + + // Generate one texture object and store the id in tids[0]. + glGenTextures(1, tids, 0); + if (tids[0] == 0) { + Log.w(TAG, "setupTexture: glGenTextures() failed"); + return; + } + + // Bind a named texture to a texturing target. + glBindTexture(GL_TEXTURE_2D, tids[0]); + // Load the bitmap data and copy it over into the texture object that is currently bound. + GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0); + // Use bilinear texture filtering when minification. + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + // Use bilinear texture filtering when magnification. + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + mTextureId = tids[0]; + } + + void useTexture() { + // Set the active texture unit to texture unit 0. + glActiveTexture(GL_TEXTURE0); + // Bind the texture to this unit. + glBindTexture(GL_TEXTURE_2D, mTextureId); + // Let the texture sampler in fragment shader to read form this texture unit. + glUniform1i(mUniTexture, 0); + } + + void adjustTextureCoordinates(Bitmap bitmap, int surfaceWidth, int surfaceHeight, + float xOffset, float yOffset) { + if (bitmap == null) { + Log.d(TAG, "adjustTextureCoordinates: invalid bitmap"); + return; + } + + int bitmapWidth = bitmap.getWidth(); + int bitmapHeight = bitmap.getHeight(); + float ratioW = 1f; + float ratioH = 1f; + float rX = 0f; + float rY = 0f; + float[] coordinates = null; + + final boolean adjustWidth = bitmapWidth > surfaceWidth; + final boolean adjustHeight = bitmapHeight > surfaceHeight; + + if (adjustWidth || adjustHeight) { + coordinates = TEXTURES.clone(); + } + + if (adjustWidth) { + float x = (float) Math.round((bitmapWidth - surfaceWidth) * xOffset) / bitmapWidth; + ratioW = (float) surfaceWidth / bitmapWidth; + float referenceX = x + ratioW > 1f ? 1f - ratioW : x; + for (int i = 0; i < coordinates.length; i += 2) { + if (i == 2 || i == 4 || i == 6) { + coordinates[i] = Math.min(1f, referenceX + ratioW); + } else { + coordinates[i] = referenceX; + } + } + rX = referenceX; + } + + + if (adjustHeight) { + float y = (float) Math.round((bitmapHeight - surfaceHeight) * yOffset) / bitmapHeight; + ratioH = (float) surfaceHeight / bitmapHeight; + float referenceY = y + ratioH > 1f ? 1f - ratioH : y; + for (int i = 1; i < coordinates.length; i += 2) { + if (i == 1 || i == 3 || i == 11) { + coordinates[i] = Math.min(1f, referenceY + ratioH); + } else { + coordinates[i] = referenceY; + } + } + rY = referenceY; + } + + if (adjustWidth || adjustHeight) { + if (Build.IS_DEBUGGABLE) { + Log.d(TAG, "adjustTextureCoordinates: sW=" + surfaceWidth + ", sH=" + surfaceHeight + + ", bW=" + bitmapWidth + ", bH=" + bitmapHeight + + ", rW=" + ratioW + ", rH=" + ratioH + ", rX=" + rX + ", rY=" + rY); + } + mTextureBuffer.put(coordinates); + mTextureBuffer.position(0); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java new file mode 100644 index 000000000000..477e7d7ebf72 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java @@ -0,0 +1,144 @@ +/* + * 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.glwallpaper; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.os.AsyncTask; +import android.os.Handler; +import android.os.Handler.Callback; +import android.os.Message; +import android.util.Log; + +/** + * A helper class that computes histogram and percentile 85 from a bitmap. + * Percentile 85 will be computed each time the user picks a new image wallpaper. + */ +class ImageProcessHelper { + private static final String TAG = ImageProcessHelper.class.getSimpleName(); + private static final float DEFAULT_PER85 = 0.8f; + private static final int MSG_UPDATE_PER85 = 1; + + /** + * This color matrix will be applied to each pixel to get luminance from rgb by below formula: + * Luminance = .2126f * r + .7152f * g + .0722f * b. + */ + private static final float[] LUMINOSITY_MATRIX = new float[] { + .2126f, .0000f, .0000f, .0000f, .0000f, + .0000f, .7152f, .0000f, .0000f, .0000f, + .0000f, .0000f, .0722f, .0000f, .0000f, + .0000f, .0000f, .0000f, 1.000f, .0000f + }; + + private final Handler mHandler = new Handler(new Callback() { + @Override + public boolean handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE_PER85: + mPer85 = (float) msg.obj; + return true; + default: + return false; + } + } + }); + + private float mPer85 = DEFAULT_PER85; + + void startComputingPercentile85(Bitmap bitmap) { + new Per85ComputeTask(mHandler).execute(bitmap); + } + + float getPercentile85() { + return mPer85; + } + + private static class Per85ComputeTask extends AsyncTask<Bitmap, Void, Float> { + private Handler mUpdateHandler; + + Per85ComputeTask(Handler handler) { + super(handler); + mUpdateHandler = handler; + } + + @Override + protected Float doInBackground(Bitmap... bitmaps) { + Bitmap bitmap = bitmaps[0]; + if (bitmap != null) { + int[] histogram = processHistogram(bitmap); + return computePercentile85(bitmap, histogram); + } + Log.e(TAG, "Per85ComputeTask: Can't get bitmap"); + return DEFAULT_PER85; + } + + @Override + protected void onPostExecute(Float result) { + Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_PER85, result); + mUpdateHandler.sendMessage(msg); + } + + private int[] processHistogram(Bitmap bitmap) { + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + Bitmap target = Bitmap.createBitmap(width, height, bitmap.getConfig()); + Canvas canvas = new Canvas(target); + ColorMatrix cm = new ColorMatrix(LUMINOSITY_MATRIX); + Paint paint = new Paint(); + paint.setColorFilter(new ColorMatrixColorFilter(cm)); + canvas.drawBitmap(bitmap, new Matrix(), paint); + + // TODO: Fine tune the performance here, tracking on b/123615079. + int[] histogram = new int[256]; + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + int pixel = target.getPixel(col, row); + int y = Color.red(pixel) + Color.green(pixel) + Color.blue(pixel); + histogram[y]++; + } + } + + return histogram; + } + + private float computePercentile85(Bitmap bitmap, int[] histogram) { + float per85 = DEFAULT_PER85; + int pixelCount = bitmap.getWidth() * bitmap.getHeight(); + float[] acc = new float[256]; + for (int i = 0; i < acc.length; i++) { + acc[i] = (float) histogram[i] / pixelCount; + float prev = i == 0 ? 0f : acc[i - 1]; + float next = acc[i]; + float idx = (float) (i + 1) / 255; + float sum = prev + next; + if (prev < 0.85f && sum >= 0.85f) { + per85 = idx; + } + if (i > 0) { + acc[i] += acc[i - 1]; + } + } + return per85; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java new file mode 100644 index 000000000000..5914236ab349 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java @@ -0,0 +1,97 @@ +/* + * 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.glwallpaper; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; + +import com.android.systemui.Interpolators; + +/** + * Use ValueAnimator and appropriate interpolator to control the progress of reveal transition. + * The transition will happen while getting awake and quit events. + */ +class ImageRevealHelper { + private static final String TAG = ImageRevealHelper.class.getSimpleName(); + private static final float MAX_REVEAL = 0f; + private static final float MIN_REVEAL = 1f; + + private final ValueAnimator mAnimator; + private final RevealStateListener mRevealListener; + private float mReveal = MAX_REVEAL; + private boolean mAwake = false; + + ImageRevealHelper(RevealStateListener listener) { + mRevealListener = listener; + mAnimator = ValueAnimator.ofFloat(); + mAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + mAnimator.addUpdateListener(animator -> { + mReveal = (float) animator.getAnimatedValue(); + if (mRevealListener != null) { + mRevealListener.onRevealStateChanged(); + } + }); + mAnimator.addListener(new AnimatorListenerAdapter() { + private boolean mIsCanceled; + + @Override + public void onAnimationCancel(Animator animation) { + mIsCanceled = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + if (!mIsCanceled) { + mAwake = !mAwake; + } + mIsCanceled = false; + } + }); + } + + private void animate() { + mAnimator.cancel(); + mAnimator.setFloatValues(mReveal, !mAwake ? MIN_REVEAL : MAX_REVEAL); + mAnimator.start(); + } + + public float getReveal() { + return mReveal; + } + + public boolean isAwake() { + return mAwake; + } + + void updateAwake(boolean awake, long duration) { + mAwake = awake; + mAnimator.setDuration(duration); + animate(); + } + + /** + * A listener to trace value changes of reveal. + */ + public interface RevealStateListener { + + /** + * Called back while reveal status changes. + */ + void onRevealStateChanged(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java new file mode 100644 index 000000000000..991b1161dde2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.glwallpaper; + +import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT; +import static android.opengl.GLES20.glClear; +import static android.opengl.GLES20.glClearColor; +import static android.opengl.GLES20.glUniform1f; +import static android.opengl.GLES20.glViewport; + +import android.app.WallpaperManager; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.opengl.GLSurfaceView; +import android.os.Build; +import android.util.Log; + +import com.android.systemui.ImageWallpaper; +import com.android.systemui.ImageWallpaper.ImageGLView; +import com.android.systemui.R; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +/** + * A GL renderer for image wallpaper. + */ +public class ImageWallpaperRenderer implements GLSurfaceView.Renderer, + ImageWallpaper.WallpaperStatusListener, ImageRevealHelper.RevealStateListener { + private static final String TAG = ImageWallpaperRenderer.class.getSimpleName(); + + private final WallpaperManager mWallpaperManager; + private final ImageGLProgram mProgram; + private final ImageGLWallpaper mWallpaper; + private final ImageProcessHelper mImageProcessHelper; + private final ImageRevealHelper mImageRevealHelper; + private final ImageGLView mGLView; + private float mXOffset = 0f; + private float mYOffset = 0f; + + public ImageWallpaperRenderer(Context context, ImageGLView glView) { + mWallpaperManager = context.getSystemService(WallpaperManager.class); + if (mWallpaperManager == null) { + Log.w(TAG, "WallpaperManager not available"); + } + + mProgram = new ImageGLProgram(context); + mWallpaper = new ImageGLWallpaper(mProgram); + mImageProcessHelper = new ImageProcessHelper(); + mImageRevealHelper = new ImageRevealHelper(this); + mGLView = glView; + + if (mWallpaperManager != null) { + // Compute per85 as transition threshold, this is an async work. + mImageProcessHelper.startComputingPercentile85(mWallpaperManager.getBitmap()); + } + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + glClearColor(0f, 0f, 0f, 1.0f); + mProgram.useGLProgram( + R.raw.image_wallpaper_vertex_shader, R.raw.image_wallpaper_fragment_shader); + mWallpaper.setup(); + mWallpaper.setupTexture(mWallpaperManager.getBitmap()); + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + glViewport(0, 0, width, height); + if (Build.IS_DEBUGGABLE) { + Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height + + ", xOffset=" + mXOffset + ", yOffset=" + mYOffset); + } + mWallpaper.adjustTextureCoordinates(mWallpaperManager.getBitmap(), + width, height, mXOffset, mYOffset); + } + + @Override + public void onDrawFrame(GL10 gl) { + float threshold = mImageProcessHelper.getPercentile85(); + float reveal = mImageRevealHelper.getReveal(); + + glClear(GL_COLOR_BUFFER_BIT); + + glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_AOD2OPACITY), 1); + glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_CENTER_REVEAL), threshold); + glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_REVEAL), reveal); + + mWallpaper.useTexture(); + mWallpaper.draw(); + } + + @Override + public void onAmbientModeChanged(boolean inAmbientMode, long duration) { + mImageRevealHelper.updateAwake(!inAmbientMode, duration); + requestRender(); + } + + @Override + public void onOffsetsChanged(float xOffset, float yOffset, Rect frame) { + if (frame == null || mWallpaperManager == null + || (xOffset == mXOffset && yOffset == mYOffset)) { + return; + } + + Bitmap bitmap = mWallpaperManager.getBitmap(); + if (bitmap == null) { + return; + } + + int width = frame.width(); + int height = frame.height(); + mXOffset = xOffset; + mYOffset = yOffset; + + if (Build.IS_DEBUGGABLE) { + Log.d(TAG, "onOffsetsChanged: width=" + width + ", height=" + height + + ", xOffset=" + mXOffset + ", yOffset=" + mYOffset); + } + mWallpaper.adjustTextureCoordinates(bitmap, width, height, mXOffset, mYOffset); + requestRender(); + } + + @Override + public void onRevealStateChanged() { + requestRender(); + } + + private void requestRender() { + if (mGLView != null) { + mGLView.render(); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt index 1765dc866c66..15dc43f041f4 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt @@ -16,7 +16,6 @@ package com.android.systemui.privacy import android.content.Context import android.util.AttributeSet -import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.LinearLayout @@ -40,10 +39,12 @@ class OngoingPrivacyChip @JvmOverloads constructor( context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size) private val iconColor = context.resources.getColor( R.color.status_bar_clock_color, context.theme) + private val sidePadding = + context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding) private val backgroundDrawable = context.getDrawable(R.drawable.privacy_chip_bg) private lateinit var text: TextView private lateinit var iconsContainer: LinearLayout - private lateinit var inUseText: TextView + private lateinit var back: LinearLayout var expanded = false set(value) { if (value != field) { @@ -64,15 +65,15 @@ class OngoingPrivacyChip @JvmOverloads constructor( override fun onFinishInflate() { super.onFinishInflate() - inUseText = findViewById(R.id.in_use_text) + back = findViewById(R.id.background) text = findViewById(R.id.text_container) iconsContainer = findViewById(R.id.icons_container) } // Should only be called if the builder icons or app changed private fun updateView() { - inUseText.visibility = if (expanded) View.GONE else View.VISIBLE - background = if (expanded) backgroundDrawable else null + back.background = if (expanded) backgroundDrawable else null + back.setPaddingRelative(0, 0, if (expanded) sidePadding else 0, 0) fun setIcons(dialogBuilder: PrivacyDialogBuilder, iconsContainer: ViewGroup) { iconsContainer.removeAllViews() dialogBuilder.generateIcons().forEachIndexed { i, it -> diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt index f7ca51d6f840..a6e48f8835c7 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt @@ -62,4 +62,6 @@ data class PrivacyApplication(val packageName: String, val uid: Int, val context context.packageManager.getApplicationLabel(it) as String } ?: packageName } + + override fun toString() = "PrivacyApplication(packageName=$packageName, uid=$uid)" } diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt index 0f3393708bcd..625eacd7e2a7 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt @@ -31,6 +31,9 @@ import com.android.systemui.Dependency.MAIN_HANDLER_NAME import com.android.systemui.R import com.android.systemui.appops.AppOpItem import com.android.systemui.appops.AppOpsController +import com.android.systemui.Dumpable +import java.io.FileDescriptor +import java.io.PrintWriter import java.lang.ref.WeakReference import javax.inject.Inject import javax.inject.Named @@ -42,7 +45,7 @@ class PrivacyItemController @Inject constructor( private val appOpsController: AppOpsController, @Named(MAIN_HANDLER_NAME) private val uiHandler: Handler, @Named(BG_HANDLER_NAME) private val bgHandler: Handler -) { +) : Dumpable { companion object { val OPS = intArrayOf(AppOpsManager.OP_CAMERA, @@ -56,7 +59,10 @@ class PrivacyItemController @Inject constructor( const val SYSTEM_UID = 1000 } - private var privacyList = emptyList<PrivacyItem>() + @VisibleForTesting + internal var privacyList = emptyList<PrivacyItem>() + get() = field.toList() // Provides a shallow copy of the list + private val userManager = context.getSystemService(UserManager::class.java) private var currentUserIds = emptyList<Int>() private var listening = false @@ -189,4 +195,22 @@ class PrivacyItemController @Inject constructor( callback?.privacyChanged(list) } } + + override fun dump(fd: FileDescriptor?, pw: PrintWriter?, args: Array<out String>?) { + pw?.println("PrivacyItemController state:") + pw?.println(" Listening: $listening") + pw?.println(" Current user ids: $currentUserIds") + pw?.println(" Privacy Items:") + privacyList.forEach { + pw?.print(" ") + pw?.println(it.toString()) + } + pw?.println(" Callbacks:") + callbacks.forEach { + it.get()?.let { + pw?.print(" ") + pw?.println(it.toString()) + } + } + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index c0f87cb37e50..6a8c19ad0e77 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -81,6 +81,7 @@ import com.android.systemui.statusbar.policy.DateView; import com.android.systemui.statusbar.policy.NextAlarmController; import com.android.systemui.statusbar.policy.ZenModeController; +import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Objects; @@ -200,6 +201,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons); mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons); StatusIconContainer iconContainer = findViewById(R.id.statusIcons); + // Ignore privacy icons because they show in the space above QQS + iconContainer.addIgnoredSlots(getIgnoredIconSlots()); iconContainer.setShouldRestrictIcons(false); mIconManager = new TintedIconManager(iconContainer); @@ -241,6 +244,18 @@ public class QuickStatusBarHeader extends RelativeLayout implements updateShowPercent(); } + private List<String> getIgnoredIconSlots() { + ArrayList<String> ignored = new ArrayList<>(); + ignored.add(mContext.getResources().getString( + com.android.internal.R.string.status_bar_camera)); + ignored.add(mContext.getResources().getString( + com.android.internal.R.string.status_bar_microphone)); + ignored.add(mContext.getResources().getString( + com.android.internal.R.string.status_bar_location)); + + return ignored; + } + private void updateStatusText() { boolean changed = updateRingerStatus() || updateAlarmStatus(); @@ -372,15 +387,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements setLayoutParams(lp); - if (mPrivacyChip != null) { - MarginLayoutParams lm = (MarginLayoutParams) mPrivacyChip.getLayoutParams(); - int sideMargins = lm.leftMargin; - int topBottomMargins = resources.getDimensionPixelSize( - R.dimen.ongoing_appops_top_chip_margin); - lm.setMargins(sideMargins, topBottomMargins, sideMargins, topBottomMargins); - mPrivacyChip.setLayoutParams(lm); - } - updateStatusIconAlphaAnimator(); updateHeaderTextContainerAlphaAnimator(); updatePrivacyChipAlphaAnimator(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java index 2f1963057ad4..a6af82a28bce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java @@ -28,7 +28,7 @@ import android.view.accessibility.AccessibilityEvent; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; +import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; import java.util.stream.Stream; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java index a3beb96780d3..57d058880118 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java @@ -16,7 +16,7 @@ package com.android.systemui.statusbar; -import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT; +import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT; import android.annotation.NonNull; import android.content.Context; @@ -26,7 +26,7 @@ import android.util.ArraySet; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; +import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; import javax.inject.Inject; import javax.inject.Singleton; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt index 494473242a90..52b8cc2b685b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt @@ -49,7 +49,7 @@ class MediaArtworkProcessor @Inject constructor() { context.display.getSize(mTmpSize) val renderScript = RenderScript.create(context) - val rect = Rect(0, 0,artwork.width, artwork.height) + val rect = Rect(0, 0, artwork.width, artwork.height) MathUtils.fitRect(rect, Math.max(mTmpSize.x / DOWNSAMPLE, mTmpSize.y / DOWNSAMPLE)) val inBitmap = Bitmap.createScaledBitmap(artwork, rect.width(), rect.height(), true /* filter */) @@ -67,6 +67,7 @@ class MediaArtworkProcessor @Inject constructor() { input.destroy() output.destroy() inBitmap.recycle() + blur.destroy() val canvas = Canvas(outBitmap) canvas.drawColor(ColorUtils.setAlphaComponent(color, COLOR_ALPHA)) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 662cf514b977..ee5ac7c602aa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -24,6 +24,7 @@ import android.view.View; import android.view.ViewGroup; import com.android.systemui.R; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -61,7 +62,7 @@ public class NotificationViewHierarchyManager { protected final NotificationLockscreenUserManager mLockscreenUserManager; protected final NotificationGroupManager mGroupManager; protected final VisualStabilityManager mVisualStabilityManager; - private final StatusBarStateControllerImpl mStatusBarStateController; + private final SysuiStatusBarStateController mStatusBarStateController; private final NotificationEntryManager mEntryManager; // Lazy @@ -82,13 +83,13 @@ public class NotificationViewHierarchyManager { NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationGroupManager groupManager, VisualStabilityManager visualStabilityManager, - StatusBarStateControllerImpl statusBarStateController, + StatusBarStateController statusBarStateController, NotificationEntryManager notificationEntryManager, Lazy<ShadeController> shadeController) { mLockscreenUserManager = notificationLockscreenUserManager; mGroupManager = groupManager; mVisualStabilityManager = visualStabilityManager; - mStatusBarStateController = statusBarStateController; + mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController; mEntryManager = notificationEntryManager; mShadeController = shadeController; Resources res = context.getResources(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java index 5605f3db90fb..f6d3cdfddb31 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.notification; import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY; -import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT; -import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP; +import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT; +import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP; import android.app.Notification; import android.service.notification.StatusBarNotification; @@ -30,7 +30,7 @@ import com.android.systemui.statusbar.AmbientPulseManager; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.row.NotificationInflater; +import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -102,8 +102,7 @@ public class NotificationAlertingManager { * @param entry entry to add * @param inflatedFlags flags representing content views that were inflated */ - private void showAlertingView(NotificationEntry entry, - @NotificationInflater.InflationFlag int inflatedFlags) { + private void showAlertingView(NotificationEntry entry, @InflationFlag int inflatedFlags) { if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) { // Possible for shouldHeadsUp to change between the inflation starting and ending. // If it does and we no longer need to heads up, we should free the view. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java index 1ed671fdb6fb..a5a6d87c2030 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java @@ -20,7 +20,7 @@ import android.service.notification.StatusBarNotification; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.row.NotificationInflater; +import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; /** * Listener interface for changes sent by NotificationEntryManager. @@ -65,8 +65,7 @@ public interface NotificationEntryListener { /** * Called when a notification's views are inflated for the first time. */ - default void onEntryInflated(NotificationEntry entry, - @NotificationInflater.InflationFlag int inflatedFlags) { + default void onEntryInflated(NotificationEntry entry, @InflationFlag int inflatedFlags) { } /** 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 56922be73bec..3fbc64163759 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -36,8 +36,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationData; import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.logging.NotificationLogger; -import com.android.systemui.statusbar.notification.row.NotificationInflater; -import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; +import com.android.systemui.statusbar.notification.row.NotificationContentInflater; +import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.leak.LeakDetector; @@ -56,7 +56,7 @@ import java.util.Map; */ public class NotificationEntryManager implements Dumpable, - NotificationInflater.InflationCallback, + NotificationContentInflater.InflationCallback, NotificationUpdateHandler, VisualStabilityManager.Callback { private static final String TAG = "NotificationEntryMgr"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java index f1bb0d77deaa..6f5baf9faf39 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java @@ -19,8 +19,8 @@ package com.android.systemui.statusbar.notification; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT; -import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT; -import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP; +import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT; +import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP; import android.annotation.Nullable; import android.content.Context; @@ -42,8 +42,8 @@ import com.android.systemui.statusbar.NotificationUiAdjustment; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.row.NotificationContentInflater; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; -import com.android.systemui.statusbar.notification.row.NotificationInflater; import com.android.systemui.statusbar.notification.row.RowInflaterTask; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.NotificationGroupManager; @@ -78,7 +78,7 @@ public class NotificationRowBinder { private NotificationPresenter mPresenter; private NotificationListContainer mListContainer; private HeadsUpManager mHeadsUpManager; - private NotificationInflater.InflationCallback mInflationCallback; + private NotificationContentInflater.InflationCallback mInflationCallback; private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener; private BindRowCallback mBindRowCallback; private NotificationClicker mNotificationClicker; @@ -105,7 +105,7 @@ public class NotificationRowBinder { public void setUpWithPresenter(NotificationPresenter presenter, NotificationListContainer listContainer, HeadsUpManager headsUpManager, - NotificationInflater.InflationCallback inflationCallback, + NotificationContentInflater.InflationCallback inflationCallback, BindRowCallback bindRowCallback) { mPresenter = presenter; mListContainer = listContainer; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index f6d4ce22e905..3bf4d4beb4f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -54,8 +54,8 @@ import com.android.systemui.statusbar.InflationTask; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; import com.android.systemui.statusbar.notification.row.NotificationGuts; -import com.android.systemui.statusbar.notification.row.NotificationInflater; import java.util.ArrayList; import java.util.Collections; @@ -515,7 +515,7 @@ public final class NotificationEntry { if (row != null) row.resetUserExpansion(); } - public void freeContentViewWhenSafe(@NotificationInflater.InflationFlag int inflationFlag) { + public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) { if (row != null) row.freeContentViewWhenSafe(inflationFlag); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 8f7778b4a351..2b643d0a2fea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -17,13 +17,13 @@ package com.android.systemui.statusbar.notification.row; import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; +import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT; +import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP; +import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_PUBLIC; +import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationCallback; import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_AMBIENT; import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED; import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP; -import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT; -import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP; -import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC; -import static com.android.systemui.statusbar.notification.row.NotificationInflater.InflationCallback; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -89,7 +89,7 @@ import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.logging.NotificationCounters; -import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; +import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; import com.android.systemui.statusbar.notification.row.wrapper.NotificationMediaTemplateViewWrapper; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.statusbar.notification.stack.AmbientState; @@ -134,7 +134,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } private LayoutListener mLayoutListener; - private final NotificationInflater mNotificationInflater; + private final NotificationContentInflater mNotificationInflater; private int mIconTransformContentShift; private int mIconTransformContentShiftNoIcon; private int mMaxHeadsUpHeightBeforeN; @@ -1213,6 +1213,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView l.initView(); l.reInflateViews(); } + mStatusBarNotification.clearPackageContext(); mNotificationInflater.clearCachesAndReInflate(); onNotificationUpdated(); } @@ -1615,7 +1616,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @VisibleForTesting - public NotificationInflater getNotificationInflater() { + public NotificationContentInflater getNotificationInflater() { return mNotificationInflater; } @@ -1630,7 +1631,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView public ExpandableNotificationRow(Context context, AttributeSet attrs) { super(context, attrs); mFalsingManager = FalsingManager.getInstance(context); - mNotificationInflater = new NotificationInflater(this); + mNotificationInflater = new NotificationContentInflater(this); mMenuRow = new NotificationMenuRow(mContext); mImageResolver = new NotificationInlineImageResolver(context, new NotificationInlineImageCache()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 42ebfceca334..b34907dfebf1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -55,9 +55,9 @@ import java.util.concurrent.atomic.AtomicInteger; /** * A utility that inflates the right kind of contentView based on the state */ -public class NotificationInflater { +public class NotificationContentInflater { - public static final String TAG = "NotificationInflater"; + public static final String TAG = "NotifContentInflater"; @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, @@ -127,7 +127,7 @@ public class NotificationInflater { private boolean mRedactAmbient; private final ArrayMap<Integer, RemoteViews> mCachedContentViews = new ArrayMap<>(); - public NotificationInflater(ExpandableNotificationRow row) { + public NotificationContentInflater(ExpandableNotificationRow row) { mRow = row; } @@ -232,8 +232,7 @@ public class NotificationInflater { * will reinflate it. * * @param reInflateFlags flags which views should be inflated. Should be a subset of - * {@link NotificationInflater#mInflationFlags} as only those will be - * inflated/reinflated. + * {@link #mInflationFlags} as only those will be inflated/reinflated. */ private void inflateNotificationViews(@InflationFlag int reInflateFlags) { if (mRow.isRemoved()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java index a5411ecb4bd0..6eb376b58586 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java @@ -52,7 +52,7 @@ public class NotificationInlineImageCache implements NotificationInlineImageReso @Override public void preload(Uri uri) { PreloadImageTask newTask = new PreloadImageTask(mResolver); - newTask.executeOnExecutor(NotificationInflater.EXECUTOR, uri); + newTask.executeOnExecutor(NotificationContentInflater.EXECUTOR, uri); mCache.put(uri, newTask); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java index 4c06ff6f5e49..3808702176a9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java @@ -26,12 +26,15 @@ import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.os.Build; import android.view.NotificationHeaderView; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; +import com.android.internal.util.ContrastColorUtil; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.notification.TransformState; @@ -108,6 +111,11 @@ public abstract class NotificationViewWrapper implements TransformableView { return false; } + // Apps targeting Q should fix their dark mode bugs. + if (mRow.getEntry().targetSdk >= Build.VERSION_CODES.Q) { + return false; + } + int background = getBackgroundColor(view); if (background == Color.TRANSPARENT) { background = defaultBackgroundColor; @@ -138,17 +146,19 @@ public abstract class NotificationViewWrapper implements TransformableView { } } - private boolean childrenNeedInversion(@ColorInt int parentBackground, ViewGroup viewGroup) { + @VisibleForTesting + boolean childrenNeedInversion(@ColorInt int parentBackground, ViewGroup viewGroup) { if (viewGroup == null) { return false; } + int backgroundColor = getBackgroundColor(viewGroup); + if (Color.alpha(backgroundColor) != 255) { + backgroundColor = ContrastColorUtil.compositeColors(backgroundColor, parentBackground); + backgroundColor = ColorUtils.setAlphaComponent(backgroundColor, 255); + } for (int i = 0; i < viewGroup.getChildCount(); i++) { View child = viewGroup.getChildAt(i); - int backgroundColor = getBackgroundColor(viewGroup); - if (backgroundColor == Color.TRANSPARENT) { - backgroundColor = parentBackground; - } if (child instanceof TextView) { int foreground = ((TextView) child).getCurrentTextColor(); if (ColorUtils.calculateContrast(foreground, backgroundColor) < 3) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java index 6410860a852d..195d02d9ca72 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java @@ -191,7 +191,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } protected int adjustDisableFlags(int state) { - if (!mStatusBarComponent.isLaunchTransitionFadingAway() + if (!mKeyguardMonitor.isLaunchTransitionFadingAway() && !mKeyguardMonitor.isKeyguardFadingAway() && shouldHideNotificationIcons()) { state |= DISABLE_NOTIFICATION_ICONS; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java index dae4da7355c7..64209a7b9e73 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java @@ -16,9 +16,17 @@ package com.android.systemui.statusbar.phone; +import android.animation.ObjectAnimator; import android.annotation.NonNull; import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; import android.graphics.PixelFormat; +import android.util.FloatProperty; +import android.util.MathUtils; +import android.view.Gravity; +import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; @@ -27,6 +35,71 @@ import com.android.systemui.R; public class NavigationBarEdgePanel extends View { private static final String TAG = "NavigationBarEdgePanel"; + // TODO: read from resources once drawing is finalized. + private static final boolean SHOW_PROTECTION_STROKE = true; + private static final int PROTECTION_COLOR = 0xffc0c0c0; + private static final int STROKE_COLOR = 0xffe5e5e5; + private static final int PROTECTION_WIDTH_PX = 4; + private static final int BASE_EXTENT = 32; + private static final int ARROW_HEIGHT_DP = 32; + private static final int POINT_EXTENT_DP = 8; + private static final int ARROW_THICKNESS_DP = 4; + private static final float TRACK_LENGTH_MULTIPLIER = 1.5f; + private static final float START_POINTING_RATIO = 0.3f; + private static final float POINTEDNESS_BEFORE_SNAP_RATIO = 0.4f; + private static final int ANIM_DURATION_MS = 150; + + private final Paint mPaint = new Paint(); + private final Paint mProtectionPaint = new Paint(); + + private final ObjectAnimator mEndAnimator; + private final ObjectAnimator mLegAnimator; + + private final float mDensity; + private final float mBaseExtent; + private final float mPointExtent; + private final float mHeight; + private final float mStrokeThickness; + private final boolean mIsLeftPanel; + + private float mStartY; + private float mStartX; + + private boolean mGestureDetected; + private boolean mArrowsPointLeft; + private float mGestureLength; + private float mLegProgress; + private float mDragProgress; + + // How much the "legs" of the back arrow have proceeded from being a line to an arrow. + private static final FloatProperty<NavigationBarEdgePanel> LEG_PROGRESS = + new FloatProperty<NavigationBarEdgePanel>("legProgress") { + @Override + public void setValue(NavigationBarEdgePanel object, float value) { + object.setLegProgress(value); + } + + @Override + public Float get(NavigationBarEdgePanel object) { + return object.getLegProgress(); + } + }; + + // How far across the view the arrow should be drawn. + private static final FloatProperty<NavigationBarEdgePanel> DRAG_PROGRESS = + new FloatProperty<NavigationBarEdgePanel>("dragProgress") { + + @Override + public void setValue(NavigationBarEdgePanel object, float value) { + object.setDragProgress(value); + } + + @Override + public Float get(NavigationBarEdgePanel object) { + return object.getDragProgress(); + } + }; + public static NavigationBarEdgePanel create(@NonNull Context context, int width, int height, int gravity) { final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height, @@ -40,13 +113,43 @@ public class NavigationBarEdgePanel extends View { lp.setTitle(TAG + context.getDisplayId()); lp.accessibilityTitle = context.getString(R.string.nav_bar_edge_panel); lp.windowAnimations = 0; - NavigationBarEdgePanel panel = new NavigationBarEdgePanel(context); + NavigationBarEdgePanel panel = new NavigationBarEdgePanel( + context, (gravity & Gravity.LEFT) == Gravity.LEFT); panel.setLayoutParams(lp); return panel; } - private NavigationBarEdgePanel(Context context) { + private NavigationBarEdgePanel(Context context, boolean isLeftPanel) { super(context); + + mEndAnimator = ObjectAnimator.ofFloat(this, DRAG_PROGRESS, 1f); + mEndAnimator.setAutoCancel(true); + mEndAnimator.setDuration(ANIM_DURATION_MS); + + mLegAnimator = ObjectAnimator.ofFloat(this, LEG_PROGRESS, 1f); + mLegAnimator.setAutoCancel(true); + mLegAnimator.setDuration(ANIM_DURATION_MS); + + mDensity = context.getResources().getDisplayMetrics().density; + + mBaseExtent = dp(BASE_EXTENT); + mHeight = dp(ARROW_HEIGHT_DP); + mPointExtent = dp(POINT_EXTENT_DP); + mStrokeThickness = dp(ARROW_THICKNESS_DP); + + mPaint.setStrokeWidth(mStrokeThickness); + mPaint.setStrokeCap(Paint.Cap.ROUND); + mPaint.setColor(STROKE_COLOR); + mPaint.setAntiAlias(true); + + mProtectionPaint.setStrokeWidth(mStrokeThickness + PROTECTION_WIDTH_PX); + mProtectionPaint.setStrokeCap(Paint.Cap.ROUND); + mProtectionPaint.setColor(PROTECTION_COLOR); + mProtectionPaint.setAntiAlias(true); + + // Both panels arrow point the same way + mArrowsPointLeft = getLayoutDirection() == LAYOUT_DIRECTION_LTR; + mIsLeftPanel = isLeftPanel; } public void setWindowFlag(int flags, boolean enable) { @@ -62,6 +165,58 @@ public class NavigationBarEdgePanel extends View { updateLayout(lp); } + @Override + public boolean onTouchEvent(MotionEvent event) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN : { + show(event.getX(), event.getY()); + break; + } + case MotionEvent.ACTION_MOVE: { + handleNewSwipePoint(event.getX()); + break; + } + // Fall through + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: { + hide(); + break; + } + } + + return false; + } + + @Override + protected void onDraw(Canvas canvas) { + float edgeOffset = mBaseExtent * mDragProgress - mStrokeThickness; + float animatedOffset = mPointExtent * mLegProgress; + canvas.save(); + canvas.translate( + mIsLeftPanel ? edgeOffset : getWidth() - edgeOffset, + mStartY - mHeight * 0.5f); + + float outsideX = mArrowsPointLeft ? animatedOffset : 0; + float middleX = mArrowsPointLeft ? 0 : animatedOffset; + + if (SHOW_PROTECTION_STROKE) { + canvas.drawLine(outsideX, 0, middleX, mHeight * 0.5f, mProtectionPaint); + canvas.drawLine(middleX, mHeight * 0.5f, outsideX, mHeight, mProtectionPaint); + } + + canvas.drawLine(outsideX, 0, middleX, mHeight * 0.5f, mPaint); + canvas.drawLine(middleX, mHeight * 0.5f, outsideX, mHeight, mPaint); + canvas.restore(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + // TODO: read the gesture length from the nav controller. + mGestureLength = getWidth(); + } + public void setDimensions(int width, int height) { final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams(); if (lp.width != width || lp.height != height) { @@ -71,8 +226,81 @@ public class NavigationBarEdgePanel extends View { } } + private void setLegProgress(float progress) { + mLegProgress = progress; + invalidate(); + } + + private float getLegProgress() { + return mLegProgress; + } + + private void setDragProgress(float dragProgress) { + mDragProgress = dragProgress; + invalidate(); + } + + private float getDragProgress() { + return mDragProgress; + } + + private void hide() { + animate().alpha(0f).setDuration(ANIM_DURATION_MS); + } + + private void show(float x, float y) { + mEndAnimator.cancel(); + mLegAnimator.cancel(); + setLegProgress(0f); + setDragProgress(0f); + setAlpha(1f); + + float halfHeight = mHeight * 0.5f; + mStartY = MathUtils.constrain(y, halfHeight, getHeight() - halfHeight); + mStartX = x; + } + + private void handleNewSwipePoint(float x) { + float dist = MathUtils.abs(x - mStartX); + + setDragProgress(MathUtils.constrainedMap( + 0, 1.0f, + 0, mGestureLength * TRACK_LENGTH_MULTIPLIER, + dist)); + + if (dist < mGestureLength) { + float calculatedLegProgress = MathUtils.constrainedMap( + 0f, POINTEDNESS_BEFORE_SNAP_RATIO, + mGestureLength * START_POINTING_RATIO, mGestureLength, + dist); + + // Blend animated value with drag calculated value, allow the gesture to continue + // while the animation is playing with jump cuts in the animation. + setLegProgress(MathUtils.lerp(calculatedLegProgress, mLegProgress, mDragProgress)); + + if (mGestureDetected) { + mGestureDetected = false; + + mLegAnimator.setFloatValues(POINTEDNESS_BEFORE_SNAP_RATIO); + mLegAnimator.start(); + } + } else { + if (!mGestureDetected) { + performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK); + mGestureDetected = true; + + mLegAnimator.setFloatValues(1f); + mLegAnimator.start(); + } + } + } + private void updateLayout(WindowManager.LayoutParams lp) { WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); wm.updateViewLayout(this, lp); } + + private float dp(float dp) { + return mDensity * dp; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index ebd420478c0f..8152206aa2ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -1227,10 +1227,11 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav .getSystemService(Context.WINDOW_SERVICE); int width = mPrototypeController.getEdgeSensitivityWidth(); int height = mPrototypeController.getEdgeSensitivityHeight(); + // Explicitly left and right, not start and end as this is device relative. mLeftEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height, - Gravity.START | Gravity.BOTTOM); + Gravity.LEFT | Gravity.BOTTOM); mRightEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height, - Gravity.END | Gravity.BOTTOM); + Gravity.RIGHT | Gravity.BOTTOM); mLeftEdgePanel.setOnTouchListener(mEdgePanelTouchListener); mRightEdgePanel.setOnTouchListener(mEdgePanelTouchListener); wm.addView(mLeftEdgePanel, mLeftEdgePanel.getLayoutParams()); @@ -1252,15 +1253,12 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mButtonDispatchers.valueAt(i).onDestroy(); } - if (mPrototypeController.isEnabled()) { - WindowManager wm = (WindowManager) getContext() - .getSystemService(Context.WINDOW_SERVICE); - if (mLeftEdgePanel != null) { - wm.removeView(mLeftEdgePanel); - } - if (mRightEdgePanel != null) { - wm.removeView(mRightEdgePanel); - } + WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); + if (mLeftEdgePanel != null) { + wm.removeView(mLeftEdgePanel); + } + if (mRightEdgePanel != null) { + wm.removeView(mRightEdgePanel); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java index b613e8efa8ee..4dbd8545efb9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java @@ -34,8 +34,8 @@ import com.android.systemui.statusbar.InflationTask; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.row.NotificationInflater.AsyncInflationTask; -import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; +import com.android.systemui.statusbar.notification.row.NotificationContentInflater.AsyncInflationTask; +import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup; import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener; import com.android.systemui.statusbar.policy.HeadsUpManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index 62f85fe8a00e..99269cf17141 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -24,6 +24,7 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationListener; +import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -47,6 +48,7 @@ public class NotificationIconAreaController implements DarkReceiver, private final NotificationEntryManager mEntryManager; private final Runnable mUpdateStatusBarIcons = this::updateStatusBarIcons; private final StatusBarStateController mStatusBarStateController; + private final NotificationMediaManager mMediaManager; @VisibleForTesting final NotificationListener.NotificationSettingsListener mSettingsListener = new NotificationListener.NotificationSettingsListener() { @@ -93,13 +95,15 @@ public class NotificationIconAreaController implements DarkReceiver, public NotificationIconAreaController(Context context, StatusBar statusBar, StatusBarStateController statusBarStateController, - NotificationListener notificationListener) { + NotificationListener notificationListener, + NotificationMediaManager notificationMediaManager) { mStatusBar = statusBar; mContrastColorUtil = ContrastColorUtil.getInstance(context); mContext = context; mEntryManager = Dependency.get(NotificationEntryManager.class); mStatusBarStateController = statusBarStateController; mStatusBarStateController.addCallback(this); + mMediaManager = notificationMediaManager; notificationListener.addNotificationSettingsListener(mSettingsListener); initializeNotificationAreaViews(context); @@ -192,10 +196,13 @@ public class NotificationIconAreaController implements DarkReceiver, protected boolean shouldShowNotificationIcon(NotificationEntry entry, boolean showAmbient, boolean showLowPriority, boolean hideDismissed, - boolean hideRepliedMessages) { + boolean hideRepliedMessages, boolean hideCurrentMedia) { if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) { return false; } + if (hideCurrentMedia && entry.key.equals(mMediaManager.getMediaNotificationKey())) { + return false; + } if (!showLowPriority && !entry.isHighPriority()) { return false; } @@ -235,14 +242,16 @@ public class NotificationIconAreaController implements DarkReceiver, private void updateShelfIcons() { updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons, true /* showAmbient */, !mFullyDark /* showLowPriority */, - false /* hideDismissed */, mFullyDark /* hideRepliedMessages */); + false /* hideDismissed */, mFullyDark /* hideRepliedMessages */, + mFullyDark /* hideCurrentMedia */); } public void updateStatusBarIcons() { updateIconsForLayout(entry -> entry.icon, mNotificationIcons, false /* showAmbient */, mShowLowPriority /* showLowPriority */, true /* hideDismissed */, - true /* hideRepliedMessages */); + true /* hideRepliedMessages */, + false /* hideCurrentMedia */); } @VisibleForTesting @@ -261,7 +270,7 @@ public class NotificationIconAreaController implements DarkReceiver, */ private void updateIconsForLayout(Function<NotificationEntry, StatusBarIconView> function, NotificationIconContainer hostLayout, boolean showAmbient, boolean showLowPriority, - boolean hideDismissed, boolean hideRepliedMessages) { + boolean hideDismissed, boolean hideRepliedMessages, boolean hideCurrentMedia) { ArrayList<StatusBarIconView> toShow = new ArrayList<>( mNotificationScrollLayout.getChildCount()); @@ -271,7 +280,7 @@ public class NotificationIconAreaController implements DarkReceiver, if (view instanceof ExpandableNotificationRow) { NotificationEntry ent = ((ExpandableNotificationRow) view).getEntry(); if (shouldShowNotificationIcon(ent, showAmbient, showLowPriority, hideDismissed, - hideRepliedMessages)) { + hideRepliedMessages, hideCurrentMedia)) { toShow.add(function.apply(ent)); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 2799191a886f..e0c5e59b73f3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -92,6 +92,8 @@ import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.util.NotificationChannels; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.List; import java.util.Locale; @@ -793,6 +795,15 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, boolean showMicrophone = false; boolean showLocation = false; for (PrivacyItem item : items) { + if (item == null /* b/124234367 */) { + if (DEBUG) { + Log.e(TAG, "updatePrivacyItems - null item found"); + StringWriter out = new StringWriter(); + mPrivacyItemController.dump(null, new PrintWriter(out), null); + Log.e(TAG, out.toString()); + } + continue; + } switch (item.getPrivacyType()) { case TYPE_CAMERA: showCamera = true; 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 f980d074f6ef..008eeefa8341 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -117,7 +117,6 @@ import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; import android.view.animation.AccelerateInterpolator; import android.widget.DateTimeView; -import android.widget.ImageView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; @@ -189,7 +188,6 @@ import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; @@ -461,9 +459,6 @@ public class StatusBar extends SystemUI implements DemoMode, protected boolean mDozing; private boolean mDozingRequested; - protected BackDropView mBackdrop; - protected ImageView mBackdropFront, mBackdropBack; - private NotificationMediaManager mMediaManager; protected NotificationLockscreenUserManager mLockscreenUserManager; protected NotificationRemoteInputManager mRemoteInputManager; @@ -484,8 +479,7 @@ public class StatusBar extends SystemUI implements DemoMode, updateAodMaskVisibility(deviceSupportsAodWallpaper && aodImageWallpaperEnabled); // If WallpaperInfo is null, it must be ImageWallpaper. final boolean supportsAmbientMode = deviceSupportsAodWallpaper - && (info == null && aodImageWallpaperEnabled - || info != null && info.supportsAmbientMode()); + && (info == null || info.supportsAmbientMode()); mStatusBarWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode); mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode); @@ -493,7 +487,6 @@ public class StatusBar extends SystemUI implements DemoMode, }; private Runnable mLaunchTransitionEndRunnable; - protected boolean mLaunchTransitionFadingAway; private NotificationEntry mDraggedDownEntry; private boolean mLaunchCameraOnScreenTurningOn; private boolean mLaunchCameraOnFinishedGoingToSleep; @@ -653,7 +646,7 @@ public class StatusBar extends SystemUI implements DemoMode, mColorExtractor.addOnColorsChangedListener(this); mStatusBarStateController.addCallback(this, - StatusBarStateControllerImpl.RANK_STATUS_BAR); + SysuiStatusBarStateController.RANK_STATUS_BAR); mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); mDreamManager = IDreamManager.Stub.asInterface( @@ -933,11 +926,9 @@ public class StatusBar extends SystemUI implements DemoMode, mHeadsUpManager, mNotificationIconAreaController, mScrimController); mDozeScrimController = new DozeScrimController(DozeParameters.getInstance(context)); - mBackdrop = mStatusBarWindow.findViewById(R.id.backdrop); - mBackdropFront = mBackdrop.findViewById(R.id.backdrop_front); - mBackdropBack = mBackdrop.findViewById(R.id.backdrop_back); - mMediaManager.setup(mBackdrop, mBackdropFront, mBackdropBack, - mScrimController, mLockscreenWallpaper); + BackDropView backdrop = mStatusBarWindow.findViewById(R.id.backdrop); + mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front), + backdrop.findViewById(R.id.backdrop_back), mScrimController, mLockscreenWallpaper); // Other icons mVolumeComponent = getComponent(VolumeComponent.class); @@ -1593,10 +1584,6 @@ public class StatusBar extends SystemUI implements DemoMode, return mPulsing; } - public boolean isLaunchTransitionFadingAway() { - return mLaunchTransitionFadingAway; - } - public boolean hideStatusBarIconsWhenExpanded() { return mNotificationPanel.hideStatusBarIconsWhenExpanded(); } @@ -1893,6 +1880,8 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarWindow.cancelExpandHelper(); mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor); + } else { + mBubbleController.collapseStack(); } } @@ -2533,6 +2522,9 @@ public class StatusBar extends SystemUI implements DemoMode, if (mRemoteInputManager.getController() != null) { mRemoteInputManager.getController().closeRemoteInputs(); } + if (mBubbleController.isStackExpanded()) { + mBubbleController.collapseStack(); + } if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) { int flags = CommandQueue.FLAG_EXCLUDE_NONE; String reason = intent.getStringExtra("reason"); @@ -2546,6 +2538,9 @@ public class StatusBar extends SystemUI implements DemoMode, if (mStatusBarWindowController != null) { mStatusBarWindowController.setNotTouchable(false); } + if (mBubbleController.isStackExpanded()) { + mBubbleController.collapseStack(); + } finishBarAnimations(); resetUserExpandedStates(); } @@ -2967,7 +2962,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void showKeyguardImpl() { mIsKeyguard = true; - if (mLaunchTransitionFadingAway) { + if (mKeyguardMonitor.isLaunchTransitionFadingAway()) { mNotificationPanel.animate().cancel(); onLaunchTransitionFadingEnded(); } @@ -2999,7 +2994,7 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationPanel.onAffordanceLaunchEnded(); releaseGestureWakeLock(); runLaunchTransitionEndRunnable(); - mLaunchTransitionFadingAway = false; + mKeyguardMonitor.setLaunchTransitionFadingAway(false); mPresenter.updateMediaMetaData(true /* metaDataChanged */, true); } @@ -3025,7 +3020,6 @@ public class StatusBar extends SystemUI implements DemoMode, mLaunchTransitionEndRunnable = endRunnable; Runnable hideRunnable = () -> { mKeyguardMonitor.setLaunchTransitionFadingAway(true); - mLaunchTransitionFadingAway = true; if (beforeFading != null) { beforeFading.run(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java index 6495910359bb..6e36c019bb28 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java @@ -38,6 +38,7 @@ import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.notification.stack.ViewState; import java.util.ArrayList; +import java.util.List; /** * A container for Status bar system icons. Limits the number of system icons and handles overflow @@ -67,6 +68,8 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { private ArrayList<StatusIconState> mLayoutStates = new ArrayList<>(); // So we can count and measure properly private ArrayList<View> mMeasureViews = new ArrayList<>(); + // Any ignored icon will never be added as a child + private ArrayList<String> mIgnoredSlots = new ArrayList<>(); public StatusIconContainer(Context context) { this(context, null); @@ -146,7 +149,8 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { // Collect all of the views which want to be laid out for (int i = 0; i < count; i++) { StatusIconDisplayable icon = (StatusIconDisplayable) getChildAt(i); - if (icon.isIconVisible() && !icon.isIconBlocked()) { + if (icon.isIconVisible() && !icon.isIconBlocked() + && !mIgnoredSlots.contains(icon.getSlot())) { mMeasureViews.add((View) icon); } } @@ -205,6 +209,47 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { } /** + * Add a name of an icon slot to be ignored. It will not show up nor be measured + * @param slotName name of the icon as it exists in + * frameworks/base/core/res/res/values/config.xml + */ + public void addIgnoredSlot(String slotName) { + addIgnoredSlotInternal(slotName); + requestLayout(); + } + + /** + * Add a list of slots to be ignored + * @param slots names of the icons to ignore + */ + public void addIgnoredSlots(List<String> slots) { + for (String slot : slots) { + addIgnoredSlotInternal(slot); + } + + requestLayout(); + } + + private void addIgnoredSlotInternal(String slotName) { + if (!mIgnoredSlots.contains(slotName)) { + mIgnoredSlots.add(slotName); + } + } + + /** + * Remove a slot from the list of ignored icon slots. It will then be shown when set to visible + * by the {@link StatusBarIconController}. + * @param slotName name of the icon slot to remove from the ignored list + */ + public void removeIgnoredSlot(String slotName) { + if (mIgnoredSlots.contains(slotName)) { + mIgnoredSlots.remove(slotName); + } + + requestLayout(); + } + + /** * Layout is happening from end -> start */ private void calculateIconTranslations() { @@ -223,7 +268,8 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { StatusIconDisplayable iconView = (StatusIconDisplayable) child; StatusIconState childState = getViewStateFromChild(child); - if (!iconView.isIconVisible() || iconView.isIconBlocked()) { + if (!iconView.isIconVisible() || iconView.isIconBlocked() + || mIgnoredSlots.contains(iconView.getSlot())) { childState.visibleState = STATE_HIDDEN; if (DEBUG) Log.d(TAG, "skipping child (" + iconView.getSlot() + ") not visible"); continue; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index fd3f680e5e77..0461057bf2e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -16,7 +16,7 @@ package com.android.systemui.statusbar.policy; -import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP; +import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP; import android.annotation.NonNull; import android.annotation.Nullable; @@ -31,7 +31,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.statusbar.AlertingNotificationManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; +import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; import java.io.FileDescriptor; import java.io.PrintWriter; diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java b/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java index 6ee341dd974c..f446cefb7b6f 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java +++ b/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java @@ -156,6 +156,8 @@ public class AodMaskView extends ImageView implements StatusBarStateController.S private boolean checkIfNeedMask() { // We need mask for ImageWallpaper / LockScreen Wallpaper (Music album art). + // Because of conflicting with another wallpaper feature, + // we only support LockScreen wallpaper currently. return mWallpaperManager.getWallpaperInfo() == null || ScrimState.AOD.hasBackdrop(); } diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml index e73c70b29b74..efb4ff008b23 100644 --- a/packages/SystemUI/tests/AndroidManifest.xml +++ b/packages/SystemUI/tests/AndroidManifest.xml @@ -68,6 +68,13 @@ </intent-filter> </receiver> + <activity android:name="com.android.systemui.bubbles.BubblesTestActivity" + android:allowEmbedded="true" + android:documentLaunchMode="always" + android:excludeFromRecents="true" + android:exported="false" + android:resizeableActivity="true" /> + <provider android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer" tools:replace="android:authorities" diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index d9315f937867..ca72602f2c2b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -82,6 +82,8 @@ public class BubbleControllerTest extends SysuiTestCase { @Mock private BubbleController.BubbleExpandListener mBubbleExpandListener; + private BubbleData mBubbleData; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -104,7 +106,9 @@ public class BubbleControllerTest extends SysuiTestCase { when(mNotificationData.getChannel(mRow.getEntry().key)).thenReturn(mRow.getEntry().channel); when(mNotificationData.getChannel(mNoChannelRow.getEntry().key)).thenReturn(null); - mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController); + mBubbleData = new BubbleData(); + mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController, + mBubbleData); mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener); mBubbleController.setExpandListener(mBubbleExpandListener); @@ -112,9 +116,6 @@ public class BubbleControllerTest extends SysuiTestCase { verify(mNotificationEntryManager, atLeastOnce()) .addNotificationEntryListener(mEntryListenerCaptor.capture()); mEntryListener = mEntryListenerCaptor.getValue(); - - // Reset the data - BubbleData.getInstance().clear(); } @Test @@ -300,8 +301,8 @@ public class BubbleControllerTest extends SysuiTestCase { static class TestableBubbleController extends BubbleController { TestableBubbleController(Context context, - StatusBarWindowController statusBarWindowController) { - super(context, statusBarWindowController); + StatusBarWindowController statusBarWindowController, BubbleData data) { + super(context, statusBarWindowController, data); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java new file mode 100644 index 000000000000..ea472da910f2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java @@ -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. + */ + +package com.android.systemui.bubbles; + +import android.app.Activity; +import android.os.Bundle; + +import com.android.systemui.R; + +/** + * Referenced by NotificationTestHelper#makeBubbleMetadata + */ +public class BubblesTestActivity extends Activity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java index c0aac7ee8793..3bd582f955af 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java @@ -22,6 +22,8 @@ import android.content.res.Resources; import android.graphics.PointF; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; +import android.view.View; +import android.widget.FrameLayout; import androidx.dynamicanimation.animation.DynamicAnimation; @@ -63,15 +65,13 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC public void testExpansionAndCollapse() throws InterruptedException { Runnable afterExpand = Mockito.mock(Runnable.class); mExpandedController.expandFromStack(mExpansionPoint, afterExpand); - waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); - testExpanded(); + testBubblesInCorrectExpandedPositions(); Mockito.verify(afterExpand).run(); Runnable afterCollapse = Mockito.mock(Runnable.class); mExpandedController.collapseBackToStack(afterCollapse); - waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); testStackedAtPosition(mExpansionPoint.x, mExpansionPoint.y, -1); @@ -79,17 +79,70 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC } @Test - public void testOnChildRemoved() throws InterruptedException { - Runnable afterExpand = Mockito.mock(Runnable.class); - mExpandedController.expandFromStack(mExpansionPoint, afterExpand); + public void testOnChildAdded() throws InterruptedException { + expand(); + + // Add another new view and wait for its animation. + final View newView = new FrameLayout(getContext()); + mLayout.addView(newView, 0); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); - testExpanded(); + + testBubblesInCorrectExpandedPositions(); + } + + @Test + public void testOnChildRemoved() throws InterruptedException { + expand(); // Remove some views and see if the remaining child views still pass the expansion test. mLayout.removeView(mViews.get(0)); mLayout.removeView(mViews.get(3)); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); - testExpanded(); + testBubblesInCorrectExpandedPositions(); + } + + @Test + public void testBubbleDraggedNotDismissedSnapsBack() throws InterruptedException { + expand(); + + final View draggedBubble = mViews.get(0); + mExpandedController.prepareForBubbleDrag(draggedBubble); + mExpandedController.dragBubbleOut(draggedBubble, 500f, 500f); + + assertEquals(500f, draggedBubble.getTranslationX(), 1f); + assertEquals(500f, draggedBubble.getTranslationY(), 1f); + + // Snap it back and make sure it made it back correctly. + mExpandedController.snapBubbleBack(draggedBubble, 0f, 0f); + waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); + testBubblesInCorrectExpandedPositions(); + } + + @Test + public void testBubbleDismissed() throws InterruptedException { + expand(); + + final View draggedBubble = mViews.get(0); + mExpandedController.prepareForBubbleDrag(draggedBubble); + mExpandedController.dragBubbleOut(draggedBubble, 500f, 500f); + + assertEquals(500f, draggedBubble.getTranslationX(), 1f); + assertEquals(500f, draggedBubble.getTranslationY(), 1f); + + // Snap it back and make sure it made it back correctly. + mExpandedController.prepareForDismissalWithVelocity(draggedBubble, 0f, 0f); + mLayout.removeView(draggedBubble); + waitForLayoutMessageQueue(); + waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); + + assertEquals(-1, mLayout.indexOfChild(draggedBubble)); + testBubblesInCorrectExpandedPositions(); + } + + /** Expand the stack and wait for animations to finish. */ + private void expand() throws InterruptedException { + mExpandedController.expandFromStack(mExpansionPoint, Mockito.mock(Runnable.class)); + waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); } /** Check that children are in the correct positions for being stacked. */ @@ -108,7 +161,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC } /** Check that children are in the correct positions for being expanded. */ - private void testExpanded() { + private void testBubblesInCorrectExpandedPositions() { // Check all the visible bubbles to see if they're in the right place. for (int i = 0; i < Math.min(mLayout.getChildCount(), mMaxRenderedBubbles); i++) { assertEquals(mBubblePadding + (i * (mBubbleSize + mBubblePadding)), diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java index 31e44d7b3051..d94b6694c7fa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java @@ -155,6 +155,11 @@ public class PhysicsAnimationLayoutTestCase extends SysuiTestCase { } @Override + public void cancelAnimationsOnView(View view) { + mMainThreadHandler.post(() -> super.cancelAnimationsOnView(view)); + } + + @Override protected void animateValueForChildAtIndex(DynamicAnimation.ViewProperty property, int index, float value, float startVel, Runnable after) { mMainThreadHandler.post(() -> diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt index 5d3f6cacc80f..bb384dd52875 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt @@ -35,7 +35,12 @@ import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.appops.AppOpItem import com.android.systemui.appops.AppOpsController +import org.hamcrest.Matchers.hasItem +import org.hamcrest.Matchers.not +import org.hamcrest.Matchers.nullValue import org.junit.Assert.assertEquals +import org.junit.Assert.assertThat +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -240,4 +245,26 @@ class PrivacyItemControllerTest : SysuiTestCase() { verify(callback, never()).privacyChanged(anyList()) verify(otherCallback).privacyChanged(anyList()) } + + @Test + fun testListShouldNotHaveNull() { + doReturn(listOf(AppOpItem(AppOpsManager.OP_ACTIVATE_VPN, TEST_UID, "", 0), + AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0))) + .`when`(appOpsController).getActiveAppOpsForUser(anyInt()) + privacyItemController.addCallback(callback) + testableLooper.processAllMessages() + + verify(callback).privacyChanged(capture(argCaptor)) + assertEquals(1, argCaptor.value.size) + assertThat(argCaptor.value, not(hasItem(nullValue()))) + } + + @Test + fun testListShouldBeCopy() { + val list = listOf(PrivacyItem(PrivacyType.TYPE_CAMERA, + PrivacyApplication("", TEST_UID, mContext))) + privacyItemController.privacyList = list + assertEquals(list, privacyItemController.privacyList) + assertTrue(list !== privacyItemController.privacyList) + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java index 660f853b3c7b..7b1253af12c6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java @@ -17,7 +17,7 @@ package com.android.systemui.statusbar; -import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_CONTRACTED; +import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_CONTRACTED; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java index 0b24c2169ea1..3c919a17df6c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java @@ -36,10 +36,11 @@ import android.view.LayoutInflater; import android.widget.RemoteViews; import com.android.systemui.R; +import com.android.systemui.bubbles.BubblesTestActivity; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; -import com.android.systemui.statusbar.notification.row.NotificationInflaterTest; +import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; +import com.android.systemui.statusbar.notification.row.NotificationContentInflaterTest; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -278,7 +279,7 @@ public class NotificationTestHelper { entry.channel.setBlockableSystem(true); row.setEntry(entry); row.getNotificationInflater().addInflationFlags(extraInflationFlags); - NotificationInflaterTest.runThenWaitForInflation( + NotificationContentInflaterTest.runThenWaitForInflation( () -> row.inflateViews(), row.getNotificationInflater()); @@ -290,7 +291,8 @@ public class NotificationTestHelper { } private Notification.BubbleMetadata makeBubbleMetadata() { - PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); + Intent target = new Intent(mContext, BubblesTestActivity.class); + PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, target, 0); return new Notification.BubbleMetadata.Builder() .setIntent(bubbleIntent) .setTitle("bubble title") diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index 79bc0a39d59c..04e7cab406ea 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -68,8 +68,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationData; import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; -import com.android.systemui.statusbar.notification.row.NotificationInflater; import com.android.systemui.statusbar.notification.row.RowInflaterTask; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.NotificationGroupManager; @@ -143,7 +143,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Override public void onAsyncInflationFinished(NotificationEntry entry, - @NotificationInflater.InflationFlag int inflatedFlags) { + @InflationFlag int inflatedFlags) { super.onAsyncInflationFinished(entry, inflatedFlags); mCountDownLatch.countDown(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index 6d3553912701..f6fb4163bfc4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -18,9 +18,9 @@ package com.android.systemui.statusbar.notification.row; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; -import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_ALL; -import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP; -import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC; +import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_ALL; +import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP; +import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_PUBLIC; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java index 648df3cab33c..dfaa76a58e2d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java @@ -16,11 +16,11 @@ package com.android.systemui.statusbar.notification.row; -import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_ALL; -import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT; -import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_EXPANDED; -import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP; -import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC; +import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_ALL; +import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT; +import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_EXPANDED; +import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP; +import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_PUBLIC; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -49,6 +49,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.InflationTask; import com.android.systemui.statusbar.NotificationTestHelper; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationCallback; import org.junit.Assert; import org.junit.Before; @@ -64,9 +65,9 @@ import java.util.concurrent.TimeUnit; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper(setAsMainLooper = true) -public class NotificationInflaterTest extends SysuiTestCase { +public class NotificationContentInflaterTest extends SysuiTestCase { - private NotificationInflater mNotificationInflater; + private NotificationContentInflater mNotificationInflater; private Notification.Builder mBuilder; private ExpandableNotificationRow mRow; @@ -80,8 +81,8 @@ public class NotificationInflaterTest extends SysuiTestCase { ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow( mBuilder.build()); mRow = spy(row); - mNotificationInflater = new NotificationInflater(mRow); - mNotificationInflater.setInflationCallback(new NotificationInflater.InflationCallback() { + mNotificationInflater = new NotificationContentInflater(mRow); + mNotificationInflater.setInflationCallback(new InflationCallback() { @Override public void handleInflationException(StatusBarNotification notification, Exception e) { @@ -89,7 +90,7 @@ public class NotificationInflaterTest extends SysuiTestCase { @Override public void onAsyncInflationFinished(NotificationEntry entry, - @NotificationInflater.InflationFlag int inflatedFlags) { + @NotificationContentInflater.InflationFlag int inflatedFlags) { } }); } @@ -158,14 +159,14 @@ public class NotificationInflaterTest extends SysuiTestCase { @Test @Ignore public void testInflationIsRetriedIfAsyncFails() throws Exception { - NotificationInflater.InflationProgress result = - new NotificationInflater.InflationProgress(); + NotificationContentInflater.InflationProgress result = + new NotificationContentInflater.InflationProgress(); result.packageContext = mContext; CountDownLatch countDownLatch = new CountDownLatch(1); - NotificationInflater.applyRemoteView(result, FLAG_CONTENT_VIEW_EXPANDED, 0, + NotificationContentInflater.applyRemoteView(result, FLAG_CONTENT_VIEW_EXPANDED, 0, new ArrayMap() /* cachedContentViews */, mRow, false /* redactAmbient */, true /* isNewView */, (v, p, r) -> true, - new NotificationInflater.InflationCallback() { + new InflationCallback() { @Override public void handleInflationException(StatusBarNotification notification, Exception e) { @@ -175,11 +176,11 @@ public class NotificationInflaterTest extends SysuiTestCase { @Override public void onAsyncInflationFinished(NotificationEntry entry, - @NotificationInflater.InflationFlag int inflatedFlags) { + @NotificationContentInflater.InflationFlag int inflatedFlags) { countDownLatch.countDown(); } }, mRow.getPrivateLayout(), null, null, new HashMap<>(), - new NotificationInflater.ApplyCallback() { + new NotificationContentInflater.ApplyCallback() { @Override public void setResultView(View v) { } @@ -199,8 +200,8 @@ public class NotificationInflaterTest extends SysuiTestCase { mNotificationInflater.updateInflationFlag(FLAG_CONTENT_VIEW_PUBLIC, true); mNotificationInflater.updateNeedsRedaction(true); - NotificationInflater.AsyncInflationTask asyncInflationTask = - (NotificationInflater.AsyncInflationTask) mRow.getEntry().getRunningTask(); + NotificationContentInflater.AsyncInflationTask asyncInflationTask = + (NotificationContentInflater.AsyncInflationTask) mRow.getEntry().getRunningTask(); assertEquals(FLAG_CONTENT_VIEW_AMBIENT | FLAG_CONTENT_VIEW_PUBLIC, asyncInflationTask.getReInflateFlags()); asyncInflationTask.abort(); @@ -217,8 +218,8 @@ public class NotificationInflaterTest extends SysuiTestCase { mNotificationInflater.setIsChildInGroup(true); InflationTask runningTask = mRow.getEntry().getRunningTask(); - NotificationInflater.AsyncInflationTask asyncInflationTask = - (NotificationInflater.AsyncInflationTask) runningTask; + NotificationContentInflater.AsyncInflationTask asyncInflationTask = + (NotificationContentInflater.AsyncInflationTask) runningTask; assertEquals("Successive inflations don't inherit the previous flags!", FLAG_CONTENT_VIEW_ALL, asyncInflationTask.getReInflateFlags()); runningTask.abort(); @@ -233,19 +234,19 @@ public class NotificationInflaterTest extends SysuiTestCase { R.layout.custom_view_dark)); RemoteViews decoratedMediaView = mBuilder.createContentView(); Assert.assertFalse("The decorated media style doesn't allow a view to be reapplied!", - NotificationInflater.canReapplyRemoteView(mediaView, decoratedMediaView)); + NotificationContentInflater.canReapplyRemoteView(mediaView, decoratedMediaView)); } public static void runThenWaitForInflation(Runnable block, - NotificationInflater inflater) throws Exception { + NotificationContentInflater inflater) throws Exception { runThenWaitForInflation(block, false /* expectingException */, inflater); } private static void runThenWaitForInflation(Runnable block, boolean expectingException, - NotificationInflater inflater) throws Exception { + NotificationContentInflater inflater) throws Exception { CountDownLatch countDownLatch = new CountDownLatch(1); final ExceptionHolder exceptionHolder = new ExceptionHolder(); - inflater.setInflationCallback(new NotificationInflater.InflationCallback() { + inflater.setInflationCallback(new InflationCallback() { @Override public void handleInflationException(StatusBarNotification notification, Exception e) { @@ -257,7 +258,7 @@ public class NotificationInflaterTest extends SysuiTestCase { @Override public void onAsyncInflationFinished(NotificationEntry entry, - @NotificationInflater.InflationFlag int inflatedFlags) { + @NotificationContentInflater.InflationFlag int inflatedFlags) { if (expectingException) { exceptionHolder.setException(new RuntimeException( "Inflation finished even though there should be an error")); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java index 24aa772e2fc1..637b30c6bc00 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java @@ -14,7 +14,9 @@ * limitations under the License. */ -package com.android.systemui.statusbar.notification; +package com.android.systemui.statusbar.notification.row.wrapper; + +import static org.mockito.Mockito.mock; import android.content.Context; import android.support.test.filters.SmallTest; @@ -22,13 +24,15 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.NotificationTestHelper; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.util.Assert; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -37,12 +41,26 @@ import org.junit.runner.RunWith; @RunWithLooper public class NotificationViewWrapperTest extends SysuiTestCase { - @Test - public void constructor_doesntUseViewContext() throws Exception { + private View mView; + private ExpandableNotificationRow mRow; + private TestableNotificationViewWrapper mNotificationViewWrapper; + + @Before + public void setup() throws Exception { Assert.sMainLooper = TestableLooper.get(this).getLooper(); - new TestableNotificationViewWrapper(mContext, - new View(mContext), - new NotificationTestHelper(getContext()).createRow()); + mView = mock(View.class); + mRow = new NotificationTestHelper(getContext()).createRow(); + mNotificationViewWrapper = new TestableNotificationViewWrapper(mContext, mView, mRow); + } + + @Test + public void childrenNeedInversion_doesntCrash_whenOpacity() { + LinearLayout viewGroup = new LinearLayout(mContext); + TextView textView = new TextView(mContext); + textView.setTextColor(0xcc000000); + viewGroup.addView(textView); + + mNotificationViewWrapper.childrenNeedInversion(0xcc000000, viewGroup); } static class TestableNotificationViewWrapper extends NotificationViewWrapper { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java index 608dd8b0d281..120d0b093e0e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java @@ -29,6 +29,7 @@ import android.testing.TestableLooper; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationListener; +import com.android.systemui.statusbar.NotificationMediaManager; import org.junit.Before; import org.junit.Test; @@ -47,6 +48,8 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase { StatusBar mStatusBar; @Mock StatusBarStateController mStatusBarStateController; + @Mock + private NotificationMediaManager mMediaManager; private NotificationIconAreaController mController; @Before @@ -54,7 +57,7 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mController = new NotificationIconAreaController(mContext, mStatusBar, - mStatusBarStateController, mListener); + mStatusBarStateController, mListener, mMediaManager); } @Test diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java index 17e9b350d368..00cb6d3a0a26 100644 --- a/services/backup/java/com/android/server/backup/Trampoline.java +++ b/services/backup/java/com/android/server/backup/Trampoline.java @@ -47,6 +47,8 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.DumpUtils; +import com.android.server.backup.utils.FileUtils; +import com.android.server.backup.utils.RandomAccessFileUtils; import java.io.File; import java.io.FileDescriptor; @@ -89,6 +91,12 @@ public class Trampoline extends IBackupManager.Stub { */ private static final String BACKUP_ACTIVATED_FILENAME = "backup-activated"; + /** + * Name of file for non-system users that remembers whether backup was explicitly activated or + * deactivated with a call to setBackupServiceActive. + */ + private static final String REMEMBER_ACTIVATED_FILENAME_PREFIX = "backup-remember-activated"; + // Product-level suppression of backup/restore. private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable"; @@ -134,11 +142,17 @@ public class Trampoline extends IBackupManager.Stub { } /** Stored in the system user's directory and the file is indexed by the user it refers to. */ + protected File getRememberActivatedFileForNonSystemUser(int userId) { + return FileUtils.createNewFile(UserBackupManagerFiles.getStateFileInSystemDir( + REMEMBER_ACTIVATED_FILENAME_PREFIX, userId)); + } + + /** Stored in the system user's directory and the file is indexed by the user it refers to. */ protected File getActivatedFileForNonSystemUser(int userId) { - return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM), - BACKUP_ACTIVATED_FILENAME + "-" + userId); + return UserBackupManagerFiles.getStateFileInSystemDir(BACKUP_ACTIVATED_FILENAME, userId); } + // TODO (b/124359804) move to util method in FileUtils private void createFile(File file) throws IOException { if (file.exists()) { return; @@ -150,6 +164,7 @@ public class Trampoline extends IBackupManager.Stub { } } + // TODO (b/124359804) move to util method in FileUtils private void deleteFile(File file) { if (!file.exists()) { return; @@ -312,6 +327,19 @@ public class Trampoline extends IBackupManager.Stub { public void setBackupServiceActive(int userId, boolean makeActive) { enforcePermissionsOnUser(userId); + // In Q, backup is OFF by default for non-system users. In the future, we will change that + // to ON unless backup was explicitly deactivated with a (permissioned) call to + // setBackupServiceActive. + // Therefore, remember this for use in the future. Basically the default in the future will + // be: rememberFile.exists() ? rememberFile.value() : ON + // Note that this has to be done right after the permission checks and before any other + // action since we need to remember that a permissioned call was made irrespective of + // whether the call changes the state or not. + if (userId != UserHandle.USER_SYSTEM) { + RandomAccessFileUtils.writeBoolean(getRememberActivatedFileForNonSystemUser(userId), + makeActive); + } + if (mGlobalDisable) { Slog.i(TAG, "Backup service not supported"); return; diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java index aabd41a611a1..4638ac63de4a 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java @@ -48,4 +48,9 @@ final class UserBackupManagerFiles { // is a staging dir, we dont need to copy below dir to new system user dir return new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR); } + + /** Stored in the system user's directory and the file is indexed by the user it refers to. */ + static File getStateFileInSystemDir(String prefix, int userId) { + return new File(getBaseStateDir(UserHandle.USER_SYSTEM), prefix + "-" + userId); + } } diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index b2afbc3ec5f9..d4ac731f8810 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -130,6 +130,7 @@ import com.android.server.backup.transport.TransportNotRegisteredException; import com.android.server.backup.utils.AppBackupUtils; import com.android.server.backup.utils.BackupManagerMonitorUtils; import com.android.server.backup.utils.BackupObserverUtils; +import com.android.server.backup.utils.FileUtils; import com.android.server.backup.utils.SparseArrayUtils; import com.google.android.collect.Sets; @@ -2319,6 +2320,7 @@ public class UserBackupManagerService { mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "setAncestralSerialNumber"); Slog.v(TAG, "Setting ancestral work profile id to " + ancestralSerialNumber); + // TODO (b/124359804) try (RandomAccessFile af = getAncestralSerialNumberFile()) { af.writeLong(ancestralSerialNumber); } catch (IOException e) { @@ -2331,6 +2333,7 @@ public class UserBackupManagerService { * {@link #setAncestralSerialNumber(long)}. Will return {@code -1} if not set. */ public long getAncestralSerialNumber() { + // TODO (b/124359804) try (RandomAccessFile af = getAncestralSerialNumberFile()) { return af.readLong(); } catch (IOException e) { @@ -2344,13 +2347,7 @@ public class UserBackupManagerService { mAncestralSerialNumberFile = new File( UserBackupManagerFiles.getBaseStateDir(getUserId()), SERIAL_ID_FILE); - if (!mAncestralSerialNumberFile.exists()) { - try { - mAncestralSerialNumberFile.createNewFile(); - } catch (IOException e) { - Slog.w(TAG, "serial number mapping file creation failed", e); - } - } + FileUtils.createNewFile(mAncestralSerialNumberFile); } return new RandomAccessFile(mAncestralSerialNumberFile, "rwd"); } diff --git a/services/backup/java/com/android/server/backup/utils/FileUtils.java b/services/backup/java/com/android/server/backup/utils/FileUtils.java new file mode 100644 index 000000000000..00686cba4777 --- /dev/null +++ b/services/backup/java/com/android/server/backup/utils/FileUtils.java @@ -0,0 +1,40 @@ +/* + * 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.backup.utils; + +import static com.android.server.backup.BackupManagerService.TAG; + +import android.util.Slog; + +import java.io.File; +import java.io.IOException; + +/** Utility methods useful for working with backup related files. */ +public final class FileUtils { + /** + * Ensure that the file exists in the file system. If an IOException is thrown, it is ignored. + * This method is useful to avoid code duplication of the "try-catch-ignore exception" block. + */ + public static File createNewFile(File file) { + try { + file.createNewFile(); + } catch (IOException e) { + Slog.w(TAG, "Failed to create file:" + file.getAbsolutePath(), e); + } + return file; + } +} diff --git a/services/backup/java/com/android/server/backup/utils/RandomAccessFileUtils.java b/services/backup/java/com/android/server/backup/utils/RandomAccessFileUtils.java new file mode 100644 index 000000000000..abf906aee5dd --- /dev/null +++ b/services/backup/java/com/android/server/backup/utils/RandomAccessFileUtils.java @@ -0,0 +1,52 @@ +/* + * 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.backup.utils; + +import static com.android.server.backup.BackupManagerService.TAG; + +import android.util.Slog; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; + +/** Utility methods useful for working with backup related RandomAccessFiles. */ +public final class RandomAccessFileUtils { + private static RandomAccessFile getRandomAccessFile(File file) throws FileNotFoundException { + return new RandomAccessFile(file, "rwd"); + } + + /** Write a boolean to a File by wrapping it using a RandomAccessFile. */ + public static void writeBoolean(File file, boolean b) { + try (RandomAccessFile af = getRandomAccessFile(file)) { + af.writeBoolean(b); + } catch (IOException e) { + Slog.w(TAG, "Error writing file:" + file.getAbsolutePath(), e); + } + } + + /** Read a boolean from a File by wrapping it using a RandomAccessFile. */ + public static boolean readBoolean(File file, boolean def) { + try (RandomAccessFile af = getRandomAccessFile(file)) { + return af.readBoolean(); + } catch (IOException e) { + Slog.w(TAG, "Error reading file:" + file.getAbsolutePath(), e); + } + return def; + } +} diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 4afbc641ea6c..4bd50ec21d84 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -45,6 +45,7 @@ import android.provider.Settings; import android.util.LocalLog; import android.util.Slog; import android.util.SparseBooleanArray; +import android.view.contentcapture.ContentCaptureHelper; import android.view.contentcapture.ContentCaptureManager; import android.view.contentcapture.IContentCaptureManager; import android.view.contentcapture.UserDataRemovalRequest; @@ -79,7 +80,8 @@ public final class ContentCaptureManagerService extends private final LocalService mLocalService = new LocalService(); - private final LocalLog mRequestsHistory = new LocalLog(20); + @Nullable + final LocalLog mRequestsHistory; @GuardedBy("mLock") private ActivityManagerInternal mAm; @@ -105,15 +107,19 @@ public final class ContentCaptureManagerService extends UserManager.DISALLOW_CONTENT_CAPTURE); DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, ActivityThread.currentApplication().getMainExecutor(), - (namespace, key, value) -> { - if (!ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED - .equals(key)) { - Slog.i(mTag, "Ignoring change on " + key); - return; - } - setDisabledByDeviceConfig(value); - }); - setDisabledByDeviceConfig(); + (namespace, key, value) -> onDeviceConfigChange(key, value)); + setLoggingLevelFromDeviceConfig(); + setDisabledFromDeviceConfig(); + + final int loggingSize = ContentCaptureHelper.getIntDeviceConfigProperty( + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, 20); + if (loggingSize > 0) { + if (debug) Slog.d(mTag, "log history size: " + loggingSize); + mRequestsHistory = new LocalLog(loggingSize); + } else { + if (debug) Slog.d(mTag, "disabled log history because size is " + loggingSize); + mRequestsHistory = null; + } // Sets which services are disabled final UserManager um = getContext().getSystemService(UserManager.class); @@ -213,7 +219,33 @@ public final class ContentCaptureManagerService extends return false; } - private void setDisabledByDeviceConfig() { + private void onDeviceConfigChange(@NonNull String key, @Nullable String value) { + switch (key) { + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED: + setDisabledByDeviceConfig(value); + return; + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL: + setLoggingLevelFromDeviceConfig(); + return; + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE: + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY: + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE: + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY: + // TODO(b/123096662): implement it + Slog.d(mTag, "changes on " + key + " not supported yet"); + return; + default: + Slog.i(mTag, "Ignoring change on " + key); + } + } + + private void setLoggingLevelFromDeviceConfig() { + ContentCaptureHelper.setLoggingLevel(); + verbose = ContentCaptureHelper.sVerbose; + debug = ContentCaptureHelper.sDebug; + } + + private void setDisabledFromDeviceConfig() { final String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED); setDisabledByDeviceConfig(value); @@ -327,13 +359,6 @@ public final class ContentCaptureManagerService extends } } - /** - * Logs a request so it's dumped later... - */ - void logRequestLocked(@NonNull String historyItem) { - mRequestsHistory.log(historyItem); - } - private ActivityManagerInternal getAmInternal() { synchronized (mLock) { if (mAm == null) { @@ -527,9 +552,13 @@ public final class ContentCaptureManagerService extends synchronized (mLock) { dumpLocked("", pw); } - if (showHistory) { - pw.println(); pw.println("Requests history:"); pw.println(); + pw.print("Requests history: "); + if (mRequestsHistory == null) { + pw.println("disabled by device config"); + } else if (showHistory) { + pw.println(); mRequestsHistory.reverseDump(fd, pw, args); + pw.println(); } } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index 7102b82d5e18..360f064bdf0d 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -21,6 +21,7 @@ import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED; import static android.view.contentcapture.ContentCaptureSession.STATE_DUPLICATED_ID; import static android.view.contentcapture.ContentCaptureSession.STATE_INTERNAL_ERROR; import static android.view.contentcapture.ContentCaptureSession.STATE_NO_SERVICE; +import static android.view.contentcapture.ContentCaptureSession.STATE_PACKAGE_NOT_WHITELISTED; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA; @@ -47,7 +48,7 @@ import android.service.contentcapture.ContentCaptureService; import android.service.contentcapture.IContentCaptureServiceCallback; import android.service.contentcapture.SnapshotData; import android.util.ArrayMap; -import android.util.Log; +import android.util.ArraySet; import android.util.Slog; import android.view.contentcapture.UserDataRemovalRequest; @@ -87,6 +88,12 @@ final class ContentCapturePerUserService private final ContentCaptureServiceRemoteCallback mRemoteServiceCallback = new ContentCaptureServiceRemoteCallback(); + /** + * List of packages that are whitelisted to be content captured. + */ + @GuardedBy("mLock") + private final ArraySet<String> mWhitelistedPackages = new ArraySet<>(); + // TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's ContentCapturePerUserService(@NonNull ContentCaptureManagerService master, @@ -185,15 +192,19 @@ final class ContentCapturePerUserService final int taskId = activityPresentationInfo.taskId; final int displayId = activityPresentationInfo.displayId; final ComponentName componentName = activityPresentationInfo.componentName; + final boolean whitelisted = isWhitelistedLocked(componentName); final ComponentName serviceComponentName = getServiceComponentName(); final boolean enabled = isEnabledLocked(); - final String historyItem = - "id=" + sessionId + " uid=" + uid - + " a=" + ComponentName.flattenToShortString(componentName) - + " t=" + taskId + " d=" + displayId - + " s=" + ComponentName.flattenToShortString(serviceComponentName) - + " u=" + mUserId + " f=" + flags + (enabled ? "" : " (disabled)"); - mMaster.logRequestLocked(historyItem); + if (mMaster.mRequestsHistory != null) { + final String historyItem = + "id=" + sessionId + " uid=" + uid + + " a=" + ComponentName.flattenToShortString(componentName) + + " t=" + taskId + " d=" + displayId + + " s=" + ComponentName.flattenToShortString(serviceComponentName) + + " u=" + mUserId + " f=" + flags + (enabled ? "" : " (disabled)") + + " w=" + whitelisted; + mMaster.mRequestsHistory.log(historyItem); + } if (!enabled) { // TODO: it would be better to split in differet reasons, like @@ -212,6 +223,16 @@ final class ContentCapturePerUserService return; } + if (!whitelisted) { + if (mMaster.debug) { + Slog.d(TAG, "startSession(" + componentName + "): not whitelisted"); + } + // TODO(b/122595322): need to return STATE_ACTIVITY_NOT_WHITELISTED as well + setClientState(clientReceiver, STATE_DISABLED | STATE_PACKAGE_NOT_WHITELISTED, + /* binder= */ null); + return; + } + final ContentCaptureServerSession existingSession = mSessions.get(sessionId); if (existingSession != null) { Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken @@ -245,6 +266,26 @@ final class ContentCapturePerUserService newSession.notifySessionStartedLocked(clientReceiver); } + @GuardedBy("mLock") + private boolean isWhitelistedLocked(@NonNull ComponentName componentName) { + // TODO(b/122595322): need to check whitelisted activities as well. + final String packageName = componentName.getPackageName(); + return mWhitelistedPackages.contains(packageName); + } + + private void whitelistPackages(@NonNull List<String> packages) { + // TODO(b/122595322): add CTS test for when it's null + synchronized (mLock) { + if (packages == null) { + if (mMaster.verbose) Slog.v(TAG, "clearing all whitelisted packages"); + mWhitelistedPackages.clear(); + } else { + if (mMaster.verbose) Slog.v(TAG, "whitelisting packages: " + packages); + mWhitelistedPackages.addAll(packages); + } + } + } + // TODO(b/119613670): log metrics @GuardedBy("mLock") public void finishSessionLocked(@NonNull String sessionId) { @@ -376,15 +417,23 @@ final class ContentCapturePerUserService mRemoteService.dump(prefix2, pw); } + final int whitelistSize = mWhitelistedPackages.size(); + pw.print(prefix); pw.print("Whitelisted packages: "); pw.println(whitelistSize); + for (int i = 0; i < whitelistSize; i++) { + final String whitelistedPkg = mWhitelistedPackages.valueAt(i); + pw.print(prefix2); pw.print(i + 1); pw.print(": "); pw.println(whitelistedPkg); + } + if (mSessions.isEmpty()) { pw.print(prefix); pw.println("no sessions"); } else { - final int size = mSessions.size(); - pw.print(prefix); pw.print("number sessions: "); pw.println(size); - for (int i = 0; i < size; i++) { - pw.print(prefix); pw.print("session@"); pw.println(i); + final int sessionsSize = mSessions.size(); + pw.print(prefix); pw.print("number sessions: "); pw.println(sessionsSize); + for (int i = 0; i < sessionsSize; i++) { + pw.print(prefix); pw.print("#"); pw.println(i); final ContentCaptureServerSession session = mSessions.valueAt(i); session.dumpLocked(prefix2, pw); + pw.println(); } } } @@ -410,10 +459,12 @@ final class ContentCapturePerUserService public void setContentCaptureWhitelist(List<String> packages, List<ComponentName> activities) { if (mMaster.verbose) { - Log.v(TAG, "setContentCaptureWhitelist(packages=" + packages + ", activities=" + Slog.v(TAG, "setContentCaptureWhitelist(packages=" + packages + ", activities=" + activities + ")"); } - // TODO(b/122595322): implement + whitelistPackages(packages); + + // TODO(b/122595322): whitelist activities as well // TODO(b/119613670): log metrics } } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java index 3c52e17ce1e8..40948432751d 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.os.IBinder; import android.service.contentcapture.ContentCaptureService; import android.service.contentcapture.SnapshotData; +import android.util.LocalLog; import android.util.Slog; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureSessionId; @@ -37,6 +38,9 @@ final class ContentCaptureServerSession { final IBinder mActivityToken; private final ContentCapturePerUserService mService; private final RemoteContentCaptureService mRemoteService; + + // NOTE: this is the "internal" context (like package and taskId), not the explicit content + // set by apps - those are only send to the ContentCaptureService. private final ContentCaptureContext mContentCaptureContext; /** @@ -83,7 +87,10 @@ final class ContentCaptureServerSession { */ @GuardedBy("mLock") public void sendActivitySnapshotLocked(@NonNull SnapshotData snapshotData) { - mService.getMaster().logRequestLocked("snapshot: id=" + mId); + final LocalLog logHistory = mService.getMaster().mRequestsHistory; + if (logHistory != null) { + logHistory.log("snapshot: id=" + mId); + } mRemoteService.onActivitySnapshotRequest(mId, snapshotData); } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index d1cd072ee215..915c131ff0fb 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -57,8 +57,10 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; import android.database.ContentObserver; +import android.net.CaptivePortal; import android.net.ConnectionInfo; import android.net.ConnectivityManager; +import android.net.ICaptivePortal; import android.net.IConnectivityManager; import android.net.IIpConnectivityMetrics; import android.net.INetd; @@ -86,6 +88,7 @@ import android.net.NetworkQuotaInfo; import android.net.NetworkRequest; import android.net.NetworkSpecifier; import android.net.NetworkStack; +import android.net.NetworkStackClient; import android.net.NetworkState; import android.net.NetworkUtils; import android.net.NetworkWatchlistManager; @@ -917,7 +920,8 @@ public class ConnectivityService extends IConnectivityManager.Stub mPermissionMonitor = new PermissionMonitor(mContext, mNMS); - //set up the listener for user state for creating user VPNs + // Set up the listener for user state for creating user VPNs. + // Should run on mHandler to avoid any races. IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_USER_STARTED); intentFilter.addAction(Intent.ACTION_USER_STOPPED); @@ -925,7 +929,11 @@ public class ConnectivityService extends IConnectivityManager.Stub intentFilter.addAction(Intent.ACTION_USER_REMOVED); intentFilter.addAction(Intent.ACTION_USER_UNLOCKED); mContext.registerReceiverAsUser( - mIntentReceiver, UserHandle.ALL, intentFilter, null, null); + mIntentReceiver, + UserHandle.ALL, + intentFilter, + null /* broadcastPermission */, + mHandler); mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM, new IntentFilter(Intent.ACTION_USER_PRESENT), null, null); @@ -936,7 +944,11 @@ public class ConnectivityService extends IConnectivityManager.Stub intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); intentFilter.addDataScheme("package"); mContext.registerReceiverAsUser( - mIntentReceiver, UserHandle.ALL, intentFilter, null, null); + mIntentReceiver, + UserHandle.ALL, + intentFilter, + null /* broadcastPermission */, + mHandler); try { mNMS.registerObserver(mTethering); @@ -2690,11 +2702,6 @@ public class ConnectivityService extends IConnectivityManager.Stub EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_HIDE, mNai.network.netId)); } - - @Override - public void logCaptivePortalLoginEvent(int eventId, String packageName) { - new MetricsLogger().action(eventId, packageName); - } } private boolean networkRequiresValidation(NetworkAgentInfo nai) { @@ -2842,6 +2849,8 @@ public class ConnectivityService extends IConnectivityManager.Stub if (DBG) { log(nai.name() + " got DISCONNECTED, was satisfying " + nai.numNetworkRequests()); } + // Clear all notifications of this network. + mNotifier.clearNotification(nai.network.netId); // A network agent has disconnected. // TODO - if we move the logic to the network agent (have them disconnect // because they lost all their requests or because their score isn't good) @@ -3247,22 +3256,63 @@ public class ConnectivityService extends IConnectivityManager.Stub /** * NetworkStack endpoint to start the captive portal app. The NetworkStack needs to use this * endpoint as it does not have INTERACT_ACROSS_USERS_FULL itself. + * @param network Network on which the captive portal was detected. * @param appExtras Bundle to use as intent extras for the captive portal application. * Must be treated as opaque to avoid preventing the captive portal app to * update its arguments. */ @Override - public void startCaptivePortalAppInternal(Bundle appExtras) { + public void startCaptivePortalAppInternal(Network network, Bundle appExtras) { mContext.checkCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); final Intent appIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN); appIntent.putExtras(appExtras); + appIntent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL, + new CaptivePortal(new CaptivePortalImpl(network).asBinder())); appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK); Binder.withCleanCallingIdentity(() -> mContext.startActivityAsUser(appIntent, UserHandle.CURRENT)); } + private class CaptivePortalImpl extends ICaptivePortal.Stub { + private final Network mNetwork; + + private CaptivePortalImpl(Network network) { + mNetwork = network; + } + + @Override + public void appResponse(final int response) throws RemoteException { + if (response == CaptivePortal.APP_RETURN_WANTED_AS_IS) { + enforceSettingsPermission(); + } + + // getNetworkAgentInfoForNetwork is thread-safe + final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(mNetwork); + if (nai == null) return; + + // nai.networkMonitor() is thread-safe + final INetworkMonitor nm = nai.networkMonitor(); + if (nm == null) return; + + final long token = Binder.clearCallingIdentity(); + try { + nm.notifyCaptivePortalAppFinished(response); + } finally { + // Not using Binder.withCleanCallingIdentity() to keep the checked RemoteException + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void logEvent(int eventId, String packageName) { + enforceSettingsPermission(); + + new MetricsLogger().action(eventId, packageName); + } + } + public boolean avoidBadWifi() { return mMultinetworkPolicyTracker.getAvoidBadWifi(); } @@ -4097,17 +4147,27 @@ public class ConnectivityService extends IConnectivityManager.Stub * handler thread through their agent, this is asynchronous. When the capabilities objects * are computed they will be up-to-date as they are computed synchronously from here and * this is running on the ConnectivityService thread. - * TODO : Fix this and call updateCapabilities inline to remove out-of-order events. */ private void updateAllVpnsCapabilities() { + Network defaultNetwork = getNetwork(getDefaultNetwork()); synchronized (mVpns) { for (int i = 0; i < mVpns.size(); i++) { final Vpn vpn = mVpns.valueAt(i); - vpn.updateCapabilities(); + NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork); + updateVpnCapabilities(vpn, nc); } } } + private void updateVpnCapabilities(Vpn vpn, @Nullable NetworkCapabilities nc) { + ensureRunningOnConnectivityServiceThread(); + NetworkAgentInfo vpnNai = getNetworkAgentInfoForNetId(vpn.getNetId()); + if (vpnNai == null || nc == null) { + return; + } + updateCapabilities(vpnNai.getCurrentScore(), vpnNai, nc); + } + @Override public boolean updateLockdownVpn() { if (Binder.getCallingUid() != Process.SYSTEM_UID) { @@ -4448,22 +4508,28 @@ public class ConnectivityService extends IConnectivityManager.Stub private void onUserAdded(int userId) { mPermissionMonitor.onUserAdded(userId); + Network defaultNetwork = getNetwork(getDefaultNetwork()); synchronized (mVpns) { final int vpnsSize = mVpns.size(); for (int i = 0; i < vpnsSize; i++) { Vpn vpn = mVpns.valueAt(i); vpn.onUserAdded(userId); + NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork); + updateVpnCapabilities(vpn, nc); } } } private void onUserRemoved(int userId) { mPermissionMonitor.onUserRemoved(userId); + Network defaultNetwork = getNetwork(getDefaultNetwork()); synchronized (mVpns) { final int vpnsSize = mVpns.size(); for (int i = 0; i < vpnsSize; i++) { Vpn vpn = mVpns.valueAt(i); vpn.onUserRemoved(userId); + NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork); + updateVpnCapabilities(vpn, nc); } } } @@ -4532,6 +4598,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + ensureRunningOnConnectivityServiceThread(); final String action = intent.getAction(); final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); @@ -5041,6 +5108,19 @@ public class ConnectivityService extends IConnectivityManager.Stub return getNetworkForRequest(mDefaultRequest.requestId); } + @Nullable + private Network getNetwork(@Nullable NetworkAgentInfo nai) { + return nai != null ? nai.network : null; + } + + private void ensureRunningOnConnectivityServiceThread() { + if (mHandler.getLooper().getThread() != Thread.currentThread()) { + throw new IllegalStateException( + "Not running on ConnectivityService thread: " + + Thread.currentThread().getName()); + } + } + private boolean isDefaultNetwork(NetworkAgentInfo nai) { return nai == getDefaultNetwork(); } @@ -5097,7 +5177,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (DBG) log("registerNetworkAgent " + nai); final long token = Binder.clearCallingIdentity(); try { - mContext.getSystemService(NetworkStack.class).makeNetworkMonitor( + getNetworkStack().makeNetworkMonitor( toStableParcelable(nai.network), name, new NetworkMonitorCallbacks(nai)); } finally { Binder.restoreCallingIdentity(token); @@ -5109,6 +5189,11 @@ public class ConnectivityService extends IConnectivityManager.Stub return nai.network.netId; } + @VisibleForTesting + protected NetworkStackClient getNetworkStack() { + return NetworkStackClient.getInstance(); + } + private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) { nai.onNetworkMonitorCreated(networkMonitor); if (VDBG) log("Got NetworkAgent Messenger"); @@ -5667,6 +5752,8 @@ public class ConnectivityService extends IConnectivityManager.Stub updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes()); mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers()); notifyIfacesChangedForNetworkStats(); + // Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks. + updateAllVpnsCapabilities(); } private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) { @@ -6106,6 +6193,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // doing. updateSignalStrengthThresholds(networkAgent, "CONNECT", null); + if (networkAgent.isVPN()) { + updateAllVpnsCapabilities(); + } + // Consider network even though it is not yet validated. final long now = SystemClock.elapsedRealtime(); rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP, now); @@ -6367,7 +6458,11 @@ public class ConnectivityService extends IConnectivityManager.Stub success = mVpns.get(user).setUnderlyingNetworks(networks); } if (success) { - mHandler.post(() -> notifyIfacesChangedForNetworkStats()); + mHandler.post(() -> { + // Update VPN's capabilities based on updated underlying network set. + updateAllVpnsCapabilities(); + notifyIfacesChangedForNetworkStats(); + }); } return success; } diff --git a/services/core/java/com/android/server/ExtconUEventObserver.java b/services/core/java/com/android/server/ExtconUEventObserver.java index eb591528bc90..775e4c8cf4ed 100644 --- a/services/core/java/com/android/server/ExtconUEventObserver.java +++ b/services/core/java/com/android/server/ExtconUEventObserver.java @@ -68,7 +68,7 @@ public abstract class ExtconUEventObserver extends UEventObserver { * Subclasses of ExtconUEventObserver should override this method to handle UEvents. * * @param extconInfo that matches the {@code DEVPATH} of {@code event} - * @param event the event + * @param event the event */ protected abstract void onUEvent(ExtconInfo extconInfo, UEvent event); @@ -91,6 +91,9 @@ public abstract class ExtconUEventObserver extends UEventObserver { /** Returns a new list of all external connections whose name matches {@code regex}. */ public static List<ExtconInfo> getExtconInfos(@Nullable String regex) { + if (!extconExists()) { + return new ArrayList<>(0); // Always return a new list. + } Pattern p = regex == null ? null : Pattern.compile(regex); File file = new File("/sys/class/extcon"); File[] files = file.listFiles(); diff --git a/services/core/java/com/android/server/FgThread.java b/services/core/java/com/android/server/FgThread.java index fe30057fc820..5d0e308f6649 100644 --- a/services/core/java/com/android/server/FgThread.java +++ b/services/core/java/com/android/server/FgThread.java @@ -17,9 +17,12 @@ package com.android.server; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.Looper; import android.os.Trace; +import java.util.concurrent.Executor; + /** * Shared singleton foreground thread for the system. This is a thread for regular * foreground service operations, which shouldn't be blocked by anything running in @@ -34,6 +37,7 @@ public final class FgThread extends ServiceThread { private static FgThread sInstance; private static Handler sHandler; + private static HandlerExecutor sHandlerExecutor; private FgThread() { super("android.fg", android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/); @@ -48,6 +52,7 @@ public final class FgThread extends ServiceThread { looper.setSlowLogThresholdMs( SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS); sHandler = new Handler(sInstance.getLooper()); + sHandlerExecutor = new HandlerExecutor(sHandler); } } @@ -64,4 +69,11 @@ public final class FgThread extends ServiceThread { return sHandler; } } + + public static Executor getExecutor() { + synchronized (FgThread.class) { + ensureThreadLocked(); + return sHandlerExecutor; + } + } } diff --git a/services/core/java/com/android/server/IoThread.java b/services/core/java/com/android/server/IoThread.java index bfe825a3a89e..21fd29c3bbef 100644 --- a/services/core/java/com/android/server/IoThread.java +++ b/services/core/java/com/android/server/IoThread.java @@ -17,8 +17,11 @@ package com.android.server; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.Trace; +import java.util.concurrent.Executor; + /** * Shared singleton I/O thread for the system. This is a thread for non-background * service operations that can potential block briefly on network IO operations @@ -27,6 +30,7 @@ import android.os.Trace; public final class IoThread extends ServiceThread { private static IoThread sInstance; private static Handler sHandler; + private static HandlerExecutor sHandlerExecutor; private IoThread() { super("android.io", android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/); @@ -38,6 +42,7 @@ public final class IoThread extends ServiceThread { sInstance.start(); sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER); sHandler = new Handler(sInstance.getLooper()); + sHandlerExecutor = new HandlerExecutor(sHandler); } } @@ -54,4 +59,11 @@ public final class IoThread extends ServiceThread { return sHandler; } } + + public static Executor getExecutor() { + synchronized (IoThread.class) { + ensureThreadLocked(); + return sHandlerExecutor; + } + } } diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 0b0934bc5a5e..5989a46c5a0a 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -1645,36 +1645,6 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private abstract static class LinkedListenerBase implements IBinder.DeathRecipient { - protected final CallerIdentity mCallerIdentity; - protected final String mListenerName; - - private LinkedListenerBase(@NonNull CallerIdentity callerIdentity, - @NonNull String listenerName) { - mCallerIdentity = callerIdentity; - mListenerName = listenerName; - } - } - - private static class LinkedListener<TListener> extends LinkedListenerBase { - private final TListener mListener; - private final Consumer<TListener> mBinderDeathCallback; - - private LinkedListener(@NonNull TListener listener, String listenerName, - @NonNull CallerIdentity callerIdentity, - @NonNull Consumer<TListener> binderDeathCallback) { - super(callerIdentity, listenerName); - mListener = listener; - mBinderDeathCallback = binderDeathCallback; - } - - @Override - public void binderDied() { - if (D) Log.d(TAG, "Remote " + mListenerName + " died."); - mBinderDeathCallback.accept(mListener); - } - } - @Override public void removeGnssBatchingCallback() { synchronized (mLock) { @@ -2166,6 +2136,13 @@ public class LocationManagerService extends ILocationManager.Stub { } } + @Override + public String[] getIgnoreSettingsWhitelist() { + synchronized (mLock) { + return mIgnoreSettingsPackageWhitelist.toArray(new String[0]); + } + } + @GuardedBy("mLock") private boolean isThrottlingExemptLocked(CallerIdentity callerIdentity) { if (callerIdentity.mUid == Process.SYSTEM_UID) { @@ -2711,77 +2688,85 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName) { - if (!hasGnssPermissions(packageName) || mGnssStatusProvider == null) { - return false; - } + return addGnssDataListener(listener, packageName, "GnssStatusListener", + mGnssStatusProvider, mGnssStatusListeners, + this::unregisterGnssStatusCallback); + } - CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), - Binder.getCallingPid(), packageName); - LinkedListener<IGnssStatusListener> linkedListener = new LinkedListener<>(listener, - "GnssStatusListener", callerIdentity, this::unregisterGnssStatusCallback); - IBinder binder = listener.asBinder(); - synchronized (mLock) { - if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) { - return false; - } + @Override + public void unregisterGnssStatusCallback(IGnssStatusListener listener) { + removeGnssDataListener(listener, mGnssStatusProvider, mGnssStatusListeners); + } - mGnssStatusListeners.put(binder, linkedListener); - long identity = Binder.clearCallingIdentity(); - try { - if (isThrottlingExemptLocked(callerIdentity) - || isImportanceForeground( - mActivityManager.getPackageImportance(packageName))) { - mGnssStatusProvider.addListener(listener, callerIdentity); - } - return true; - } finally { - Binder.restoreCallingIdentity(identity); - } - } + @Override + public boolean addGnssMeasurementsListener( + IGnssMeasurementsListener listener, String packageName) { + return addGnssDataListener(listener, packageName, "GnssMeasurementsListener", + mGnssMeasurementsProvider, mGnssMeasurementsListeners, + this::removeGnssMeasurementsListener); } @Override - public void unregisterGnssStatusCallback(IGnssStatusListener listener) { - if (mGnssStatusProvider == null) { - return; + public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) { + removeGnssDataListener(listener, mGnssMeasurementsProvider, mGnssMeasurementsListeners); + } + + private abstract static class LinkedListenerBase implements IBinder.DeathRecipient { + protected final CallerIdentity mCallerIdentity; + protected final String mListenerName; + + private LinkedListenerBase(@NonNull CallerIdentity callerIdentity, + @NonNull String listenerName) { + mCallerIdentity = callerIdentity; + mListenerName = listenerName; } + } - IBinder binder = listener.asBinder(); - synchronized (mLock) { - LinkedListener<IGnssStatusListener> linkedListener = - mGnssStatusListeners.remove(binder); - if (linkedListener == null) { - return; - } - unlinkFromListenerDeathNotificationLocked(binder, linkedListener); - mGnssStatusProvider.removeListener(listener); + private static class LinkedListener<TListener> extends LinkedListenerBase { + private final TListener mListener; + private final Consumer<TListener> mBinderDeathCallback; + + private LinkedListener(@NonNull TListener listener, String listenerName, + @NonNull CallerIdentity callerIdentity, + @NonNull Consumer<TListener> binderDeathCallback) { + super(callerIdentity, listenerName); + mListener = listener; + mBinderDeathCallback = binderDeathCallback; + } + + @Override + public void binderDied() { + if (D) Log.d(TAG, "Remote " + mListenerName + " died."); + mBinderDeathCallback.accept(mListener); } } - @Override - public boolean addGnssMeasurementsListener( - IGnssMeasurementsListener listener, String packageName) { - if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) { + private <TListener extends IInterface> boolean addGnssDataListener( + TListener listener, String packageName, String listenerName, + RemoteListenerHelper<TListener> gnssDataProvider, + ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners, + Consumer<TListener> binderDeathCallback) { + if (!hasGnssPermissions(packageName) || gnssDataProvider == null) { return false; } CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName); - LinkedListener<IGnssMeasurementsListener> linkedListener = new LinkedListener<>(listener, - "GnssMeasurementsListener", callerIdentity, this::removeGnssMeasurementsListener); + LinkedListener<TListener> linkedListener = new LinkedListener<>(listener, + listenerName, callerIdentity, binderDeathCallback); IBinder binder = listener.asBinder(); synchronized (mLock) { if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) { return false; } - mGnssMeasurementsListeners.put(binder, linkedListener); + gnssDataListeners.put(binder, linkedListener); long identity = Binder.clearCallingIdentity(); try { if (isThrottlingExemptLocked(callerIdentity) || isImportanceForeground( mActivityManager.getPackageImportance(packageName))) { - mGnssMeasurementsProvider.addListener(listener, callerIdentity); + gnssDataProvider.addListener(listener, callerIdentity); } return true; } finally { @@ -2790,25 +2775,24 @@ public class LocationManagerService extends ILocationManager.Stub { } } - @Override - public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) { - if (mGnssMeasurementsProvider == null) { + private <TListener extends IInterface> void removeGnssDataListener( + TListener listener, RemoteListenerHelper<TListener> gnssDataProvider, + ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners) { + if (gnssDataProvider == null) { return; } IBinder binder = listener.asBinder(); synchronized (mLock) { - LinkedListener<IGnssMeasurementsListener> linkedListener = - mGnssMeasurementsListeners.remove(binder); + LinkedListener<TListener> linkedListener = gnssDataListeners.remove(binder); if (linkedListener == null) { return; } unlinkFromListenerDeathNotificationLocked(binder, linkedListener); - mGnssMeasurementsProvider.removeListener(listener); + gnssDataProvider.removeListener(listener); } } - private boolean linkToListenerDeathNotificationLocked(IBinder binder, LinkedListenerBase linkedListener) { try { @@ -2817,8 +2801,7 @@ public class LocationManagerService extends ILocationManager.Stub { } catch (RemoteException e) { // if the remote process registering the listener is already dead, just swallow the // exception and return - Log.w(TAG, "Could not link " + linkedListener.mListenerName + " death callback.", - e); + Log.w(TAG, "Could not link " + linkedListener.mListenerName + " death callback.", e); return false; } } @@ -2831,8 +2814,7 @@ public class LocationManagerService extends ILocationManager.Stub { } catch (NoSuchElementException e) { // if the death callback isn't connected (it should be...), log error, // swallow the exception and return - Log.w(TAG, "Could not unlink " + linkedListener.mListenerName + " death callback.", - e); + Log.w(TAG, "Could not unlink " + linkedListener.mListenerName + " death callback.", e); return false; } } @@ -2864,52 +2846,15 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean addGnssNavigationMessageListener( IGnssNavigationMessageListener listener, String packageName) { - if (!hasGnssPermissions(packageName) || mGnssNavigationMessageProvider == null) { - return false; - } - - CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), - Binder.getCallingPid(), packageName); - LinkedListener<IGnssNavigationMessageListener> linkedListener = - new LinkedListener<>(listener, "GnssNavigationMessageListener", callerIdentity, - this::removeGnssNavigationMessageListener); - IBinder binder = listener.asBinder(); - synchronized (mLock) { - if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) { - return false; - } - - mGnssNavigationMessageListeners.put(binder, linkedListener); - long identity = Binder.clearCallingIdentity(); - try { - if (isThrottlingExemptLocked(callerIdentity) - || isImportanceForeground( - mActivityManager.getPackageImportance(packageName))) { - mGnssNavigationMessageProvider.addListener(listener, callerIdentity); - } - return true; - } finally { - Binder.restoreCallingIdentity(identity); - } - } + return addGnssDataListener(listener, packageName, "GnssNavigationMessageListener", + mGnssNavigationMessageProvider, mGnssNavigationMessageListeners, + this::removeGnssNavigationMessageListener); } @Override public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) { - if (mGnssNavigationMessageProvider == null) { - return; - } - - IBinder binder = listener.asBinder(); - synchronized (mLock) { - LinkedListener<IGnssNavigationMessageListener> linkedListener = - mGnssNavigationMessageListeners.remove(binder); - if (linkedListener == null) { - return; - } - unlinkFromListenerDeathNotificationLocked(binder, linkedListener); - mGnssNavigationMessageProvider.removeListener(listener); - } + removeGnssDataListener(listener, mGnssNavigationMessageProvider, + mGnssNavigationMessageListeners); } @Override diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index f505b76178a4..dc394d0ad482 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -20,18 +20,18 @@ import static android.Manifest.permission.CONNECTIVITY_INTERNAL; import static android.Manifest.permission.NETWORK_SETTINGS; import static android.Manifest.permission.NETWORK_STACK; import static android.Manifest.permission.SHUTDOWN; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; +import static android.net.INetd.FIREWALL_BLACKLIST; +import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; +import static android.net.INetd.FIREWALL_CHAIN_NONE; +import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; +import static android.net.INetd.FIREWALL_CHAIN_STANDBY; +import static android.net.INetd.FIREWALL_RULE_ALLOW; +import static android.net.INetd.FIREWALL_RULE_DENY; +import static android.net.INetd.FIREWALL_WHITELIST; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY; -import static android.net.NetworkPolicyManager.FIREWALL_TYPE_BLACKLIST; -import static android.net.NetworkPolicyManager.FIREWALL_TYPE_WHITELIST; import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_ALL; @@ -1946,7 +1946,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub int numUids = 0; if (DBG) Slog.d(TAG, "Closing sockets after enabling chain " + chainName); - if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) { + if (getFirewallType(chain) == FIREWALL_WHITELIST) { // Close all sockets on all non-system UIDs... ranges = new UidRange[] { // TODO: is there a better way of finding all existing users? If so, we could @@ -1958,7 +1958,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub final SparseIntArray rules = getUidFirewallRulesLR(chain); exemptUids = new int[rules.size()]; for (int i = 0; i < exemptUids.length; i++) { - if (rules.valueAt(i) == NetworkPolicyManager.FIREWALL_RULE_ALLOW) { + if (rules.valueAt(i) == FIREWALL_RULE_ALLOW) { exemptUids[numUids] = rules.keyAt(i); numUids++; } @@ -1980,7 +1980,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub final SparseIntArray rules = getUidFirewallRulesLR(chain); ranges = new UidRange[rules.size()]; for (int i = 0; i < ranges.length; i++) { - if (rules.valueAt(i) == NetworkPolicyManager.FIREWALL_RULE_DENY) { + if (rules.valueAt(i) == FIREWALL_RULE_DENY) { int uid = rules.keyAt(i); ranges[numUids] = new UidRange(uid, uid); numUids++; @@ -2052,13 +2052,13 @@ public class NetworkManagementService extends INetworkManagementService.Stub private int getFirewallType(int chain) { switch (chain) { case FIREWALL_CHAIN_STANDBY: - return FIREWALL_TYPE_BLACKLIST; + return FIREWALL_BLACKLIST; case FIREWALL_CHAIN_DOZABLE: - return FIREWALL_TYPE_WHITELIST; + return FIREWALL_WHITELIST; case FIREWALL_CHAIN_POWERSAVE: - return FIREWALL_TYPE_WHITELIST; + return FIREWALL_WHITELIST; default: - return isFirewallEnabled() ? FIREWALL_TYPE_WHITELIST : FIREWALL_TYPE_BLACKLIST; + return isFirewallEnabled() ? FIREWALL_WHITELIST : FIREWALL_BLACKLIST; } } @@ -2160,14 +2160,14 @@ public class NetworkManagementService extends INetworkManagementService.Stub private @NonNull String getFirewallRuleName(int chain, int rule) { String ruleName; - if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) { - if (rule == NetworkPolicyManager.FIREWALL_RULE_ALLOW) { + if (getFirewallType(chain) == FIREWALL_WHITELIST) { + if (rule == FIREWALL_RULE_ALLOW) { ruleName = "allow"; } else { ruleName = "deny"; } } else { // Blacklist mode - if (rule == NetworkPolicyManager.FIREWALL_RULE_DENY) { + if (rule == FIREWALL_RULE_DENY) { ruleName = "deny"; } else { ruleName = "allow"; @@ -2194,7 +2194,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub private int getFirewallRuleType(int chain, int rule) { if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) { - return getFirewallType(chain) == FIREWALL_TYPE_WHITELIST + return getFirewallType(chain) == FIREWALL_WHITELIST ? INetd.FIREWALL_RULE_DENY : INetd.FIREWALL_RULE_ALLOW; } return rule; diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 6b57fcd31450..710a0ba34d4f 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -30,11 +30,13 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.os.BatteryManager; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.PowerManager; import android.os.PowerManager.ServiceType; @@ -354,8 +356,12 @@ final class UiModeManagerService extends SystemService { try { synchronized (mLock) { if (mNightMode != mode) { - Settings.Secure.putIntForUser(getContext().getContentResolver(), - Settings.Secure.UI_NIGHT_MODE, mode, user); + // Only persist setting if not transient night mode or not in car mode + if (!shouldTransientNightWhenInCarMode() || !mCarModeEnabled) { + Settings.Secure.putIntForUser(getContext().getContentResolver(), + Settings.Secure.UI_NIGHT_MODE, mode, user); + } + mNightMode = mode; updateLocked(0, 0); } @@ -438,9 +444,39 @@ final class UiModeManagerService extends SystemService { } } + // Night mode settings in car mode are only persisted below Q. + // When targeting Q, changes are not saved and night mode will be re-read + // from settings when exiting car mode. + private boolean shouldTransientNightWhenInCarMode() { + int uid = Binder.getCallingUid(); + PackageManager packageManager = getContext().getPackageManager(); + String[] packagesForUid = packageManager.getPackagesForUid(uid); + if (packagesForUid == null || packagesForUid.length == 0) { + return false; + } + + try { + ApplicationInfo appInfo = packageManager.getApplicationInfoAsUser( + packagesForUid[0], 0, uid); + + return appInfo.targetSdkVersion >= Build.VERSION_CODES.Q; + } catch (PackageManager.NameNotFoundException ignored) { + } + + return false; + } + void setCarModeLocked(boolean enabled, int flags) { if (mCarModeEnabled != enabled) { mCarModeEnabled = enabled; + + // When transient night mode and exiting car mode, restore night mode from settings + if (shouldTransientNightWhenInCarMode() && !mCarModeEnabled) { + Context context = getContext(); + updateNightModeFromSettings(context, + context.getResources(), + UserHandle.getCallingUserId()); + } } mCarModeEnableFlags = flags; } @@ -498,7 +534,9 @@ final class UiModeManagerService extends SystemService { uiMode |= mNightMode << 4; } - if (mPowerSave) { + // Override night mode in power save mode if not transient night mode or not in car mode + boolean shouldOverrideNight = !mCarModeEnabled || !shouldTransientNightWhenInCarMode(); + if (mPowerSave && shouldOverrideNight) { uiMode &= ~Configuration.UI_MODE_NIGHT_NO; uiMode |= Configuration.UI_MODE_NIGHT_YES; } diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 24543b7974df..236797b57556 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -477,6 +477,10 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { mStats.updateRpmStatsLocked(); } + if ((updateFlags & UPDATE_RAIL) != 0) { + mStats.updateRailStatsLocked(); + } + if (bluetoothInfo != null) { if (bluetoothInfo.isValid()) { mStats.updateBluetoothStateLocked(bluetoothInfo); diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 08900328a200..4d5cb8cb4473 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -59,6 +59,7 @@ import com.android.internal.app.IBatteryStats; import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.PowerProfile; +import com.android.internal.os.RailStats; import com.android.internal.os.RpmStats; import com.android.internal.util.DumpUtils; import com.android.internal.util.ParseUtils; @@ -84,7 +85,8 @@ import java.util.concurrent.Future; */ public final class BatteryStatsService extends IBatteryStats.Stub implements PowerManagerInternal.LowPowerModeListener, - BatteryStatsImpl.PlatformIdleStateCallback { + BatteryStatsImpl.PlatformIdleStateCallback, + BatteryStatsImpl.RailEnergyDataCallback { static final String TAG = "BatteryStatsService"; static final boolean DBG = false; @@ -98,6 +100,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub private native void getLowPowerStats(RpmStats rpmStats); private native int getPlatformLowPowerStats(ByteBuffer outBuffer); private native int getSubsystemLowPowerStats(ByteBuffer outBuffer); + private native void getRailEnergyPowerStats(RailStats railStats); private CharsetDecoder mDecoderStat = StandardCharsets.UTF_8 .newDecoder() .onMalformedInput(CodingErrorAction.REPLACE) @@ -121,6 +124,16 @@ public final class BatteryStatsService extends IBatteryStats.Stub } @Override + public void fillRailDataStats(RailStats railStats) { + if (DBG) Slog.d(TAG, "begin getRailEnergyPowerStats"); + try { + getRailEnergyPowerStats(railStats); + } finally { + if (DBG) Slog.d(TAG, "end getRailEnergyPowerStats"); + } + } + + @Override public String getPlatformLowPowerStats() { if (DBG) Slog.d(TAG, "begin getPlatformLowPowerStats"); try { @@ -177,7 +190,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub return (umi != null) ? umi.getUserIds() : null; } }; - mStats = new BatteryStatsImpl(systemDir, handler, this, mUserManagerUserInfoProvider); + mStats = new BatteryStatsImpl(systemDir, handler, this, + this, mUserManagerUserInfoProvider); mWorker = new BatteryExternalStatsWorker(context, mStats); mStats.setExternalStatsSyncLocked(mWorker); mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger( @@ -1460,7 +1474,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub in.unmarshall(raw, 0, raw.length); in.setDataPosition(0); BatteryStatsImpl checkinStats = new BatteryStatsImpl( - null, mStats.mHandler, null, mUserManagerUserInfoProvider); + null, mStats.mHandler, null, null, + mUserManagerUserInfoProvider); checkinStats.readSummaryFromParcel(in); in.recycle(); checkinStats.dumpProtoLocked( @@ -1498,7 +1513,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub in.unmarshall(raw, 0, raw.length); in.setDataPosition(0); BatteryStatsImpl checkinStats = new BatteryStatsImpl( - null, mStats.mHandler, null, mUserManagerUserInfoProvider); + null, mStats.mHandler, null, null, + mUserManagerUserInfoProvider); checkinStats.readSummaryFromParcel(in); in.recycle(); checkinStats.dumpCheckinLocked(mContext, pw, apps, flags, diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 93a71e5a2ed4..4e03b72e6ce6 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1692,7 +1692,8 @@ public final class OomAdjuster { (app.curAdj == ProcessList.PREVIOUS_APP_ADJ || app.curAdj == ProcessList.HOME_APP_ADJ)) { mAppCompact.compactAppSome(app); - } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ + } else if ((app.setAdj < ProcessList.CACHED_APP_MIN_ADJ + || app.setAdj > ProcessList.CACHED_APP_MAX_ADJ) && app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) { mAppCompact.compactAppFull(app); diff --git a/services/core/java/com/android/server/appbinding/AppBindingService.java b/services/core/java/com/android/server/appbinding/AppBindingService.java index 0b6a4329d15b..bbe4ed15b3a0 100644 --- a/services/core/java/com/android/server/appbinding/AppBindingService.java +++ b/services/core/java/com/android/server/appbinding/AppBindingService.java @@ -177,13 +177,12 @@ public class AppBindingService extends Binder { * Handle boot phase PHASE_ACTIVITY_MANAGER_READY. */ private void onPhaseActivityManagerReady() { + // RoleManager doesn't tell us about upgrade, so we still need to listen for app upgrades. + // (app uninstall/disable will be notified by RoleManager.) final IntentFilter packageFilter = new IntentFilter(); packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); - packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); - packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); packageFilter.addDataScheme("package"); - packageFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiverAsUser(mPackageUserMonitor, UserHandle.ALL, packageFilter, null, mHandler); @@ -256,14 +255,6 @@ public class AppBindingService extends Binder { handlePackageAddedReplacing(packageName, userId); } break; - case Intent.ACTION_PACKAGE_REMOVED: - if (!replacing) { - handlePackageRemoved(packageName, userId); - } - break; - case Intent.ACTION_PACKAGE_CHANGED: - handlePackageChanged(packageName, userId); - break; } } }; @@ -371,31 +362,6 @@ public class AppBindingService extends Binder { } } - private void handlePackageRemoved(String packageName, int userId) { - if (DEBUG) { - Slog.d(TAG, "handlePackageRemoved: u" + userId + " " + packageName); - } - synchronized (mLock) { - final AppServiceFinder finder = findFinderLocked(userId, packageName); - if (finder != null) { - unbindServicesLocked(userId, finder, "package uninstall"); - } - } - } - - private void handlePackageChanged(String packageName, int userId) { - if (DEBUG) { - Slog.d(TAG, "handlePackageChanged: u" + userId + " " + packageName); - } - synchronized (mLock) { - final AppServiceFinder finder = findFinderLocked(userId, packageName); - if (finder != null) { - unbindServicesLocked(userId, finder, "package changed"); - bindServicesLocked(userId, finder, "package changed"); - } - } - } - private void rebindAllLocked(String reason) { for (int i = 0; i < mRunningUsers.size(); i++) { if (!mRunningUsers.valueAt(i)) { diff --git a/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java index 4c5f1a1c7b49..3663518bf7b9 100644 --- a/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java +++ b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java @@ -16,14 +16,10 @@ package com.android.server.appbinding.finders; -import static android.provider.Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL; - import android.Manifest.permission; -import android.content.BroadcastReceiver; -import android.content.ComponentName; +import android.app.role.OnRoleHoldersChangedListener; +import android.app.role.RoleManager; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.ServiceInfo; import android.os.Handler; import android.os.IBinder; @@ -35,7 +31,8 @@ import android.text.TextUtils; import android.util.Slog; import com.android.internal.R; -import com.android.internal.telephony.SmsApplication; +import com.android.internal.os.BackgroundThread; +import com.android.internal.util.CollectionUtils; import com.android.server.appbinding.AppBindingConstants; import java.util.function.BiConsumer; @@ -45,10 +42,15 @@ import java.util.function.BiConsumer; */ public class CarrierMessagingClientServiceFinder extends AppServiceFinder<CarrierMessagingClientService, ICarrierMessagingClientService> { + + private final RoleManager mRoleManager; + public CarrierMessagingClientServiceFinder(Context context, BiConsumer<AppServiceFinder, Integer> listener, Handler callbackHandler) { super(context, listener, callbackHandler); + + mRoleManager = context.getSystemService(RoleManager.class); } @Override @@ -84,9 +86,8 @@ public class CarrierMessagingClientServiceFinder @Override public String getTargetPackage(int userId) { - final ComponentName cn = SmsApplication.getDefaultSmsApplicationAsUser( - mContext, /* updateIfNeeded= */ true, userId); - String ret = cn == null ? null : cn.getPackageName(); + final String ret = CollectionUtils.firstOrNull(mRoleManager.getRoleHoldersAsUser( + RoleManager.ROLE_SMS, UserHandle.of(userId))); if (DEBUG) { Slog.d(TAG, "getTargetPackage()=" + ret); @@ -97,9 +98,8 @@ public class CarrierMessagingClientServiceFinder @Override public void startMonitoring() { - final IntentFilter filter = new IntentFilter(ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL); - mContext.registerReceiverAsUser(mSmsAppChangedWatcher, UserHandle.ALL, filter, - /* permission= */ null, mHandler); + mRoleManager.addOnRoleHoldersChangedListenerAsUser( + BackgroundThread.getExecutor(), mRoleHolderChangedListener, UserHandle.ALL); } @Override @@ -118,12 +118,9 @@ public class CarrierMessagingClientServiceFinder return constants.SMS_APP_BIND_FLAGS; } - private final BroadcastReceiver mSmsAppChangedWatcher = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL.equals(intent.getAction())) { - mListener.accept(CarrierMessagingClientServiceFinder.this, getSendingUserId()); - } + private final OnRoleHoldersChangedListener mRoleHolderChangedListener = (role, user) -> { + if (RoleManager.ROLE_SMS.equals(role)) { + mListener.accept(CarrierMessagingClientServiceFinder.this, user.getIdentifier()); } }; } diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java index 9a1d7bf0053d..47c9b8639760 100644 --- a/services/core/java/com/android/server/attention/AttentionManagerService.java +++ b/services/core/java/com/android/server/attention/AttentionManagerService.java @@ -104,8 +104,7 @@ public class AttentionManagerService extends SystemService { @Override public void onSwitchUser(int userId) { - cancelAndUnbindLocked(peekUserStateLocked(userId), - AttentionService.ATTENTION_FAILURE_UNKNOWN); + cancelAndUnbindLocked(peekUserStateLocked(userId)); } /** Resolves and sets up the attention service if it had not been done yet. */ @@ -152,7 +151,8 @@ public class AttentionManagerService extends SystemService { } synchronized (mLock) { - unbindAfterTimeoutLocked(); + final long now = SystemClock.uptimeMillis(); + freeIfInactiveLocked(); final UserState userState = getOrCreateCurrentUserStateLocked(); // lazily start the service, which should be very lightweight to start @@ -172,7 +172,7 @@ public class AttentionManagerService extends SystemService { try { // throttle frequent requests final AttentionCheckCache attentionCheckCache = userState.mAttentionCheckCache; - if (attentionCheckCache != null && SystemClock.uptimeMillis() + if (attentionCheckCache != null && now < attentionCheckCache.mLastComputed + STALE_AFTER_MILLIS) { callback.onSuccess(requestCode, attentionCheckCache.mResult, attentionCheckCache.mTimestamp); @@ -190,6 +190,7 @@ public class AttentionManagerService extends SystemService { userState.mAttentionCheckCache = new AttentionCheckCache( SystemClock.uptimeMillis(), result, timestamp); + userState.mCurrentAttentionCheckIsFulfilled = true; } StatsLog.write(StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED, result); @@ -198,14 +199,10 @@ public class AttentionManagerService extends SystemService { @Override public void onFailure(int requestCode, int error) { callback.onFailure(requestCode, error); + userState.mCurrentAttentionCheckIsFulfilled = true; StatsLog.write(StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED, error); } - - @Override - public IBinder asBinder() { - return null; - } }); } catch (RemoteException e) { Slog.e(LOG_TAG, "Cannot call into the AttentionService"); @@ -219,7 +216,10 @@ public class AttentionManagerService extends SystemService { /** Cancels the specified attention check. */ public void cancelAttentionCheck(int requestCode) { synchronized (mLock) { - final UserState userState = getOrCreateCurrentUserStateLocked(); + final UserState userState = peekCurrentUserStateLocked(); + if (userState == null) { + return; + } if (userState.mService == null) { if (userState.mPendingAttentionCheck != null && userState.mPendingAttentionCheck.mRequestCode == requestCode) { @@ -236,8 +236,12 @@ public class AttentionManagerService extends SystemService { } @GuardedBy("mLock") - private void unbindAfterTimeoutLocked() { - mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.CONNECTION_EXPIRED, + private void freeIfInactiveLocked() { + // If we are called here, it means someone used the API again - reset the timer then. + mAttentionHandler.removeMessages(AttentionHandler.CHECK_CONNECTION_EXPIRATION); + + // Schedule resources cleanup if no one calls the API again. + mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.CHECK_CONNECTION_EXPIRATION, CONNECTION_TTL_MILLIS); } @@ -264,12 +268,14 @@ public class AttentionManagerService extends SystemService { } @GuardedBy("mLock") - UserState peekCurrentUserStateLocked() { + @Nullable + private UserState peekCurrentUserStateLocked() { return peekUserStateLocked(ActivityManager.getCurrentUser()); } @GuardedBy("mLock") - UserState peekUserStateLocked(int userId) { + @Nullable + private UserState peekUserStateLocked(int userId) { return mUserStates.get(userId); } @@ -406,6 +412,8 @@ public class AttentionManagerService extends SystemService { @GuardedBy("mLock") int mCurrentAttentionCheckRequestCode; @GuardedBy("mLock") + boolean mCurrentAttentionCheckIsFulfilled; + @GuardedBy("mLock") PendingAttentionCheck mPendingAttentionCheck; @GuardedBy("mLock") @@ -501,7 +509,7 @@ public class AttentionManagerService extends SystemService { } private class AttentionHandler extends Handler { - private static final int CONNECTION_EXPIRED = 1; + private static final int CHECK_CONNECTION_EXPIRATION = 1; private static final int ATTENTION_CHECK_TIMEOUT = 2; AttentionHandler() { @@ -511,19 +519,26 @@ public class AttentionManagerService extends SystemService { public void handleMessage(Message msg) { switch (msg.what) { // Do not occupy resources when not in use - unbind proactively. - case CONNECTION_EXPIRED: { + case CHECK_CONNECTION_EXPIRATION: { for (int i = 0; i < mUserStates.size(); i++) { - cancelAndUnbindLocked(mUserStates.valueAt(i), - AttentionService.ATTENTION_FAILURE_UNKNOWN); + cancelAndUnbindLocked(mUserStates.valueAt(i)); } - } break; // Callee is no longer interested in the attention check result - cancel. case ATTENTION_CHECK_TIMEOUT: { - cancelAndUnbindLocked(peekCurrentUserStateLocked(), - AttentionService.ATTENTION_FAILURE_TIMED_OUT); + synchronized (mLock) { + final UserState userState = peekCurrentUserStateLocked(); + if (userState != null) { + // If not called back already. + if (!userState.mCurrentAttentionCheckIsFulfilled) { + cancel(userState, + AttentionService.ATTENTION_FAILURE_TIMED_OUT); + } + + } + } } break; @@ -533,25 +548,29 @@ public class AttentionManagerService extends SystemService { } } + private void cancel(UserState userState, @AttentionFailureCodes int failureCode) { + if (userState != null && userState.mService != null) { + try { + userState.mService.cancelAttentionCheck( + userState.mCurrentAttentionCheckRequestCode); + } catch (RemoteException e) { + Slog.e(LOG_TAG, "Unable to cancel attention check"); + } + + if (userState.mPendingAttentionCheck != null) { + userState.mPendingAttentionCheck.cancel(failureCode); + } + } + } + @GuardedBy("mLock") - private void cancelAndUnbindLocked(UserState userState, - @AttentionFailureCodes int failureCode) { + private void cancelAndUnbindLocked(UserState userState) { synchronized (mLock) { - if (userState != null && userState.mService != null) { - try { - userState.mService.cancelAttentionCheck( - userState.mCurrentAttentionCheckRequestCode); - } catch (RemoteException e) { - Slog.e(LOG_TAG, "Unable to cancel attention check"); - } + cancel(userState, AttentionService.ATTENTION_FAILURE_UNKNOWN); - if (userState.mPendingAttentionCheck != null) { - userState.mPendingAttentionCheck.cancel(failureCode); - } - mContext.unbindService(userState.mConnection); - userState.mConnection.cleanupService(); - mUserStates.remove(userState.mUserId); - } + mContext.unbindService(userState.mConnection); + userState.mConnection.cleanupService(); + mUserStates.remove(userState.mUserId); } } @@ -563,8 +582,7 @@ public class AttentionManagerService extends SystemService { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { - cancelAndUnbindLocked(peekCurrentUserStateLocked(), - AttentionService.ATTENTION_FAILURE_UNKNOWN); + cancelAndUnbindLocked(peekCurrentUserStateLocked()); } } } diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 6df60d6bdd3a..9af57daa259b 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -280,9 +280,9 @@ import java.util.ArrayList; AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource); sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE, AudioSystem.FOR_RECORD, mForcedUseForComm, eventSource); - // Un-mute ringtone stream volume - mAudioService.setUpdateRingerModeServiceInt(); } + // Un-mute ringtone stream volume + mAudioService.postUpdateRingerModeServiceInt(); } /*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index a6643d49c79f..d902201df212 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -248,6 +248,7 @@ public class AudioService extends IAudioService.Stub private static final int MSG_NOTIFY_VOL_EVENT = 22; private static final int MSG_DISPATCH_AUDIO_SERVER_STATE = 23; private static final int MSG_ENABLE_SURROUND_FORMATS = 24; + private static final int MSG_UPDATE_RINGER_MODE = 25; // start of messages handled under wakelock // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...) @@ -753,6 +754,7 @@ public class AudioService extends IAudioService.Stub intentFilter.addAction(Intent.ACTION_USER_FOREGROUND); intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); + intentFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED); intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); mMonitorRotation = SystemProperties.getBoolean("ro.audio.monitorRotation", false); @@ -2720,7 +2722,11 @@ public class AudioService extends IAudioService.Stub } } - /*package*/ void setUpdateRingerModeServiceInt() { + /*package*/ void postUpdateRingerModeServiceInt() { + sendMsg(mAudioHandler, MSG_UPDATE_RINGER_MODE, SENDMSG_QUEUE, 0, 0, null, 0); + } + + private void onUpdateRingerModeServiceInt() { setRingerModeInt(getRingerModeInternal(), false); } @@ -3217,6 +3223,21 @@ public class AudioService extends IAudioService.Stub if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) { return; } + + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_PHONE_STATE) + != PackageManager.PERMISSION_GRANTED) { + synchronized (mSetModeDeathHandlers) { + for (SetModeDeathHandler h : mSetModeDeathHandlers) { + if (h.getMode() == AudioSystem.MODE_IN_CALL) { + Log.w(TAG, "getMode is call, Permission Denial: setSpeakerphoneOn from pid=" + + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); + return; + } + } + } + } + // for logging only final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on) .append(") from u/pid:").append(Binder.getCallingUid()).append("/") @@ -4944,6 +4965,10 @@ public class AudioService extends IAudioService.Stub case MSG_ENABLE_SURROUND_FORMATS: onEnableSurroundFormats((ArrayList<Integer>) msg.obj); break; + + case MSG_UPDATE_RINGER_MODE: + onUpdateRingerModeServiceInt(); + break; } } } @@ -5159,6 +5184,20 @@ public class AudioService extends IAudioService.Stub } else if (action.equals(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION) || action.equals(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION)) { handleAudioEffectBroadcast(context, intent); + } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) { + final int[] suspendedUids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST); + final String[] suspendedPackages = + intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + if (suspendedPackages == null || suspendedUids == null + || suspendedPackages.length != suspendedUids.length) { + return; + } + for (int i = 0; i < suspendedUids.length; i++) { + if (!TextUtils.isEmpty(suspendedPackages[i])) { + mMediaFocusControl.noFocusForSuspendedApp( + suspendedPackages[i], suspendedUids[i]); + } + } } } } // end class AudioServiceBroadcastReceiver @@ -5323,6 +5362,11 @@ public class AudioService extends IAudioService.Stub } } + if (callingPackageName == null || clientId == null || aa == null) { + Log.e(TAG, "Invalid null parameter to request audio focus"); + return AudioManager.AUDIOFOCUS_REQUEST_FAILED; + } + return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd, clientId, callingPackageName, flags, sdk, forceFocusDuckingForAccessibility(aa, durationHint, Binder.getCallingUid())); diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java index 99f08405a375..db55138e446d 100644 --- a/services/core/java/com/android/server/audio/FocusRequester.java +++ b/services/core/java/com/android/server/audio/FocusRequester.java @@ -45,8 +45,8 @@ public class FocusRequester { private AudioFocusDeathHandler mDeathHandler; // may be null private IAudioFocusDispatcher mFocusDispatcher; // may be null private final IBinder mSourceRef; // may be null - private final String mClientId; - private final String mPackageName; + private final @NonNull String mClientId; + private final @NonNull String mPackageName; private final int mCallingUid; private final MediaFocusControl mFocusController; // never null private final int mSdkTarget; @@ -72,7 +72,7 @@ public class FocusRequester { /** * the audio attributes associated with the focus request */ - private final AudioAttributes mAttributes; + private final @NonNull AudioAttributes mAttributes; /** * Class constructor @@ -87,9 +87,10 @@ public class FocusRequester { * @param uid * @param ctlr cannot be null */ - FocusRequester(AudioAttributes aa, int focusRequest, int grantFlags, - IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr, - String pn, int uid, @NonNull MediaFocusControl ctlr, int sdk) { + FocusRequester(@NonNull AudioAttributes aa, int focusRequest, int grantFlags, + IAudioFocusDispatcher afl, IBinder source, @NonNull String id, + AudioFocusDeathHandler hdlr, @NonNull String pn, int uid, + @NonNull MediaFocusControl ctlr, int sdk) { mAttributes = aa; mFocusDispatcher = afl; mSourceRef = source; @@ -124,11 +125,7 @@ public class FocusRequester { } boolean hasSameClient(String otherClient) { - try { - return mClientId.compareTo(otherClient) == 0; - } catch (NullPointerException e) { - return false; - } + return mClientId.compareTo(otherClient) == 0; } boolean isLockedFocusOwner() { @@ -143,12 +140,8 @@ public class FocusRequester { return (mFocusDispatcher != null) && mFocusDispatcher.equals(fd); } - boolean hasSamePackage(String pack) { - try { - return mPackageName.compareTo(pack) == 0; - } catch (NullPointerException e) { - return false; - } + boolean hasSamePackage(@NonNull String pack) { + return mPackageName.compareTo(pack) == 0; } boolean hasSameUid(int uid) { diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java index d023bd7827ff..b4bbbc729505 100644 --- a/services/core/java/com/android/server/audio/MediaFocusControl.java +++ b/services/core/java/com/android/server/audio/MediaFocusControl.java @@ -24,7 +24,6 @@ import android.media.AudioFocusInfo; import android.media.AudioManager; import android.media.AudioSystem; import android.media.IAudioFocusDispatcher; -import android.media.audiopolicy.AudioPolicy; import android.media.audiopolicy.IAudioPolicyCallback; import android.os.Binder; import android.os.Build; @@ -35,6 +34,7 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; +import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -44,7 +44,6 @@ import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.Stack; -import java.text.DateFormat; /** * @hide @@ -138,6 +137,30 @@ public class MediaFocusControl implements PlayerFocusEnforcer { private static final AudioEventLogger mEventLogger = new AudioEventLogger(50, "focus commands as seen by MediaFocusControl"); + /*package*/ void noFocusForSuspendedApp(@NonNull String packageName, int uid) { + synchronized (mAudioFocusLock) { + final Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); + List<String> clientsToRemove = new ArrayList<>(); + while (stackIterator.hasNext()) { + final FocusRequester focusOwner = stackIterator.next(); + if (focusOwner.hasSameUid(uid) && focusOwner.hasSamePackage(packageName)) { + clientsToRemove.add(focusOwner.getClientId()); + mEventLogger.log((new AudioEventLogger.StringEvent( + "focus owner:" + focusOwner.getClientId() + + " in uid:" + uid + " pack: " + packageName + + " getting AUDIOFOCUS_LOSS due to app suspension")) + .printLog(TAG)); + // make the suspended app lose focus through its focus listener (if any) + focusOwner.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS); + } + } + for (String clientToRemove : clientsToRemove) { + // update the stack but don't signal the change. + removeFocusStackEntry(clientToRemove, false, true); + } + } + } + /** * Discard the current audio focus owner. * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign @@ -688,9 +711,9 @@ public class MediaFocusControl implements PlayerFocusEnforcer { } /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int, int) */ - protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb, - IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags, - int sdk, boolean forceDuck) { + protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb, + IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName, + int flags, int sdk, boolean forceDuck) { mEventLogger.log((new AudioEventLogger.StringEvent( "requestAudioFocus() from uid/pid " + Binder.getCallingUid() + "/" + Binder.getCallingPid() diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java index bd4acdbf4070..7d4ac592a9df 100644 --- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java @@ -34,7 +34,7 @@ public abstract class AuthenticationClient extends ClientMonitor { private long mOpId; public abstract int handleFailedAttempt(); - public abstract void resetFailedAttempts(); + public void resetFailedAttempts() {} public static final int LOCKOUT_NONE = 0; public static final int LOCKOUT_TIMED = 1; @@ -42,6 +42,11 @@ public abstract class AuthenticationClient extends ClientMonitor { private final boolean mRequireConfirmation; + // We need to track this state since it's possible for applications to request for + // authentication while the device is already locked out. In that case, the client is created + // but not started yet. The user shouldn't receive the error haptics in this case. + private boolean mStarted; + /** * This method is called when authentication starts. */ @@ -53,6 +58,11 @@ public abstract class AuthenticationClient extends ClientMonitor { */ public abstract void onStop(); + /** + * @return true if the framework should handle lockout. + */ + public abstract boolean shouldFrameworkHandleLockout(); + public AuthenticationClient(Context context, Metrics metrics, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId, @@ -91,6 +101,23 @@ public abstract class AuthenticationClient extends ClientMonitor { } @Override + public boolean onError(long deviceId, int error, int vendorCode) { + if (!shouldFrameworkHandleLockout()) { + switch (error) { + case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT: + case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT: + if (mStarted) { + vibrateError(); + } + break; + default: + break; + } + } + return super.onError(deviceId, error, vendorCode); + } + + @Override public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> token) { super.logOnAuthenticated(authenticated, mRequireConfirmation, getTargetUserId(), @@ -113,7 +140,9 @@ public abstract class AuthenticationClient extends ClientMonitor { vibrateSuccess(); } result = true; - resetFailedAttempts(); + if (shouldFrameworkHandleLockout()) { + resetFailedAttempts(); + } onStop(); final byte[] byteToken = new byte[token.size()]; @@ -147,9 +176,10 @@ public abstract class AuthenticationClient extends ClientMonitor { if (listener != null) { vibrateError(); } + // Allow system-defined limit of number of attempts before giving up final int lockoutMode = handleFailedAttempt(); - if (lockoutMode != LOCKOUT_NONE) { + if (lockoutMode != LOCKOUT_NONE && shouldFrameworkHandleLockout()) { Slog.w(getLogTag(), "Forcing lockout (driver code should do this!), mode(" + lockoutMode + ")"); stop(false); @@ -170,7 +200,7 @@ public abstract class AuthenticationClient extends ClientMonitor { } } } - result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode + result = lockoutMode != LOCKOUT_NONE; // in a lockout mode } } catch (RemoteException e) { Slog.e(getLogTag(), "Remote exception", e); @@ -184,6 +214,7 @@ public abstract class AuthenticationClient extends ClientMonitor { */ @Override public int start() { + mStarted = true; onStart(); try { final int result = getDaemonWrapper().authenticate(mOpId, getGroupId()); @@ -209,6 +240,8 @@ public abstract class AuthenticationClient extends ClientMonitor { return 0; } + mStarted = false; + onStop(); try { diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index bca84f7b7217..ddd416e14164 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -273,16 +273,13 @@ public class BiometricService extends SystemService { */ private static final int STATE_AUTH_STARTED = 2; /** - * Authentication is paused, waiting for the user to press "try again" button. Since the - * try again button requires us to cancel authentication, this represents the state where - * ERROR_CANCELED is not received yet. + * Authentication is paused, waiting for the user to press "try again" button. Only + * passive modalities such as Face or Iris should have this state. Note that for passive + * modalities, the HAL enters the idle state after onAuthenticated(false) which differs from + * fingerprint. */ private static final int STATE_AUTH_PAUSED = 3; /** - * Same as above, except the ERROR_CANCELED has been received. - */ - private static final int STATE_AUTH_PAUSED_CANCELED = 4; - /** * Authentication is successful, but we're waiting for the user to press "confirm" button. */ private static final int STATE_AUTH_PENDING_CONFIRM = 5; @@ -457,11 +454,6 @@ public class BiometricService extends SystemService { // Pause authentication. onBiometricAuthenticated(false) causes the // dialog to show a "try again" button for passive modalities. mCurrentAuthSession.mState = STATE_AUTH_PAUSED; - // Cancel authentication. Skip the token/package check since we are - // cancelling from system server. The interface is permission protected so - // this is fine. - cancelInternal(null /* token */, null /* package */, - false /* fromClient */); } mCurrentAuthSession.mClientReceiver.onAuthenticationFailed(); @@ -507,24 +499,15 @@ public class BiometricService extends SystemService { } }, BiometricPrompt.HIDE_DIALOG_DELAY); } - } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED - || mCurrentAuthSession.mState == STATE_AUTH_PAUSED_CANCELED) { - if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED - && error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) { - // Skip the first ERROR_CANCELED message when this happens, since - // "try again" requires us to cancel authentication but keep - // the prompt showing. - mCurrentAuthSession.mState = STATE_AUTH_PAUSED_CANCELED; - } else { - // In the "try again" state, we should forward canceled errors to - // the client and and clean up. - mCurrentAuthSession.mClientReceiver.onError(error, message); - mStatusBarService.onBiometricError(message); - mActivityTaskManager.unregisterTaskStackListener( - mTaskStackListener); - mCurrentAuthSession.mState = STATE_AUTH_IDLE; - mCurrentAuthSession = null; - } + } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) { + // In the "try again" state, we should forward canceled errors to + // the client and and clean up. + mCurrentAuthSession.mClientReceiver.onError(error, message); + mStatusBarService.onBiometricError(message); + mActivityTaskManager.unregisterTaskStackListener( + mTaskStackListener); + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; } else { Slog.e(TAG, "Impossible session error state: " + mCurrentAuthSession.mState); @@ -705,8 +688,7 @@ public class BiometricService extends SystemService { if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) { final boolean continuing = mCurrentAuthSession != null && - (mCurrentAuthSession.mState == STATE_AUTH_PAUSED - || mCurrentAuthSession.mState == STATE_AUTH_PAUSED_CANCELED); + (mCurrentAuthSession.mState == STATE_AUTH_PAUSED); mCurrentAuthSession = mPendingAuthSession; mPendingAuthSession = null; @@ -1029,7 +1011,7 @@ public class BiometricService extends SystemService { } @Override // Binder call - public void resetTimeout(byte[] token) { + public void resetLockout(byte[] token) { checkInternalPermission(); final long ident = Binder.clearCallingIdentity(); try { @@ -1037,7 +1019,7 @@ public class BiometricService extends SystemService { mFingerprintService.resetTimeout(token); } if (mFaceService != null) { - mFaceService.resetTimeout(token); + mFaceService.resetLockout(token); } } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java index 9e0f2fcaa29f..92a8d9359b58 100644 --- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java @@ -20,17 +20,12 @@ import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREG import android.app.ActivityManager; import android.app.ActivityTaskManager; -import android.app.AlarmManager; import android.app.AppOpsManager; import android.app.IActivityTaskManager; -import android.app.PendingIntent; import android.app.SynchronousUserSwitchObserver; import android.app.TaskStackListener; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricAuthenticator; @@ -54,14 +49,11 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.util.Slog; -import android.util.SparseBooleanArray; -import android.util.SparseIntArray; import android.util.StatsLog; import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.server.SystemService; -import com.android.server.biometrics.fingerprint.FingerprintService; import java.util.ArrayList; import java.util.Collections; @@ -80,38 +72,36 @@ public abstract class BiometricServiceBase extends SystemService protected static final boolean DEBUG = true; + private static final boolean CLEANUP_UNKNOWN_TEMPLATES = true; private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user"; private static final int MSG_USER_SWITCHING = 10; - private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30 * 1000; private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms private final Context mContext; private final String mKeyguardPackage; - private final SparseBooleanArray mTimedLockoutCleared; - private final SparseIntArray mFailedAttempts; private final IActivityTaskManager mActivityTaskManager; - private final AlarmManager mAlarmManager; private final PowerManager mPowerManager; private final UserManager mUserManager; private final MetricsLogger mMetricsLogger; private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener(); private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable(); - private final LockoutReceiver mLockoutReceiver = new LockoutReceiver(); private final ArrayList<LockoutResetMonitor> mLockoutMonitors = new ArrayList<>(); protected final IStatusBarService mStatusBarService; protected final Map<Integer, Long> mAuthenticatorIds = Collections.synchronizedMap(new HashMap<>()); - protected final ResetFailedAttemptsForUserRunnable mResetFailedAttemptsForCurrentUserRunnable = - new ResetFailedAttemptsForUserRunnable(); protected final AppOpsManager mAppOps; protected final H mHandler = new H(); + private final IBinder mToken = new Binder(); // Used for internal enumeration + private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>(); + private IBiometricService mBiometricService; private ClientMonitor mCurrentClient; private ClientMonitor mPendingClient; private PerformanceStats mPerformanceStats; protected int mCurrentUserId = UserHandle.USER_NULL; + protected long mHalDeviceId; // Tracks if the current authentication makes use of CryptoObjects. protected boolean mIsCrypto; // Normal authentications are tracked by mPerformanceMap. @@ -135,21 +125,14 @@ public abstract class BiometricServiceBase extends SystemService protected abstract String getTag(); /** - * @return the biometric utilities for a specific implementation. - */ - protected abstract BiometricUtils getBiometricUtils(); - - /** - * @return the number of failed attempts after which the user will be temporarily locked out - * from using the biometric. A strong auth (pin/pattern/pass) clears this counter. + * @return wrapper for the HAL */ - protected abstract int getFailedAttemptsLockoutTimed(); + protected abstract DaemonWrapper getDaemonWrapper(); /** - * @return the number of failed attempts after which the user will be permanently locked out - * from using the biometric. A strong auth (pin/pattern/pass) clears this counter. + * @return the biometric utilities for a specific implementation. */ - protected abstract int getFailedAttemptsLockoutPermanent(); + protected abstract BiometricUtils getBiometricUtils(); /** * @return the metrics constants for a biometric implementation. @@ -186,13 +169,6 @@ public abstract class BiometricServiceBase extends SystemService protected abstract long getHalDeviceId(); /** - * This method is called when the user switches. Implementations should probably notify the - * HAL. - * @param userId - */ - protected abstract void handleUserSwitching(int userId); - - /** * @param userId * @return Returns true if the user has any enrolled biometrics. */ @@ -215,6 +191,9 @@ public abstract class BiometricServiceBase extends SystemService */ protected abstract boolean checkAppOps(int uid, String opPackageName); + protected abstract List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates( + int userId); + /** * Notifies clients of any change in the biometric state (active / idle). This is mainly for * Fingerprint navigation gestures. @@ -224,6 +203,11 @@ public abstract class BiometricServiceBase extends SystemService protected abstract int statsModality(); + /** + * @return one of the AuthenticationClient LOCKOUT constants + */ + protected abstract int getLockoutMode(); + protected abstract class AuthenticationClientImpl extends AuthenticationClient { // Used to check if the public API that was invoked was from FingerprintManager. Only @@ -271,21 +255,12 @@ public abstract class BiometricServiceBase extends SystemService } @Override - public void resetFailedAttempts() { - resetFailedAttemptsForUser(true /* clearAttemptCounter */, - ActivityManager.getCurrentUser()); - } - - @Override public void notifyUserActivity() { userActivity(); } @Override public int handleFailedAttempt() { - final int currentUser = ActivityManager.getCurrentUser(); - mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1); - mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false); final int lockoutMode = getLockoutMode(); if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) { mPerformanceStats.permanentLockout++; @@ -295,7 +270,6 @@ public abstract class BiometricServiceBase extends SystemService // Failing multiple times will continue to push out the lockout time if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) { - scheduleLockoutResetForUser(currentUser); return lockoutMode; } return AuthenticationClient.LOCKOUT_NONE; @@ -319,40 +293,106 @@ public abstract class BiometricServiceBase extends SystemService } } - protected abstract class RemovalClientImpl extends RemovalClient { - private boolean mShouldNotify; - - public RemovalClientImpl(Context context, DaemonWrapper daemon, long halDeviceId, - IBinder token, ServiceListener listener, int fingerId, int groupId, int userId, + /** + * An internal class to help clean up unknown templates in HAL and Framework + */ + private final class InternalRemovalClient extends RemovalClient { + InternalRemovalClient(Context context, + DaemonWrapper daemon, long halDeviceId, IBinder token, + ServiceListener listener, int templateId, int groupId, int userId, boolean restricted, String owner) { - super(context, getMetrics(), daemon, halDeviceId, token, listener, fingerId, groupId, + super(context, getMetrics(), daemon, halDeviceId, token, listener, templateId, groupId, userId, restricted, owner, getBiometricUtils()); } - public void setShouldNotifyUserActivity(boolean shouldNotify) { - mShouldNotify = shouldNotify; - } - @Override - public void notifyUserActivity() { - if (mShouldNotify) { - userActivity(); - } + protected int statsModality() { + return BiometricServiceBase.this.statsModality(); } } - protected abstract class EnumerateClientImpl extends EnumerateClient { - - public EnumerateClientImpl(Context context, DaemonWrapper daemon, long halDeviceId, - IBinder token, ServiceListener listener, int groupId, int userId, - boolean restricted, String owner) { + /** + * Internal class to help clean up unknown templates in the HAL and Framework + */ + private final class InternalEnumerateClient extends EnumerateClient { + + private BiometricUtils mUtils; + // List of templates that are known to the Framework. Remove from this list when enumerate + // returns a template that contains a match. + private List<? extends BiometricAuthenticator.Identifier> mEnrolledList; + // List of templates to remove from the HAL + private List<BiometricAuthenticator.Identifier> mUnknownHALTemplates = new ArrayList<>(); + + InternalEnumerateClient(Context context, + DaemonWrapper daemon, long halDeviceId, IBinder token, + ServiceListener listener, int groupId, int userId, boolean restricted, + String owner, List<? extends BiometricAuthenticator.Identifier> enrolledList, + BiometricUtils utils) { super(context, getMetrics(), daemon, halDeviceId, token, listener, groupId, userId, restricted, owner); + mEnrolledList = enrolledList; + mUtils = utils; + } + + private void handleEnumeratedTemplate(BiometricAuthenticator.Identifier identifier) { + if (identifier == null) { + return; + } + Slog.v(getTag(), "handleEnumeratedTemplate: " + identifier.getBiometricId()); + boolean matched = false; + for (int i = 0; i < mEnrolledList.size(); i++) { + if (mEnrolledList.get(i).getBiometricId() == identifier.getBiometricId()) { + mEnrolledList.remove(i); + matched = true; + break; + } + } + + // TemplateId 0 means no templates in HAL + if (!matched && identifier.getBiometricId() != 0) { + mUnknownHALTemplates.add(identifier); + } + Slog.v(getTag(), "Matched: " + matched); + } + + private void doTemplateCleanup() { + if (mEnrolledList == null) { + return; + } + + // At this point, mEnrolledList only contains templates known to the framework and + // not the HAL. + for (int i = 0; i < mEnrolledList.size(); i++) { + BiometricAuthenticator.Identifier identifier = mEnrolledList.get(i); + Slog.e(getTag(), "doTemplateCleanup(): Removing dangling template from framework: " + + identifier.getBiometricId() + " " + + identifier.getName()); + mUtils.removeBiometricForUser(getContext(), + getTargetUserId(), identifier.getBiometricId()); + StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, + statsModality(), + BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK); + } + mEnrolledList.clear(); + } + + public List<BiometricAuthenticator.Identifier> getUnknownHALTemplates() { + return mUnknownHALTemplates; } @Override - public void notifyUserActivity() { - userActivity(); + public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier, + int remaining) { + handleEnumeratedTemplate(identifier); + if (remaining == 0) { + doTemplateCleanup(); + } + return remaining == 0; + } + + @Override + protected int statsModality() { + return BiometricServiceBase.this.statsModality(); } } @@ -429,13 +469,14 @@ public abstract class BiometricServiceBase extends SystemService * subclasses. */ protected interface DaemonWrapper { - int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. see errno.h. + int ERROR_ESRCH = 3; // Likely HAL is dead. see errno.h. int authenticate(long operationId, int groupId) throws RemoteException; int cancel() throws RemoteException; int remove(int groupId, int biometricId) throws RemoteException; int enumerate() throws RemoteException; - int enroll(byte[] cryptoToken, int groupId, int timeout, + int enroll(byte[] token, int groupId, int timeout, ArrayList<Integer> disabledFeatures) throws RemoteException; + void resetLockout(byte[] token) throws RemoteException; } /** @@ -506,24 +547,7 @@ public abstract class BiometricServiceBase extends SystemService } } - private final class LockoutReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - Slog.v(getTag(), "Resetting lockout: " + intent.getAction()); - if (getLockoutResetIntent().equals(intent.getAction())) { - final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0); - resetFailedAttemptsForUser(false /* clearAttemptCounter */, user); - } - } - } - private final class ResetFailedAttemptsForUserRunnable implements Runnable { - @Override - public void run() { - resetFailedAttemptsForUser(true /* clearAttemptCounter */, - ActivityManager.getCurrentUser()); - } - } private final class LockoutResetMonitor implements IBinder.DeathRecipient { private static final long WAKELOCK_TIMEOUT_MS = 2000; @@ -583,6 +607,19 @@ public abstract class BiometricServiceBase extends SystemService } /** + * Container for enumerated templates. Used to keep track when cleaning up unknown + * templates. + */ + private final class UserTemplate { + final BiometricAuthenticator.Identifier mIdentifier; + final int mUserId; + UserTemplate(BiometricAuthenticator.Identifier identifier, int userId) { + this.mIdentifier = identifier; + this.mUserId = userId; + } + } + + /** * Initializes the system service. * <p> * Subclasses must define a single argument constructor that accepts the context @@ -599,16 +636,11 @@ public abstract class BiometricServiceBase extends SystemService mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString( com.android.internal.R.string.config_keyguardComponent)).getPackageName(); mAppOps = context.getSystemService(AppOpsManager.class); - mTimedLockoutCleared = new SparseBooleanArray(); - mFailedAttempts = new SparseIntArray(); mActivityTaskManager = ((ActivityTaskManager) context.getSystemService( Context.ACTIVITY_TASK_SERVICE)).getService(); mPowerManager = mContext.getSystemService(PowerManager.class); - mAlarmManager = mContext.getSystemService(AlarmManager.class); mUserManager = UserManager.get(mContext); mMetricsLogger = new MetricsLogger(); - mContext.registerReceiver(mLockoutReceiver, new IntentFilter(getLockoutResetIntent()), - getLockoutBroadcastPermission(), null /* handler */); } @Override @@ -688,6 +720,11 @@ public abstract class BiometricServiceBase extends SystemService if (DEBUG) Slog.v(getTag(), "handleError(client=" + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")"); + if (client instanceof InternalRemovalClient + || client instanceof InternalEnumerateClient) { + clearEnumerateState(); + } + if (client != null && client.onError(deviceId, error, vendorCode)) { removeClient(client); } @@ -721,6 +758,39 @@ public abstract class BiometricServiceBase extends SystemService updateActiveGroup(userId, null); } } + + if (client instanceof InternalRemovalClient && !mUnknownHALTemplates.isEmpty()) { + startCleanupUnknownHALTemplates(); + } else if (client instanceof InternalRemovalClient) { + clearEnumerateState(); + } + } + + protected void handleEnumerate(BiometricAuthenticator.Identifier identifier, int remaining) { + ClientMonitor client = getCurrentClient(); + + client.onEnumerationResult(identifier, remaining); + + // All templates in the HAL for this user were enumerated + if (remaining == 0) { + if (client instanceof InternalEnumerateClient) { + List<BiometricAuthenticator.Identifier> unknownHALTemplates = + ((InternalEnumerateClient) client).getUnknownHALTemplates(); + + if (!unknownHALTemplates.isEmpty()) { + Slog.w(getTag(), "Adding " + unknownHALTemplates.size() + + " templates for deletion"); + } + for (int i = 0; i < unknownHALTemplates.size(); i++) { + mUnknownHALTemplates.add(new UserTemplate(unknownHALTemplates.get(i), + client.getTargetUserId())); + } + removeClient(client); + startCleanupUnknownHALTemplates(); + } else { + removeClient(client); + } + } } /** @@ -832,13 +902,13 @@ public abstract class BiometricServiceBase extends SystemService }); } - protected void removeInternal(RemovalClientImpl client) { + protected void removeInternal(RemovalClient client) { mHandler.post(() -> { startClient(client, true /* initiatedByClient */); }); } - protected void enumerateInternal(EnumerateClientImpl client) { + protected void enumerateInternal(EnumerateClient client) { mHandler.post(() -> { startClient(client, true /* initiatedByClient */); }); @@ -919,19 +989,6 @@ public abstract class BiometricServiceBase extends SystemService return mKeyguardPackage.equals(clientPackage); } - protected int getLockoutMode() { - final int currentUser = ActivityManager.getCurrentUser(); - final int failedAttempts = mFailedAttempts.get(currentUser, 0); - if (failedAttempts >= getFailedAttemptsLockoutPermanent()) { - return AuthenticationClient.LOCKOUT_PERMANENT; - } else if (failedAttempts > 0 && - mTimedLockoutCleared.get(currentUser, false) == false - && (failedAttempts % getFailedAttemptsLockoutTimed() == 0)) { - return AuthenticationClient.LOCKOUT_TIMED; - } - return AuthenticationClient.LOCKOUT_NONE; - } - private boolean isForegroundActivity(int uid, int pid) { try { List<ActivityManager.RunningAppProcessInfo> procs = @@ -965,10 +1022,10 @@ public abstract class BiometricServiceBase extends SystemService currentClient.getOwnerString()); // This check only matters for FingerprintService, since enumerate may call back // multiple times. - if (currentClient instanceof FingerprintService.EnumerateClientImpl || - currentClient instanceof FingerprintService.RemovalClientImpl) { + if (currentClient instanceof InternalEnumerateClient + || currentClient instanceof InternalRemovalClient) { // This condition means we're currently running internal diagnostics to - // remove extra fingerprints in the hardware and/or the software + // remove extra templates in the hardware and/or the software // TODO: design an escape hatch in case client never finishes if (newClient != null) { Slog.w(getTag(), "Internal cleanup in progress but trying to start client " @@ -1124,16 +1181,75 @@ public abstract class BiometricServiceBase extends SystemService return mAuthenticatorIds.getOrDefault(userId, 0L); } - private void scheduleLockoutResetForUser(int userId) { - mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS, - getLockoutResetIntentForUser(userId)); + /** + * This method should be called upon connection to the daemon, and when user switches. + * @param userId + */ + protected void doTemplateCleanupForUser(int userId) { + if (CLEANUP_UNKNOWN_TEMPLATES) { + enumerateUser(userId); + } } - private PendingIntent getLockoutResetIntentForUser(int userId) { - return PendingIntent.getBroadcast(mContext, userId, - new Intent(getLockoutResetIntent()).putExtra(KEY_LOCKOUT_RESET_USER, userId), - PendingIntent.FLAG_UPDATE_CURRENT); + private void clearEnumerateState() { + if (DEBUG) Slog.v(getTag(), "clearEnumerateState()"); + mUnknownHALTemplates.clear(); + } + + /** + * Remove unknown templates from HAL + */ + private void startCleanupUnknownHALTemplates() { + if (!mUnknownHALTemplates.isEmpty()) { + UserTemplate template = mUnknownHALTemplates.get(0); + mUnknownHALTemplates.remove(template); + boolean restricted = !hasPermission(getManageBiometricPermission()); + InternalRemovalClient client = new InternalRemovalClient(getContext(), + getDaemonWrapper(), mHalDeviceId, mToken, null /* listener */, + template.mIdentifier.getBiometricId(), 0 /* groupId */, template.mUserId, + restricted, getContext().getPackageName()); + removeInternal(client); + StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, + statsModality(), + BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL); + } else { + clearEnumerateState(); + } + } + + private void enumerateUser(int userId) { + if (DEBUG) Slog.v(getTag(), "Enumerating user(" + userId + ")"); + + final boolean restricted = !hasPermission(getManageBiometricPermission()); + final List<? extends BiometricAuthenticator.Identifier> enrolledList = + getEnrolledTemplates(userId); + + InternalEnumerateClient client = new InternalEnumerateClient(getContext(), + getDaemonWrapper(), mHalDeviceId, mToken, null /* serviceListener */, userId, + userId, restricted, getContext().getOpPackageName(), enrolledList, + getBiometricUtils()); + enumerateInternal(client); + } + + /** + * This method is called when the user switches. Implementations should probably notify the + * HAL. + */ + protected void handleUserSwitching(int userId) { + if (getCurrentClient() instanceof InternalRemovalClient + || getCurrentClient() instanceof InternalEnumerateClient) { + Slog.w(getTag(), "User switched while performing cleanup"); + removeClient(getCurrentClient()); + clearEnumerateState(); + } + updateActiveGroup(userId, null); + doTemplateCleanupForUser(userId); + } + + protected void notifyLockoutResetMonitors() { + for (int i = 0; i < mLockoutMonitors.size(); i++) { + mLockoutMonitors.get(i).sendLockoutReset(); + } } private void userActivity() { @@ -1169,25 +1285,6 @@ public abstract class BiometricServiceBase extends SystemService return userId; } - // Attempt counter should only be cleared when Keyguard goes away or when - // a biometric is successfully authenticated. - private void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) { - if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) { - Slog.v(getTag(), "Reset biometric lockout, clearAttemptCounter=" + clearAttemptCounter); - } - if (clearAttemptCounter) { - mFailedAttempts.put(userId, 0); - } - mTimedLockoutCleared.put(userId, true); - // If we're asked to reset failed attempts externally (i.e. from Keyguard), - // the alarm might still be pending; remove it. - cancelLockoutResetForUser(userId); - notifyLockoutResetMonitors(); - } - - private void cancelLockoutResetForUser(int userId) { - mAlarmManager.cancel(getLockoutResetIntentForUser(userId)); - } private void listenForUserSwitches() { try { @@ -1204,12 +1301,6 @@ public abstract class BiometricServiceBase extends SystemService } } - private void notifyLockoutResetMonitors() { - for (int i = 0; i < mLockoutMonitors.size(); i++) { - mLockoutMonitors.get(i).sendLockoutReset(); - } - } - private void removeLockoutResetCallback( LockoutResetMonitor monitor) { mLockoutMonitors.remove(monitor); diff --git a/services/core/java/com/android/server/biometrics/EnumerateClient.java b/services/core/java/com/android/server/biometrics/EnumerateClient.java index 0f57f484bb0a..44ac0373507a 100644 --- a/services/core/java/com/android/server/biometrics/EnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/EnumerateClient.java @@ -39,6 +39,10 @@ public abstract class EnumerateClient extends ClientMonitor { } @Override + public void notifyUserActivity() { + } + + @Override protected int statsAction() { return BiometricsProtoEnums.ACTION_ENUMERATE; } @@ -94,7 +98,9 @@ public abstract class EnumerateClient extends ClientMonitor { public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier, int remaining) { try { - getListener().onEnumerated(identifier, remaining); + if (getListener() != null) { + getListener().onEnumerated(identifier, remaining); + } } catch (RemoteException e) { Slog.w(getLogTag(), "Failed to notify enumerated:", e); } diff --git a/services/core/java/com/android/server/biometrics/RemovalClient.java b/services/core/java/com/android/server/biometrics/RemovalClient.java index 0509067b4daf..a18f336f3ef4 100644 --- a/services/core/java/com/android/server/biometrics/RemovalClient.java +++ b/services/core/java/com/android/server/biometrics/RemovalClient.java @@ -44,6 +44,10 @@ public abstract class RemovalClient extends ClientMonitor { } @Override + public void notifyUserActivity() { + } + + @Override protected int statsAction() { return BiometricsProtoEnums.ACTION_REMOVE; } @@ -95,7 +99,9 @@ public abstract class RemovalClient extends ClientMonitor { private boolean sendRemoved(BiometricAuthenticator.Identifier identifier, int remaining) { try { - getListener().onRemoved(identifier, remaining); + if (getListener() != null) { + getListener().onRemoved(identifier, remaining); + } } catch (RemoteException e) { Slog.w(getLogTag(), "Failed to notify Removed:", e); } diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index eab38209c194..d2d14829d597 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -51,9 +51,13 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.internal.util.DumpUtils; import com.android.server.SystemServerInitThreadPool; +import com.android.server.biometrics.AuthenticationClient; import com.android.server.biometrics.BiometricServiceBase; import com.android.server.biometrics.BiometricUtils; +import com.android.server.biometrics.ClientMonitor; +import com.android.server.biometrics.EnumerateClient; import com.android.server.biometrics.Metrics; +import com.android.server.biometrics.RemovalClient; import org.json.JSONArray; import org.json.JSONException; @@ -79,8 +83,6 @@ public class FaceService extends BiometricServiceBase { private static final String FACE_DATA_DIR = "facedata"; private static final String ACTION_LOCKOUT_RESET = "com.android.server.biometrics.face.ACTION_LOCKOUT_RESET"; - private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 3; - private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 12; private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes private final class FaceAuthClient extends AuthenticationClientImpl { @@ -96,6 +98,25 @@ public class FaceService extends BiometricServiceBase { protected int statsModality() { return FaceService.this.statsModality(); } + + @Override + public boolean shouldFrameworkHandleLockout() { + return false; + } + + @Override + public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier, + boolean authenticated, ArrayList<Byte> token) { + final boolean result = super.onAuthenticated(identifier, authenticated, token); + + // For face, the authentication lifecycle ends either when + // 1) Authenticated == true + // 2) Error occurred + // 3) Authenticated == false + // Fingerprint currently does not end when the third condition is met which is a bug, + // but let's leave it as-is for now. + return result || !authenticated; + } } /** @@ -106,6 +127,7 @@ public class FaceService extends BiometricServiceBase { /** * The following methods contain common code which is shared in biometrics/common. */ + @Override // Binder call public long generateChallenge(IBinder token) { checkPermission(MANAGE_BIOMETRIC); @@ -216,15 +238,14 @@ public class FaceService extends BiometricServiceBase { } final boolean restricted = isRestricted(); - final RemovalClientImpl client = new RemovalClientImpl(getContext(), mDaemonWrapper, - mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId, 0 /* groupId */, - userId, restricted, token.toString()) { + final RemovalClient client = new RemovalClient(getContext(), getMetrics(), + mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId, + 0 /* groupId */, userId, restricted, token.toString(), getBiometricUtils()) { @Override protected int statsModality() { return FaceService.this.statsModality(); } }; - client.setShouldNotifyUserActivity(true); removeInternal(client); } @@ -234,9 +255,9 @@ public class FaceService extends BiometricServiceBase { checkPermission(MANAGE_BIOMETRIC); final boolean restricted = isRestricted(); - final EnumerateClientImpl client = new EnumerateClientImpl(getContext(), mDaemonWrapper, - mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId, - restricted, getContext().getOpPackageName()) { + final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(), + mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, + userId, restricted, getContext().getOpPackageName()) { @Override protected int statsModality() { return FaceService.this.statsModality(); @@ -317,7 +338,7 @@ public class FaceService extends BiometricServiceBase { return null; } - return FaceService.this.getEnrolledFaces(userId); + return FaceService.this.getEnrolledTemplates(userId); } @Override // Binder call @@ -354,10 +375,13 @@ public class FaceService extends BiometricServiceBase { } @Override // Binder call - public void resetTimeout(byte[] token) { + public void resetLockout(byte[] token) { checkPermission(MANAGE_BIOMETRIC); - // TODO: confirm security token when we move timeout management into the HAL layer. - mHandler.post(mResetFailedAttemptsForCurrentUserRunnable); + try { + mDaemonWrapper.resetLockout(token); + } catch (RemoteException e) { + Slog.e(getTag(), "Unable to reset lockout", e); + } } @Override @@ -511,7 +535,8 @@ public class FaceService extends BiometricServiceBase { public void onEnumerated(BiometricAuthenticator.Identifier identifier, int remaining) throws RemoteException { if (mFaceServiceReceiver != null) { - + mFaceServiceReceiver.onEnumerated(identifier.getDeviceId(), + identifier.getBiometricId(), remaining); } } } @@ -520,80 +545,107 @@ public class FaceService extends BiometricServiceBase { @GuardedBy("this") private IBiometricsFace mDaemon; - private long mHalDeviceId; + // One of the AuthenticationClient constants + private int mCurrentUserLockoutMode; /** * Receives callbacks from the HAL. */ private IBiometricsFaceClientCallback mDaemonCallback = new IBiometricsFaceClientCallback.Stub() { - @Override - public void onEnrollResult(final long deviceId, int faceId, int userId, - int remaining) { - mHandler.post(() -> { - final Face face = new Face(getBiometricUtils() - .getUniqueName(getContext(), userId), faceId, deviceId); - FaceService.super.handleEnrollResult(face, remaining); - }); - } + @Override + public void onEnrollResult(final long deviceId, int faceId, int userId, + int remaining) { + mHandler.post(() -> { + final Face face = new Face(getBiometricUtils() + .getUniqueName(getContext(), userId), faceId, deviceId); + FaceService.super.handleEnrollResult(face, remaining); + }); + } - @Override - public void onAcquired(final long deviceId, final int userId, - final int acquiredInfo, - final int vendorCode) { - mHandler.post(() -> { - FaceService.super.handleAcquired(deviceId, acquiredInfo, vendorCode); - }); - } + @Override + public void onAcquired(final long deviceId, final int userId, + final int acquiredInfo, + final int vendorCode) { + mHandler.post(() -> { + FaceService.super.handleAcquired(deviceId, acquiredInfo, vendorCode); + }); + } - @Override - public void onAuthenticated(final long deviceId, final int faceId, final int userId, - ArrayList<Byte> token) { - mHandler.post(() -> { - Face face = new Face("", faceId, deviceId); - FaceService.super.handleAuthenticated(face, token); - }); - } + @Override + public void onAuthenticated(final long deviceId, final int faceId, final int userId, + ArrayList<Byte> token) { + mHandler.post(() -> { + Face face = new Face("", faceId, deviceId); + FaceService.super.handleAuthenticated(face, token); + }); + } - @Override - public void onError(final long deviceId, final int userId, final int error, - final int vendorCode) { - mHandler.post(() -> { - FaceService.super.handleError(deviceId, error, vendorCode); - - // TODO: this chunk of code should be common to all biometric services - if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) { - // If we get HW_UNAVAILABLE, try to connect again later... - Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client."); - synchronized (this) { - mDaemon = null; - mHalDeviceId = 0; - mCurrentUserId = UserHandle.USER_NULL; - } - } - }); + @Override + public void onError(final long deviceId, final int userId, final int error, + final int vendorCode) { + mHandler.post(() -> { + FaceService.super.handleError(deviceId, error, vendorCode); + + // TODO: this chunk of code should be common to all biometric services + if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) { + // If we get HW_UNAVAILABLE, try to connect again later... + Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client."); + synchronized (this) { + mDaemon = null; + mHalDeviceId = 0; + mCurrentUserId = UserHandle.USER_NULL; + } } + }); + } - @Override - public void onRemoved(final long deviceId, final int faceId, final int userId, - final int remaining) { - mHandler.post(() -> { - final Face face = new Face("", faceId, deviceId); - FaceService.super.handleRemoved(face, remaining); - }); - } + @Override + public void onRemoved(final long deviceId, final int faceId, final int userId, + final int remaining) { + mHandler.post(() -> { + final Face face = new Face("", faceId, deviceId); + FaceService.super.handleRemoved(face, remaining); + }); + } - @Override - public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId) - throws RemoteException { - // TODO + @Override + public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId) + throws RemoteException { + mHandler.post(() -> { + if (!faceIds.isEmpty()) { + for (int i = 0; i < faceIds.size(); i++) { + final Face face = new Face("", faceIds.get(i), deviceId); + // Convert to old old behavior + FaceService.super.handleEnumerate(face, faceIds.size() - i - 1); + } + } else { + // For face, the HIDL contract is to receive an empty list when there are no + // templates enrolled. Send a null identifier since we don't consume them + // anywhere, and send remaining == 0 to plumb this with existing common code. + FaceService.super.handleEnumerate(null /* identifier */, 0); } + }); + } - @Override - public void onLockoutChanged(long duration) { + @Override + public void onLockoutChanged(long duration) { + Slog.d(TAG, "onLockoutChanged: " + duration); + if (duration == 0) { + mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE; + } else if (duration == Long.MAX_VALUE) { + mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_PERMANENT; + } else { + mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_TIMED; + } + mHandler.post(() -> { + if (duration == 0) { + notifyLockoutResetMonitors(); } - }; + }); + } + }; /** * Wraps the HAL-specific code and is passed to the ClientMonitor implementations so that they @@ -652,9 +704,22 @@ public class FaceService extends BiometricServiceBase { for (int i = 0; i < cryptoToken.length; i++) { token.add(cryptoToken[i]); } - // TODO: plumb requireAttention down from framework return daemon.enroll(token, timeout, disabledFeatures); } + + @Override + public void resetLockout(byte[] cryptoToken) throws RemoteException { + IBiometricsFace daemon = getFaceDaemon(); + if (daemon == null) { + Slog.w(TAG, "resetLockout(): no face HAL!"); + return; + } + final ArrayList<Byte> token = new ArrayList<>(); + for (int i = 0; i < cryptoToken.length; i++) { + token.add(cryptoToken[i]); + } + daemon.resetLockout(token); + } }; @@ -675,18 +740,13 @@ public class FaceService extends BiometricServiceBase { } @Override - protected BiometricUtils getBiometricUtils() { - return FaceUtils.getInstance(); - } - - @Override - protected int getFailedAttemptsLockoutTimed() { - return MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED; + protected DaemonWrapper getDaemonWrapper() { + return mDaemonWrapper; } @Override - protected int getFailedAttemptsLockoutPermanent() { - return MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT; + protected BiometricUtils getBiometricUtils() { + return FaceUtils.getInstance(); } @Override @@ -698,7 +758,7 @@ public class FaceService extends BiometricServiceBase { protected boolean hasReachedEnrollmentLimit(int userId) { final int limit = getContext().getResources().getInteger( com.android.internal.R.integer.config_faceMaxTemplatesPerUser); - final int enrolled = FaceService.this.getEnrolledFaces(userId).size(); + final int enrolled = FaceService.this.getEnrolledTemplates(userId).size(); if (enrolled >= limit) { Slog.w(TAG, "Too many faces registered"); return true; @@ -766,7 +826,9 @@ public class FaceService extends BiometricServiceBase { @Override protected void handleUserSwitching(int userId) { - updateActiveGroup(userId, null); + super.handleUserSwitching(userId); + // Will be updated when we get the callback from HAL + mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE; } @Override @@ -785,7 +847,6 @@ public class FaceService extends BiometricServiceBase { @Override protected void checkUseBiometricPermission() { // noop for Face. The permission checks are all done on the incoming binder call. - // TODO: Perhaps do the same in FingerprintService } @Override @@ -795,6 +856,11 @@ public class FaceService extends BiometricServiceBase { } @Override + protected List<Face> getEnrolledTemplates(int userId) { + return getBiometricUtils().getBiometricsForUser(getContext(), userId); + } + + @Override protected void notifyClientActiveCallbacks(boolean isActive) { // noop for Face. } @@ -804,6 +870,11 @@ public class FaceService extends BiometricServiceBase { return BiometricsProtoEnums.MODALITY_FACE; } + @Override + protected int getLockoutMode() { + return mCurrentUserLockoutMode; + } + /** Gets the face daemon */ private synchronized IBiometricsFace getFaceDaemon() { if (mDaemon == null) { @@ -833,6 +904,7 @@ public class FaceService extends BiometricServiceBase { if (mHalDeviceId != 0) { loadAuthenticatorIds(); updateActiveGroup(ActivityManager.getCurrentUser(), null); + doTemplateCleanupForUser(ActivityManager.getCurrentUser()); } else { Slog.w(TAG, "Failed to open Face HAL!"); MetricsLogger.count(getContext(), "faced_openhal_error", 1); @@ -870,10 +942,6 @@ public class FaceService extends BiometricServiceBase { return 0; } - private List<Face> getEnrolledFaces(int userId) { - return getBiometricUtils().getBiometricsForUser(getContext(), userId); - } - private void dumpInternal(PrintWriter pw) { JSONObject dump = new JSONObject(); try { diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java index d8544e324618..164468e99967 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java @@ -24,8 +24,13 @@ import static android.Manifest.permission.USE_BIOMETRIC; import static android.Manifest.permission.USE_FINGERPRINT; import android.app.ActivityManager; +import android.app.AlarmManager; import android.app.AppOpsManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricAuthenticator; @@ -46,21 +51,25 @@ import android.os.Environment; import android.os.IBinder; import android.os.RemoteException; import android.os.SELinux; +import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.util.Slog; -import android.util.StatsLog; +import android.util.SparseBooleanArray; +import android.util.SparseIntArray; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.internal.util.DumpUtils; import com.android.server.SystemServerInitThreadPool; +import com.android.server.biometrics.AuthenticationClient; import com.android.server.biometrics.BiometricServiceBase; import com.android.server.biometrics.BiometricUtils; import com.android.server.biometrics.ClientMonitor; import com.android.server.biometrics.EnumerateClient; import com.android.server.biometrics.Metrics; +import com.android.server.biometrics.RemovalClient; import org.json.JSONArray; import org.json.JSONException; @@ -85,20 +94,30 @@ public class FingerprintService extends BiometricServiceBase { protected static final String TAG = "FingerprintService"; private static final boolean DEBUG = true; - private static final boolean CLEANUP_UNUSED_FP = true; private static final String FP_DATA_DIR = "fpdata"; private static final String ACTION_LOCKOUT_RESET = "com.android.server.biometrics.fingerprint.ACTION_LOCKOUT_RESET"; private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 5; private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 20; + private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30 * 1000; + private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user"; - // TODO: This should be refactored into BiometricService - private final class UserFingerprint { - Fingerprint f; - int userId; - public UserFingerprint(Fingerprint f, int userId) { - this.f = f; - this.userId = userId; + private final class ResetFailedAttemptsForUserRunnable implements Runnable { + @Override + public void run() { + resetFailedAttemptsForUser(true /* clearAttemptCounter */, + ActivityManager.getCurrentUser()); + } + } + + private final class LockoutReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + Slog.v(getTag(), "Resetting lockout: " + intent.getAction()); + if (getLockoutResetIntent().equals(intent.getAction())) { + final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0); + resetFailedAttemptsForUser(false /* clearAttemptCounter */, user); + } } } @@ -121,6 +140,30 @@ public class FingerprintService extends BiometricServiceBase { protected int statsModality() { return FingerprintService.this.statsModality(); } + + @Override + public void resetFailedAttempts() { + resetFailedAttemptsForUser(true /* clearAttemptCounter */, + ActivityManager.getCurrentUser()); + } + + @Override + public boolean shouldFrameworkHandleLockout() { + return true; + } + + @Override + public int handleFailedAttempt() { + final int currentUser = ActivityManager.getCurrentUser(); + mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1); + mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false); + + if (getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) { + scheduleLockoutResetForUser(currentUser); + } + + return super.handleFailedAttempt(); + } } /** @@ -241,15 +284,14 @@ public class FingerprintService extends BiometricServiceBase { } final boolean restricted = isRestricted(); - final RemovalClientImpl client = new RemovalClientImpl(getContext(), mDaemonWrapper, - mHalDeviceId, token, new ServiceListenerImpl(receiver), fingerId, groupId, - userId, restricted, token.toString()) { + final RemovalClient client = new RemovalClient(getContext(), getMetrics(), + mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), + fingerId, groupId, userId, restricted, token.toString(), getBiometricUtils()) { @Override protected int statsModality() { return FingerprintService.this.statsModality(); } }; - client.setShouldNotifyUserActivity(true); removeInternal(client); } @@ -259,9 +301,9 @@ public class FingerprintService extends BiometricServiceBase { checkPermission(MANAGE_FINGERPRINT); final boolean restricted = isRestricted(); - final EnumerateClientImpl client = new EnumerateClientImpl(getContext(), mDaemonWrapper, - mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId, - restricted, getContext().getOpPackageName()) { + final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(), + mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, + userId, restricted, getContext().getOpPackageName()) { @Override protected int statsModality() { return FingerprintService.this.statsModality(); @@ -339,7 +381,7 @@ public class FingerprintService extends BiometricServiceBase { return Collections.emptyList(); } - return FingerprintService.this.getEnrolledFingerprints(userId); + return FingerprintService.this.getEnrolledTemplates(userId); } @Override // Binder call @@ -445,7 +487,6 @@ public class FingerprintService extends BiometricServiceBase { public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) throws RemoteException { if (mFingerprintServiceReceiver != null) { - // TODO: Pass up the fp directly instead final Fingerprint fp = (Fingerprint) identifier; mFingerprintServiceReceiver.onEnrollResult(fp.getDeviceId(), fp.getBiometricId(), fp.getGroupId(), remaining); @@ -493,7 +534,6 @@ public class FingerprintService extends BiometricServiceBase { public void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) throws RemoteException { if (mFingerprintServiceReceiver != null) { - // TODO: Pass up the fp directly instead final Fingerprint fp = (Fingerprint) identifier; mFingerprintServiceReceiver.onRemoved(fp.getDeviceId(), fp.getBiometricId(), fp.getGroupId(), remaining); @@ -511,105 +551,18 @@ public class FingerprintService extends BiometricServiceBase { } } - /** - * An internal class to help clean up unknown fingerprints in the hardware and software. - */ - private final class InternalEnumerateClient extends BiometricServiceBase.EnumerateClientImpl { - - private List<Fingerprint> mEnrolledList; - private List<Fingerprint> mUnknownFingerprints = new ArrayList<>(); // list of fp to delete - - public InternalEnumerateClient(Context context, DaemonWrapper daemon, long halDeviceId, - IBinder token, ServiceListener listener, int groupId, int userId, - boolean restricted, String owner, List<Fingerprint> enrolledList) { - super(context, daemon, halDeviceId, token, listener, groupId, userId, restricted, - owner); - mEnrolledList = enrolledList; - } - - private void handleEnumeratedFingerprint( - BiometricAuthenticator.Identifier identifier, int remaining) { - boolean matched = false; - for (int i = 0; i < mEnrolledList.size(); i++) { - if (mEnrolledList.get(i).getBiometricId() == identifier.getBiometricId()) { - mEnrolledList.remove(i); - matched = true; - break; - } - } - - // fingerId 0 means no fingerprints are in hardware - if (!matched && identifier.getBiometricId() != 0) { - mUnknownFingerprints.add((Fingerprint) identifier); - } - } - - private void doFingerprintCleanup() { - if (mEnrolledList == null) { - return; - } - - for (Fingerprint f : mEnrolledList) { - Slog.e(TAG, "doFingerprintCleanup(): Removing dangling enrolled fingerprint: " - + f.getName() + " " + f.getBiometricId() + " " + f.getGroupId() - + " " + f.getDeviceId()); - FingerprintUtils.getInstance().removeBiometricForUser(getContext(), - getTargetUserId(), f.getBiometricId()); - StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, - BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK); - } - mEnrolledList.clear(); - } - - public List<Fingerprint> getUnknownFingerprints() { - return mUnknownFingerprints; - } - - @Override - public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier, - int remaining) { - handleEnumeratedFingerprint(identifier, remaining); - if (remaining == 0) { - doFingerprintCleanup(); - } - return remaining == 0; - } - - @Override - protected int statsModality() { - return FingerprintService.this.statsModality(); - } - } - - /** - * An internal class to help clean up unknown fingerprints in hardware and software. - */ - private final class InternalRemovalClient extends BiometricServiceBase.RemovalClientImpl { - public InternalRemovalClient(Context context, - DaemonWrapper daemon, long halDeviceId, IBinder token, - ServiceListener listener, int fingerId, int groupId, int userId, boolean restricted, - String owner) { - super(context, daemon, halDeviceId, token, listener, fingerId, groupId, userId, - restricted, - owner); - } - - @Override - protected int statsModality() { - return FingerprintService.this.statsModality(); - } - } - private final FingerprintMetrics mFingerprintMetrics = new FingerprintMetrics(); private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks = new CopyOnWriteArrayList<>(); @GuardedBy("this") private IBiometricsFingerprint mDaemon; - - private long mHalDeviceId; - private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration - private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw fingerprints + private final SparseBooleanArray mTimedLockoutCleared; + private final SparseIntArray mFailedAttempts; + private final AlarmManager mAlarmManager; + private final LockoutReceiver mLockoutReceiver = new LockoutReceiver(); + protected final ResetFailedAttemptsForUserRunnable mResetFailedAttemptsForCurrentUserRunnable = + new ResetFailedAttemptsForUserRunnable(); /** * Receives callbacks from the HAL. @@ -646,13 +599,7 @@ public class FingerprintService extends BiometricServiceBase { @Override public void onError(final long deviceId, final int error, final int vendorCode) { mHandler.post(() -> { - ClientMonitor client = getCurrentClient(); - if (client instanceof InternalRemovalClient - || client instanceof InternalEnumerateClient) { - clearEnumerateState(); - } FingerprintService.super.handleError(deviceId, error, vendorCode); - // TODO: this chunk of code should be common to all biometric services if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) { // If we get HW_UNAVAILABLE, try to connect again later... @@ -673,11 +620,6 @@ public class FingerprintService extends BiometricServiceBase { ClientMonitor client = getCurrentClient(); final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId); FingerprintService.super.handleRemoved(fp, remaining); - if (client instanceof InternalRemovalClient && !mUnknownFingerprints.isEmpty()) { - cleanupUnknownFingerprints(); - } else if (client instanceof InternalRemovalClient){ - clearEnumerateState(); - } }); } @@ -685,9 +627,8 @@ public class FingerprintService extends BiometricServiceBase { public void onEnumerate(final long deviceId, final int fingerId, final int groupId, final int remaining) { mHandler.post(() -> { - // TODO: factor out common enumerate logic if possible final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId); - FingerprintService.this.handleEnumerate(fp, remaining); + FingerprintService.super.handleEnumerate(fp, remaining); }); } @@ -748,10 +689,22 @@ public class FingerprintService extends BiometricServiceBase { } return daemon.enroll(cryptoToken, groupId, timeout); } + + @Override + public void resetLockout(byte[] token) throws RemoteException { + // TODO: confirm security token when we move timeout management into the HAL layer. + Slog.e(TAG, "Not supported"); + return; + } }; public FingerprintService(Context context) { super(context); + mTimedLockoutCleared = new SparseBooleanArray(); + mFailedAttempts = new SparseIntArray(); + mAlarmManager = context.getSystemService(AlarmManager.class); + context.registerReceiver(mLockoutReceiver, new IntentFilter(getLockoutResetIntent()), + getLockoutBroadcastPermission(), null /* handler */); } @Override @@ -767,18 +720,13 @@ public class FingerprintService extends BiometricServiceBase { } @Override - protected BiometricUtils getBiometricUtils() { - return FingerprintUtils.getInstance(); - } - - @Override - protected int getFailedAttemptsLockoutTimed() { - return MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED; + protected DaemonWrapper getDaemonWrapper() { + return mDaemonWrapper; } @Override - protected int getFailedAttemptsLockoutPermanent() { - return MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT; + protected BiometricUtils getBiometricUtils() { + return FingerprintUtils.getInstance(); } @Override @@ -790,7 +738,7 @@ public class FingerprintService extends BiometricServiceBase { protected boolean hasReachedEnrollmentLimit(int userId) { final int limit = getContext().getResources().getInteger( com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser); - final int enrolled = FingerprintService.this.getEnrolledFingerprints(userId).size(); + final int enrolled = FingerprintService.this.getEnrolledTemplates(userId).size(); if (enrolled >= limit) { Slog.w(TAG, "Too many fingerprints registered"); return true; @@ -866,19 +814,6 @@ public class FingerprintService extends BiometricServiceBase { } @Override - protected void handleUserSwitching(int userId) { - if (getCurrentClient() instanceof InternalRemovalClient - || getCurrentClient() instanceof InternalEnumerateClient) { - Slog.w(TAG, "User switched while performing cleanup"); - removeClient(getCurrentClient()); - clearEnumerateState(); - } - updateActiveGroup(userId, null); - doFingerprintCleanupForUser(userId); - } - - - @Override protected boolean hasEnrolledBiometrics(int userId) { if (userId != UserHandle.getCallingUserId()) { checkPermission(INTERACT_ACROSS_USERS); @@ -913,6 +848,11 @@ public class FingerprintService extends BiometricServiceBase { } @Override + protected List<Fingerprint> getEnrolledTemplates(int userId) { + return getBiometricUtils().getBiometricsForUser(getContext(), userId); + } + + @Override protected void notifyClientActiveCallbacks(boolean isActive) { List<IFingerprintClientActiveCallback> callbacks = mClientActiveCallbacks; for (int i = 0; i < callbacks.size(); i++) { @@ -930,6 +870,20 @@ public class FingerprintService extends BiometricServiceBase { return BiometricsProtoEnums.MODALITY_FINGERPRINT; } + @Override + protected int getLockoutMode() { + final int currentUser = ActivityManager.getCurrentUser(); + final int failedAttempts = mFailedAttempts.get(currentUser, 0); + if (failedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) { + return AuthenticationClient.LOCKOUT_PERMANENT; + } else if (failedAttempts > 0 + && !mTimedLockoutCleared.get(currentUser, false) + && (failedAttempts % MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED == 0)) { + return AuthenticationClient.LOCKOUT_TIMED; + } + return AuthenticationClient.LOCKOUT_NONE; + } + /** Gets the fingerprint daemon */ private synchronized IBiometricsFingerprint getFingerprintDaemon() { if (mDaemon == null) { @@ -959,7 +913,7 @@ public class FingerprintService extends BiometricServiceBase { if (mHalDeviceId != 0) { loadAuthenticatorIds(); updateActiveGroup(ActivityManager.getCurrentUser(), null); - doFingerprintCleanupForUser(ActivityManager.getCurrentUser()); + doTemplateCleanupForUser(ActivityManager.getCurrentUser()); } else { Slog.w(TAG, "Failed to open Fingerprint HAL!"); MetricsLogger.count(getContext(), "fingerprintd_openhal_error", 1); @@ -969,79 +923,6 @@ public class FingerprintService extends BiometricServiceBase { return mDaemon; } - /** - * This method should be called upon connection to the daemon, and when user switches. - * @param userId - */ - private void doFingerprintCleanupForUser(int userId) { - if (CLEANUP_UNUSED_FP) { - enumerateUser(userId); - } - } - - private void clearEnumerateState() { - if (DEBUG) Slog.v(TAG, "clearEnumerateState()"); - mUnknownFingerprints.clear(); - } - - private void enumerateUser(int userId) { - if (DEBUG) Slog.v(TAG, "Enumerating user(" + userId + ")"); - - final boolean restricted = !hasPermission(MANAGE_FINGERPRINT); - final List<Fingerprint> enrolledList = getEnrolledFingerprints(userId); - - InternalEnumerateClient client = new InternalEnumerateClient(getContext(), mDaemonWrapper, - mHalDeviceId, mToken, new ServiceListenerImpl(null), userId, userId, restricted, - getContext().getOpPackageName(), enrolledList); - enumerateInternal(client); - } - - // Remove unknown fingerprints from hardware - private void cleanupUnknownFingerprints() { - if (!mUnknownFingerprints.isEmpty()) { - UserFingerprint uf = mUnknownFingerprints.get(0); - mUnknownFingerprints.remove(uf); - boolean restricted = !hasPermission(MANAGE_FINGERPRINT); - InternalRemovalClient client = new InternalRemovalClient(getContext(), mDaemonWrapper, - mHalDeviceId, mToken, new ServiceListenerImpl(null), uf.f.getBiometricId(), - uf.f.getGroupId(), uf.userId, restricted, getContext().getOpPackageName()); - removeInternal(client); - StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, statsModality(), - BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL); - } else { - clearEnumerateState(); - } - } - - private void handleEnumerate(Fingerprint fingerprint, int remaining) { - ClientMonitor client = getCurrentClient(); - - if ( !(client instanceof InternalRemovalClient) && !(client instanceof EnumerateClient) ) { - return; - } - client.onEnumerationResult(fingerprint, remaining); - - // All fingerprints in hardware for this user were enumerated - if (remaining == 0) { - if (client instanceof InternalEnumerateClient) { - List<Fingerprint> unknownFingerprints = - ((InternalEnumerateClient) client).getUnknownFingerprints(); - - if (!unknownFingerprints.isEmpty()) { - Slog.w(TAG, "Adding " + unknownFingerprints.size() + - " fingerprints for deletion"); - } - for (Fingerprint f : unknownFingerprints) { - mUnknownFingerprints.add(new UserFingerprint(f, client.getTargetUserId())); - } - removeClient(client); - cleanupUnknownFingerprints(); - } else { - removeClient(client); - } - } - } - private long startPreEnroll(IBinder token) { IBiometricsFingerprint daemon = getFingerprintDaemon(); if (daemon == null) { @@ -1070,8 +951,38 @@ public class FingerprintService extends BiometricServiceBase { return 0; } - private List<Fingerprint> getEnrolledFingerprints(int userId) { - return getBiometricUtils().getBiometricsForUser(getContext(), userId); + // Attempt counter should only be cleared when Keyguard goes away or when + // a biometric is successfully authenticated. Lockout should eventually be done below the HAL. + // See AuthenticationClient#shouldFrameworkHandleLockout(). + private void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) { + if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) { + Slog.v(getTag(), "Reset biometric lockout, clearAttemptCounter=" + clearAttemptCounter); + } + if (clearAttemptCounter) { + mFailedAttempts.put(userId, 0); + } + mTimedLockoutCleared.put(userId, true); + // If we're asked to reset failed attempts externally (i.e. from Keyguard), + // the alarm might still be pending; remove it. + cancelLockoutResetForUser(userId); + notifyLockoutResetMonitors(); + } + + + private void cancelLockoutResetForUser(int userId) { + mAlarmManager.cancel(getLockoutResetIntentForUser(userId)); + } + + private void scheduleLockoutResetForUser(int userId) { + mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS, + getLockoutResetIntentForUser(userId)); + } + + private PendingIntent getLockoutResetIntentForUser(int userId) { + return PendingIntent.getBroadcast(getContext(), userId, + new Intent(getLockoutResetIntent()).putExtra(KEY_LOCKOUT_RESET_USER, userId), + PendingIntent.FLAG_UPDATE_CURRENT); } private void dumpInternal(PrintWriter pw) { diff --git a/services/core/java/com/android/server/biometrics/iris/IrisService.java b/services/core/java/com/android/server/biometrics/iris/IrisService.java index eb457b6bedf1..cb8a772356cc 100644 --- a/services/core/java/com/android/server/biometrics/iris/IrisService.java +++ b/services/core/java/com/android/server/biometrics/iris/IrisService.java @@ -17,12 +17,16 @@ package com.android.server.biometrics.iris; import android.content.Context; +import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricsProtoEnums; +import com.android.server.biometrics.AuthenticationClient; import com.android.server.biometrics.BiometricServiceBase; import com.android.server.biometrics.BiometricUtils; import com.android.server.biometrics.Metrics; +import java.util.List; + /** * A service to manage multiple clients that want to access the Iris HAL API. * The service is responsible for maintaining a list of clients and dispatching all @@ -61,18 +65,13 @@ public class IrisService extends BiometricServiceBase { } @Override - protected BiometricUtils getBiometricUtils() { + protected DaemonWrapper getDaemonWrapper() { return null; } @Override - protected int getFailedAttemptsLockoutTimed() { - return 0; - } - - @Override - protected int getFailedAttemptsLockoutPermanent() { - return 0; + protected BiometricUtils getBiometricUtils() { + return null; } @Override @@ -106,11 +105,6 @@ public class IrisService extends BiometricServiceBase { } @Override - protected void handleUserSwitching(int userId) { - - } - - @Override protected boolean hasEnrolledBiometrics(int userId) { return false; } @@ -131,7 +125,17 @@ public class IrisService extends BiometricServiceBase { } @Override + protected List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(int userId) { + return null; + } + + @Override protected int statsModality() { return BiometricsProtoEnums.MODALITY_IRIS; } + + @Override + protected int getLockoutMode() { + return AuthenticationClient.LOCKOUT_NONE; + } } diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index 19bdc0969e6d..c91e1a12078e 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -1859,7 +1859,7 @@ public class Tethering extends BaseNetworkObserver { final TetherState tetherState = new TetherState( new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService, makeControlCallback(), mConfig.enableLegacyDhcpServer, - mDeps.getIpServerDependencies(mContext))); + mDeps.getIpServerDependencies())); mTetherStates.put(iface, tetherState); tetherState.ipServer.start(); } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index e1af81b2f658..da547eaf8db2 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -241,7 +241,7 @@ public class Vpn { mNetworkCapabilities = new NetworkCapabilities(); mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN); mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN); - updateCapabilities(); + updateCapabilities(null /* defaultNetwork */); loadAlwaysOnPackage(); } @@ -268,22 +268,44 @@ public class Vpn { updateAlwaysOnNotification(detailedState); } - public void updateCapabilities() { - final Network[] underlyingNetworks = (mConfig != null) ? mConfig.underlyingNetworks : null; - // Only apps targeting Q and above can explicitly declare themselves as metered. - final boolean isAlwaysMetered = - mIsPackageTargetingAtLeastQ && (mConfig == null || mConfig.isMetered); - updateCapabilities(mContext.getSystemService(ConnectivityManager.class), underlyingNetworks, - mNetworkCapabilities, isAlwaysMetered); + /** + * Updates {@link #mNetworkCapabilities} based on current underlying networks and returns a + * defensive copy. + * + * <p>Does not propagate updated capabilities to apps. + * + * @param defaultNetwork underlying network for VPNs following platform's default + */ + public synchronized NetworkCapabilities updateCapabilities( + @Nullable Network defaultNetwork) { + if (mConfig == null) { + // VPN is not running. + return null; + } - if (mNetworkAgent != null) { - mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + Network[] underlyingNetworks = mConfig.underlyingNetworks; + if (underlyingNetworks == null && defaultNetwork != null) { + // null underlying networks means to track the default. + underlyingNetworks = new Network[] { defaultNetwork }; } + // Only apps targeting Q and above can explicitly declare themselves as metered. + final boolean isAlwaysMetered = mIsPackageTargetingAtLeastQ && mConfig.isMetered; + + applyUnderlyingCapabilities( + mContext.getSystemService(ConnectivityManager.class), + underlyingNetworks, + mNetworkCapabilities, + isAlwaysMetered); + + return new NetworkCapabilities(mNetworkCapabilities); } @VisibleForTesting - public static void updateCapabilities(ConnectivityManager cm, Network[] underlyingNetworks, - NetworkCapabilities caps, boolean isAlwaysMetered) { + public static void applyUnderlyingCapabilities( + ConnectivityManager cm, + Network[] underlyingNetworks, + NetworkCapabilities caps, + boolean isAlwaysMetered) { int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN }; int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; @@ -296,6 +318,7 @@ public class Vpn { boolean hadUnderlyingNetworks = false; if (null != underlyingNetworks) { for (Network underlying : underlyingNetworks) { + // TODO(b/124469351): Get capabilities directly from ConnectivityService instead. final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying); if (underlyingCaps == null) continue; hadUnderlyingNetworks = true; @@ -1007,9 +1030,8 @@ public class Vpn { } /** - * Establish a VPN network and return the file descriptor of the VPN - * interface. This methods returns {@code null} if the application is - * revoked or not prepared. + * Establish a VPN network and return the file descriptor of the VPN interface. This methods + * returns {@code null} if the application is revoked or not prepared. * * @param config The parameters to configure the network. * @return The file descriptor of the VPN interface. @@ -1101,8 +1123,6 @@ public class Vpn { // as rules are deleted. This prevents data leakage as the rules are moved over. agentDisconnect(oldNetworkAgent); } - // Set up VPN's capabilities such as meteredness. - updateCapabilities(); if (oldConnection != null) { mContext.unbindService(oldConnection); @@ -1258,6 +1278,11 @@ public class Vpn { return ranges; } + /** + * Updates UID ranges for this VPN and also updates its internal capabilities. + * + * <p>Should be called on primary ConnectivityService thread. + */ public void onUserAdded(int userHandle) { // If the user is restricted tie them to the parent user's VPN UserInfo user = UserManager.get(mContext).getUserInfo(userHandle); @@ -1268,8 +1293,9 @@ public class Vpn { try { addUserToRanges(existingRanges, userHandle, mConfig.allowedApplications, mConfig.disallowedApplications); + // ConnectivityService will call {@link #updateCapabilities} and apply + // those for VPN network. mNetworkCapabilities.setUids(existingRanges); - updateCapabilities(); } catch (Exception e) { Log.wtf(TAG, "Failed to add restricted user to owner", e); } @@ -1279,6 +1305,11 @@ public class Vpn { } } + /** + * Updates UID ranges for this VPN and also updates its capabilities. + * + * <p>Should be called on primary ConnectivityService thread. + */ public void onUserRemoved(int userHandle) { // clean up if restricted UserInfo user = UserManager.get(mContext).getUserInfo(userHandle); @@ -1290,8 +1321,9 @@ public class Vpn { final List<UidRange> removedRanges = uidRangesForUser(userHandle, existingRanges); existingRanges.removeAll(removedRanges); + // ConnectivityService will call {@link #updateCapabilities} and + // apply those for VPN network. mNetworkCapabilities.setUids(existingRanges); - updateCapabilities(); } catch (Exception e) { Log.wtf(TAG, "Failed to remove restricted user to owner", e); } @@ -1504,6 +1536,12 @@ public class Vpn { return success; } + /** + * Updates underlying network set. + * + * <p>Note: Does not updates capabilities. Call {@link #updateCapabilities} from + * ConnectivityService thread to get updated capabilities. + */ public synchronized boolean setUnderlyingNetworks(Network[] networks) { if (!isCallerEstablishedOwnerLocked()) { return false; @@ -1520,7 +1558,6 @@ public class Vpn { } } } - updateCapabilities(); return true; } diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java index 3fddac111ec5..173d7860e4ac 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java @@ -61,8 +61,8 @@ public class TetheringDependencies { /** * Get dependencies to be used by IpServer. */ - public IpServer.Dependencies getIpServerDependencies(Context context) { - return new IpServer.Dependencies(context); + public IpServer.Dependencies getIpServerDependencies() { + return new IpServer.Dependencies(); } /** diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java index 45f169ca0b6f..7dd3b363810d 100644 --- a/services/core/java/com/android/server/display/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/ColorDisplayService.java @@ -129,240 +129,9 @@ public final class ColorDisplayService extends SystemService { private final NightDisplayTintController mNightDisplayTintController = new NightDisplayTintController(); - private final TintController mDisplayWhiteBalanceTintController = new TintController() { - // Three chromaticity coordinates per color: X, Y, and Z - private final int NUM_VALUES_PER_PRIMARY = 3; - // Four colors: red, green, blue, and white - private final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY; - - private final Object mLock = new Object(); - private int mTemperatureMin; - private int mTemperatureMax; - private int mTemperatureDefault; - private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY]; - private ColorSpace.Rgb mDisplayColorSpaceRGB; - private float[] mChromaticAdaptationMatrix; - private int mCurrentColorTemperature; - private float[] mCurrentColorTemperatureXYZ; - private boolean mSetUp = false; - private float[] mMatrixDisplayWhiteBalance = new float[16]; - private Boolean mIsAvailable; - - @Override - public void setUp(Context context, boolean needsLinear) { - mSetUp = false; - final Resources res = context.getResources(); - - ColorSpace.Rgb displayColorSpaceRGB = getDisplayColorSpaceFromSurfaceControl(); - if (displayColorSpaceRGB == null) { - Slog.w(TAG, "Failed to get display color space from SurfaceControl, trying res"); - displayColorSpaceRGB = getDisplayColorSpaceFromResources(res); - if (displayColorSpaceRGB == null) { - Slog.e(TAG, "Failed to get display color space from resources"); - return; - } - } - - final String[] nominalWhiteValues = res.getStringArray( - R.array.config_displayWhiteBalanceDisplayNominalWhite); - float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY]; - for (int i = 0; i < nominalWhiteValues.length; i++) { - displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]); - } - - final int colorTemperatureMin = res.getInteger( - R.integer.config_displayWhiteBalanceColorTemperatureMin); - if (colorTemperatureMin <= 0) { - Slog.e(TAG, "Display white balance minimum temperature must be greater than 0"); - return; - } - - final int colorTemperatureMax = res.getInteger( - R.integer.config_displayWhiteBalanceColorTemperatureMax); - if (colorTemperatureMax < colorTemperatureMin) { - Slog.e(TAG, "Display white balance max temp must be greater or equal to min"); - return; - } - - final int colorTemperature = res.getInteger( - R.integer.config_displayWhiteBalanceColorTemperatureDefault); - - synchronized (mLock) { - mDisplayColorSpaceRGB = displayColorSpaceRGB; - mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ; - mTemperatureMin = colorTemperatureMin; - mTemperatureMax = colorTemperatureMax; - mTemperatureDefault = colorTemperature; - mSetUp = true; - } - - setMatrix(mTemperatureDefault); - } - - @Override - public float[] getMatrix() { - return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY; - } - - @Override - public void setMatrix(int cct) { - if (!mSetUp) { - Slog.w(TAG, "Can't set display white balance temperature: uninitialized"); - return; - } - - if (cct < mTemperatureMin) { - Slog.w(TAG, "Requested display color temperature is below allowed minimum"); - cct = mTemperatureMin; - } else if (cct > mTemperatureMax) { - Slog.w(TAG, "Requested display color temperature is above allowed maximum"); - cct = mTemperatureMax; - } - - Slog.d(TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct); - - synchronized (mLock) { - mCurrentColorTemperature = cct; - - // Adapt the display's nominal white point to match the requested CCT value - mCurrentColorTemperatureXYZ = ColorSpace.cctToIlluminantdXyz(cct); - - mChromaticAdaptationMatrix = - ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD, - mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ); - - // Convert the adaptation matrix to RGB space - float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix, - mDisplayColorSpaceRGB.getTransform()); - result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result); - - // Normalize the transform matrix to peak white value in RGB space - final float adaptedMaxR = result[0] + result[3] + result[6]; - final float adaptedMaxG = result[1] + result[4] + result[7]; - final float adaptedMaxB = result[2] + result[5] + result[8]; - final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB); - for (int i = 0; i < result.length; i++) { - result[i] /= denum; - } - - Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0); - java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3); - java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3); - java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3); - } - } - - @Override - public int getLevel() { - return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE; - } - - @Override - public boolean isAvailable(Context context) { - if (mIsAvailable == null) { - mIsAvailable = ColorDisplayManager.isDisplayWhiteBalanceAvailable(context); - } - return mIsAvailable; - } - - /** - * Format a given matrix into a string. - * - * @param matrix the matrix to format - * @param cols number of columns in the matrix - */ - private String matrixToString(float[] matrix, int cols) { - if (matrix == null || cols <= 0) { - Slog.e(TAG, "Invalid arguments when formatting matrix to string"); - return ""; - } - - StringBuilder sb = new StringBuilder(""); - for (int i = 0; i < matrix.length; i++) { - if (i % cols == 0) { - sb.append("\n "); - } - sb.append(String.format("%9.6f ", matrix[i])); - } - return sb.toString(); - } - - @Override - public void dump(PrintWriter pw) { - synchronized (mLock) { - pw.println(" mSetUp = " + mSetUp); - if (!mSetUp) { - return; - } - - pw.println(" isActivated = " + isActivated()); - pw.println(" mTemperatureMin = " + mTemperatureMin); - pw.println(" mTemperatureMax = " + mTemperatureMax); - pw.println(" mTemperatureDefault = " + mTemperatureDefault); - pw.println(" mCurrentColorTemperature = " + mCurrentColorTemperature); - pw.println(" mCurrentColorTemperatureXYZ = " + - matrixToString(mCurrentColorTemperatureXYZ, 3)); - pw.println(" mDisplayColorSpaceRGB RGB-to-XYZ = " + - matrixToString(mDisplayColorSpaceRGB.getTransform(), 3)); - pw.println(" mChromaticAdaptationMatrix = " + - matrixToString(mChromaticAdaptationMatrix, 3)); - pw.println(" mDisplayColorSpaceRGB XYZ-to-RGB = " + - matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3)); - pw.println(" mMatrixDisplayWhiteBalance = " + - matrixToString(mMatrixDisplayWhiteBalance, 4)); - } - } - - private ColorSpace.Rgb makeRgbColorSpaceFromXYZ(float[] redGreenBlueXYZ, float[] whiteXYZ) { - return new ColorSpace.Rgb( - "Display Color Space", - redGreenBlueXYZ, - whiteXYZ, - 2.2f // gamma, unused for display white balance - ); - } - - private ColorSpace.Rgb getDisplayColorSpaceFromSurfaceControl() { - final IBinder displayToken = SurfaceControl.getInternalDisplayToken(); - if (displayToken == null) { - return null; - } - - DisplayPrimaries primaries = SurfaceControl.getDisplayNativePrimaries(displayToken); - if (primaries == null || primaries.red == null || primaries.green == null || - primaries.blue == null || primaries.white == null) { - return null; - } - - return makeRgbColorSpaceFromXYZ( - new float[] { - primaries.red.X, primaries.red.Y, primaries.red.Z, - primaries.green.X, primaries.green.Y, primaries.green.Z, - primaries.blue.X, primaries.blue.Y, primaries.blue.Z, - }, - new float[] { primaries.white.X, primaries.white.Y, primaries.white.Z } - ); - } - - private ColorSpace.Rgb getDisplayColorSpaceFromResources(Resources res) { - final String[] displayPrimariesValues = res.getStringArray( - R.array.config_displayWhiteBalanceDisplayPrimaries); - float[] displayRedGreenBlueXYZ = - new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY]; - float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY]; - - for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) { - displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]); - } - - for (int i = 0; i < displayWhiteXYZ.length; i++) { - displayWhiteXYZ[i] = Float.parseFloat( - displayPrimariesValues[displayRedGreenBlueXYZ.length + i]); - } - - return makeRgbColorSpaceFromXYZ(displayRedGreenBlueXYZ, displayWhiteXYZ); - } - }; + @VisibleForTesting + final DisplayWhiteBalanceTintController mDisplayWhiteBalanceTintController = + new DisplayWhiteBalanceTintController(); private final TintController mGlobalSaturationTintController = new TintController() { @@ -860,7 +629,8 @@ public final class ColorDisplayService extends SystemService { return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt; } - private void updateDisplayWhiteBalanceStatus() { + @VisibleForTesting + void updateDisplayWhiteBalanceStatus() { boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated(); mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled() && !mNightDisplayTintController.isActivated() && @@ -1101,6 +871,7 @@ public final class ColorDisplayService extends SystemService { pw.println("Display white balance:"); if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) { pw.println(" Activated: " + mDisplayWhiteBalanceTintController.isActivated()); + mDisplayWhiteBalanceTintController.dump(pw); } else { pw.println(" Not available"); } @@ -1533,6 +1304,244 @@ public final class ColorDisplayService extends SystemService { } } + final class DisplayWhiteBalanceTintController extends TintController { + // Three chromaticity coordinates per color: X, Y, and Z + private final int NUM_VALUES_PER_PRIMARY = 3; + // Four colors: red, green, blue, and white + private final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY; + + private final Object mLock = new Object(); + @VisibleForTesting + int mTemperatureMin; + @VisibleForTesting + int mTemperatureMax; + private int mTemperatureDefault; + private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY]; + @VisibleForTesting + ColorSpace.Rgb mDisplayColorSpaceRGB; + private float[] mChromaticAdaptationMatrix; + @VisibleForTesting + int mCurrentColorTemperature; + private float[] mCurrentColorTemperatureXYZ; + private boolean mSetUp = false; + private float[] mMatrixDisplayWhiteBalance = new float[16]; + private Boolean mIsAvailable; + + @Override + public void setUp(Context context, boolean needsLinear) { + mSetUp = false; + final Resources res = context.getResources(); + + ColorSpace.Rgb displayColorSpaceRGB = getDisplayColorSpaceFromSurfaceControl(); + if (displayColorSpaceRGB == null) { + Slog.w(TAG, "Failed to get display color space from SurfaceControl, trying res"); + displayColorSpaceRGB = getDisplayColorSpaceFromResources(res); + if (displayColorSpaceRGB == null) { + Slog.e(TAG, "Failed to get display color space from resources"); + return; + } + } + + final String[] nominalWhiteValues = res.getStringArray( + R.array.config_displayWhiteBalanceDisplayNominalWhite); + float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY]; + for (int i = 0; i < nominalWhiteValues.length; i++) { + displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]); + } + + final int colorTemperatureMin = res.getInteger( + R.integer.config_displayWhiteBalanceColorTemperatureMin); + if (colorTemperatureMin <= 0) { + Slog.e(TAG, "Display white balance minimum temperature must be greater than 0"); + return; + } + + final int colorTemperatureMax = res.getInteger( + R.integer.config_displayWhiteBalanceColorTemperatureMax); + if (colorTemperatureMax < colorTemperatureMin) { + Slog.e(TAG, "Display white balance max temp must be greater or equal to min"); + return; + } + + final int colorTemperature = res.getInteger( + R.integer.config_displayWhiteBalanceColorTemperatureDefault); + + synchronized (mLock) { + mDisplayColorSpaceRGB = displayColorSpaceRGB; + mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ; + mTemperatureMin = colorTemperatureMin; + mTemperatureMax = colorTemperatureMax; + mTemperatureDefault = colorTemperature; + mSetUp = true; + } + + setMatrix(mTemperatureDefault); + } + + @Override + public float[] getMatrix() { + return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY; + } + + @Override + public void setMatrix(int cct) { + if (!mSetUp) { + Slog.w(TAG, "Can't set display white balance temperature: uninitialized"); + return; + } + + if (cct < mTemperatureMin) { + Slog.w(TAG, "Requested display color temperature is below allowed minimum"); + cct = mTemperatureMin; + } else if (cct > mTemperatureMax) { + Slog.w(TAG, "Requested display color temperature is above allowed maximum"); + cct = mTemperatureMax; + } + + Slog.d(TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct); + + synchronized (mLock) { + mCurrentColorTemperature = cct; + + // Adapt the display's nominal white point to match the requested CCT value + mCurrentColorTemperatureXYZ = ColorSpace.cctToIlluminantdXyz(cct); + + mChromaticAdaptationMatrix = + ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD, + mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ); + + // Convert the adaptation matrix to RGB space + float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix, + mDisplayColorSpaceRGB.getTransform()); + result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result); + + // Normalize the transform matrix to peak white value in RGB space + final float adaptedMaxR = result[0] + result[3] + result[6]; + final float adaptedMaxG = result[1] + result[4] + result[7]; + final float adaptedMaxB = result[2] + result[5] + result[8]; + final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB); + for (int i = 0; i < result.length; i++) { + result[i] /= denum; + } + + Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0); + java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3); + java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3); + java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3); + } + } + + @Override + public int getLevel() { + return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE; + } + + @Override + public boolean isAvailable(Context context) { + if (mIsAvailable == null) { + mIsAvailable = ColorDisplayManager.isDisplayWhiteBalanceAvailable(context); + } + return mIsAvailable; + } + + /** + * Format a given matrix into a string. + * + * @param matrix the matrix to format + * @param cols number of columns in the matrix + */ + private String matrixToString(float[] matrix, int cols) { + if (matrix == null || cols <= 0) { + Slog.e(TAG, "Invalid arguments when formatting matrix to string"); + return ""; + } + + StringBuilder sb = new StringBuilder(""); + for (int i = 0; i < matrix.length; i++) { + if (i % cols == 0) { + sb.append("\n "); + } + sb.append(String.format("%9.6f ", matrix[i])); + } + return sb.toString(); + } + + @Override + public void dump(PrintWriter pw) { + synchronized (mLock) { + pw.println(" mSetUp = " + mSetUp); + if (!mSetUp) { + return; + } + + pw.println(" mTemperatureMin = " + mTemperatureMin); + pw.println(" mTemperatureMax = " + mTemperatureMax); + pw.println(" mTemperatureDefault = " + mTemperatureDefault); + pw.println(" mCurrentColorTemperature = " + mCurrentColorTemperature); + pw.println(" mCurrentColorTemperatureXYZ = " + + matrixToString(mCurrentColorTemperatureXYZ, 3)); + pw.println(" mDisplayColorSpaceRGB RGB-to-XYZ = " + + matrixToString(mDisplayColorSpaceRGB.getTransform(), 3)); + pw.println(" mChromaticAdaptationMatrix = " + + matrixToString(mChromaticAdaptationMatrix, 3)); + pw.println(" mDisplayColorSpaceRGB XYZ-to-RGB = " + + matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3)); + pw.println(" mMatrixDisplayWhiteBalance = " + + matrixToString(mMatrixDisplayWhiteBalance, 4)); + } + } + + private ColorSpace.Rgb makeRgbColorSpaceFromXYZ(float[] redGreenBlueXYZ, float[] whiteXYZ) { + return new ColorSpace.Rgb( + "Display Color Space", + redGreenBlueXYZ, + whiteXYZ, + 2.2f // gamma, unused for display white balance + ); + } + + private ColorSpace.Rgb getDisplayColorSpaceFromSurfaceControl() { + final IBinder displayToken = SurfaceControl.getInternalDisplayToken(); + if (displayToken == null) { + return null; + } + + DisplayPrimaries primaries = SurfaceControl.getDisplayNativePrimaries(displayToken); + if (primaries == null || primaries.red == null || primaries.green == null || + primaries.blue == null || primaries.white == null) { + return null; + } + + return makeRgbColorSpaceFromXYZ( + new float[] { + primaries.red.X, primaries.red.Y, primaries.red.Z, + primaries.green.X, primaries.green.Y, primaries.green.Z, + primaries.blue.X, primaries.blue.Y, primaries.blue.Z, + }, + new float[] { primaries.white.X, primaries.white.Y, primaries.white.Z } + ); + } + + private ColorSpace.Rgb getDisplayColorSpaceFromResources(Resources res) { + final String[] displayPrimariesValues = res.getStringArray( + R.array.config_displayWhiteBalanceDisplayPrimaries); + float[] displayRedGreenBlueXYZ = + new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY]; + float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY]; + + for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) { + displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]); + } + + for (int i = 0; i < displayWhiteXYZ.length; i++) { + displayWhiteXYZ[i] = Float.parseFloat( + displayPrimariesValues[displayRedGreenBlueXYZ.length + i]); + } + + return makeRgbColorSpaceFromXYZ(displayRedGreenBlueXYZ, displayWhiteXYZ); + } + }; + /** * Local service that allows color transforms to be enabled from other system services. */ diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index dc5be6a6a074..15c7ef75866f 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -1019,7 +1019,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mDisplayWhiteBalanceController != null) { if (state == Display.STATE_ON && mDisplayWhiteBalanceSettings.isEnabled()) { mDisplayWhiteBalanceController.setEnabled(true); - mDisplayWhiteBalanceController.updateScreenColorTemperature(); + mDisplayWhiteBalanceController.updateDisplayColorTemperature(); } else { mDisplayWhiteBalanceController.setEnabled(false); } diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java index b9aa34e89216..d95e92b71357 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java @@ -29,14 +29,14 @@ import java.io.PrintWriter; /** * The DisplayWhiteBalanceController drives display white-balance (automatically correcting the - * screen color temperature depending on the ambient color temperature). + * display color temperature depending on the ambient color temperature). * * The DisplayWhiteBalanceController: * - Uses the AmbientColorTemperatureSensor to detect changes in the ambient color temperature; * - Uses the AmbientColorTemperatureFilter to average these changes over time, filter out the * noise, and arrive at an estimate of the actual ambient color temperature; - * - Uses the DisplayWhiteBalanceThrottler to decide whether the screen color tempearture should be - * updated, suppressing changes that are too frequent or too minor. + * - Uses the DisplayWhiteBalanceThrottler to decide whether the display color tempearture should + * be updated, suppressing changes that are too frequent or too minor. */ public class DisplayWhiteBalanceController implements AmbientSensor.AmbientBrightnessSensor.Callbacks, @@ -76,8 +76,8 @@ public class DisplayWhiteBalanceController implements // Override the ambient color temperature for debugging purposes. private float mAmbientColorTemperatureOverride; - // A piecewise linear relationship between ambient and display color temperatures - private Spline.LinearSpline mAmbientToDisplayTemperatureSpline; + // A piecewise linear relationship between ambient and display color temperatures. + private Spline.LinearSpline mAmbientToDisplayColorTemperatureSpline; /** * @param brightnessSensor @@ -91,7 +91,7 @@ public class DisplayWhiteBalanceController implements * The filter used to average ambient color temperature changes over time, filter out the * noise and arrive at an estimate of the actual ambient color temperature. * @param throttler - * The throttler used to determine whether the new screen color temperature should be + * The throttler used to determine whether the new display color temperature should be * updated or not. * @param lowLightAmbientBrightnessThreshold * The ambient brightness threshold beneath which we fall back to a fixed ambient color @@ -99,6 +99,12 @@ public class DisplayWhiteBalanceController implements * @param lowLightAmbientColorTemperature * The ambient color temperature to which we fall back when the ambient brightness drops * beneath a certain threshold. + * @param ambientColorTemperatures + * The ambient color tempeartures used to map the ambient color temperature to the display + * color temperature (or null if no mapping is necessary). + * @param displayColorTemperatures + * The display color temperatures used to map the ambient color temperature to the display + * color temperature (or null if no mapping is necessary). * * @throws NullPointerException * - brightnessSensor is null; @@ -114,7 +120,7 @@ public class DisplayWhiteBalanceController implements @NonNull AmbientFilter colorTemperatureFilter, @NonNull DisplayWhiteBalanceThrottler throttler, float lowLightAmbientBrightnessThreshold, float lowLightAmbientColorTemperature, - float[] ambientTemperatures, float[] displayTemperatures) { + float[] ambientColorTemperatures, float[] displayColorTemperatures) { validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter, throttler); mLoggingEnabled = false; @@ -134,10 +140,10 @@ public class DisplayWhiteBalanceController implements mAmbientColorTemperatureOverride = -1.0f; try { - mAmbientToDisplayTemperatureSpline = new Spline.LinearSpline(ambientTemperatures, - displayTemperatures); + mAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline( + ambientColorTemperatures, displayColorTemperatures); } catch (Exception e) { - mAmbientToDisplayTemperatureSpline = null; + mAmbientToDisplayColorTemperatureSpline = null; } mColorDisplayServiceInternal = LocalServices.getService(ColorDisplayServiceInternal.class); @@ -160,7 +166,7 @@ public class DisplayWhiteBalanceController implements } /** - * Set an object to call back to when the screen color temperature should be updated. + * Set an object to call back to when the display color temperature should be updated. * * @param callbacks * The object to call back to. @@ -201,7 +207,7 @@ public class DisplayWhiteBalanceController implements * * This is only applied when the ambient color temperature changes or is updated (in which case * it overrides the ambient color temperature estimate); in other words, it doesn't necessarily - * change the screen color temperature immediately. + * change the display color temperature immediately. * * @param ambientColorTemperatureOverride * The ambient color temperature override. @@ -240,9 +246,8 @@ public class DisplayWhiteBalanceController implements writer.println(" mLastAmbientColorTemperature=" + mLastAmbientColorTemperature); writer.println(" mAmbientColorTemperatureHistory=" + mAmbientColorTemperatureHistory); writer.println(" mAmbientColorTemperatureOverride=" + mAmbientColorTemperatureOverride); - writer.println(" mAmbientToDisplayTemperatureSpline=" - + (mAmbientToDisplayTemperatureSpline == null ? "unused" : - mAmbientToDisplayTemperatureSpline)); + writer.println(" mAmbientToDisplayColorTemperatureSpline=" + + mAmbientToDisplayColorTemperatureSpline); } @Override // AmbientSensor.AmbientBrightnessSensor.Callbacks @@ -266,9 +271,9 @@ public class DisplayWhiteBalanceController implements final long time = System.currentTimeMillis(); float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time); - if (mAmbientToDisplayTemperatureSpline != null) { + if (mAmbientToDisplayColorTemperatureSpline != null) { ambientColorTemperature = - mAmbientToDisplayTemperatureSpline.interpolate(ambientColorTemperature); + mAmbientToDisplayColorTemperatureSpline.interpolate(ambientColorTemperature); } final float ambientBrightness = mBrightnessFilter.getEstimate(time); @@ -290,7 +295,7 @@ public class DisplayWhiteBalanceController implements ambientColorTemperature = mAmbientColorTemperatureOverride; } - // When the screen color temperature needs to be updated, we call DisplayPowerController to + // When the display color temperature needs to be updated, we call DisplayPowerController to // call our updateColorTemperature. The reason we don't call it directly is that we want // all changes to the system to happen in a predictable order in DPC's main loop // (updatePowerState). @@ -308,9 +313,9 @@ public class DisplayWhiteBalanceController implements } /** - * Updates the screen color temperature. + * Updates the display color temperature. */ - public void updateScreenColorTemperature() { + public void updateDisplayColorTemperature() { float ambientColorTemperature = -1.0f; // If both the pending and the current ambient color temperatures are -1, it means the DWBC @@ -353,7 +358,7 @@ public class DisplayWhiteBalanceController implements * Called whenever the display white-balance state has changed. * * Usually, this means the estimated ambient color temperature has changed enough, and the - * screen color temperature should be updated; but it is also called by + * display color temperature should be updated; but it is also called if settings change. */ void updateWhiteBalance(); } diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java index 56f4ca339eb3..449f115ad3e3 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java @@ -67,14 +67,14 @@ public class DisplayWhiteBalanceFactory { final float lowLightAmbientColorTemperature = getFloat(resources, com.android.internal.R.dimen .config_displayWhiteBalanceLowLightAmbientColorTemperature); - final float[] ambientTemperatures = getFloatArray(resources, - com.android.internal.R.array.config_displayWhiteBalanceAmbientTemperatureValues); - final float[] displayTemperatures = getFloatArray(resources, - com.android.internal.R.array.config_displayWhiteBalanceDisplayTemperatureValues); + final float[] ambientColorTemperatures = getFloatArray(resources, + com.android.internal.R.array.config_displayWhiteBalanceAmbientColorTemperatures); + final float[] displayColorTempeartures = getFloatArray(resources, + com.android.internal.R.array.config_displayWhiteBalanceDisplayColorTemperatures); final DisplayWhiteBalanceController controller = new DisplayWhiteBalanceController( brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter, throttler, lowLightAmbientBrightnessThreshold, lowLightAmbientColorTemperature, - ambientTemperatures, displayTemperatures); + ambientColorTemperatures, displayColorTempeartures); brightnessSensor.setCallbacks(controller); colorTemperatureSensor.setCallbacks(controller); return controller; diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java index c1f0e983a366..5941bbb62d94 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java @@ -23,7 +23,7 @@ import java.util.Arrays; /** * The DisplayWhiteBalanceController uses the DisplayWhiteBalanceThrottler to decide whether the - * screen color temperature should be updated, suppressing changes that are too frequent or too + * display color temperature should be updated, suppressing changes that are too frequent or too * minor. */ class DisplayWhiteBalanceThrottler { diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index e71b156c3e86..243b6fe7a30e 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -373,6 +373,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private final NtpTimeHelper mNtpTimeHelper; private final GnssBatchingProvider mGnssBatchingProvider; private final GnssGeofenceProvider mGnssGeofenceProvider; + // Available only on GNSS HAL 2.0 implementations and later. private GnssVisibilityControl mGnssVisibilityControl; // Handler for processing events @@ -463,8 +464,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } }; - // TODO(b/119326010): replace OnSubscriptionsChangedListener with - // ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED broadcast reseiver. + // TODO: replace OnSubscriptionsChangedListener with ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED + // broadcast receiver. private final OnSubscriptionsChangedListener mOnSubscriptionsChangedListener = new OnSubscriptionsChangedListener() { @Override @@ -676,8 +677,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mNtpTimeHelper.onNetworkAvailable(); if (mDownloadXtraDataPending == STATE_PENDING_NETWORK) { if (mSupportsXtra) { - // Download only if supported, (prevents an unneccesary on-boot - // download) + // Download only if supported, (prevents an unnecessary on-boot download) xtraDownloadRequest(); } } @@ -764,7 +764,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements /** Returns true if the location request is too frequent. */ private boolean isRequestLocationRateLimited() { - // TODO(b/73198123): implement exponential backoff. + // TODO: implement exponential backoff. return false; } diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java index c3f25bfa2e5e..20f872a213fd 100644 --- a/services/core/java/com/android/server/location/GnssVisibilityControl.java +++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java @@ -24,10 +24,13 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.database.ContentObserver; +import android.location.LocationManager; import android.os.Handler; import android.os.Looper; import android.os.PowerManager; import android.os.UserHandle; +import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import android.util.StatsLog; @@ -70,7 +73,7 @@ class GnssVisibilityControl { private final Handler mHandler; private final Context mContext; - private boolean mIsMasterLocationSettingsEnabled = true; + private boolean mIsDeviceLocationSettingsEnabled; // Number of non-framework location access proxy apps is expected to be small (< 5). private static final int HASH_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS = 7; @@ -88,13 +91,9 @@ class GnssVisibilityControl { mAppOps = mContext.getSystemService(AppOpsManager.class); mPackageManager = mContext.getPackageManager(); - // Set to empty proxy app list initially until the configuration properties are loaded. - updateNfwLocationAccessProxyAppsInGnssHal(); - - // Listen for proxy app package installation, removal events. - listenForProxyAppsPackageUpdates(); - - // TODO(b/122855984): Handle global location settings on/off. + // Complete initialization as the first event to run in mHandler thread. After that, + // all object state read/update events run in the mHandler thread. + runOnHandler(this::handleInitialize); } void updateProxyApps(List<String> nfwLocationAccessProxyApps) { @@ -105,10 +104,6 @@ class GnssVisibilityControl { runOnHandler(() -> handleUpdateProxyApps(nfwLocationAccessProxyApps)); } - void masterLocationSettingsUpdated(boolean enabled) { - runOnHandler(() -> handleMasterLocationSettingsUpdated(enabled)); - } - void reportNfwNotification(String proxyAppPackageName, byte protocolStack, String otherProtocolStackName, byte requestor, String requestorId, byte responseType, boolean inEmergencyMode, boolean isCachedLocation) { @@ -117,7 +112,19 @@ class GnssVisibilityControl { requestor, requestorId, responseType, inEmergencyMode, isCachedLocation))); } + private void handleInitialize() { + disableNfwLocationAccess(); // Disable until config properties are loaded. + listenForProxyAppsPackageUpdates(); + listenForDeviceLocationSettingsUpdate(); + mIsDeviceLocationSettingsEnabled = getDeviceLocationSettings(); + } + + private boolean getDeviceLocationSettings() { + return mContext.getSystemService(LocationManager.class).isLocationEnabled(); + } + private void listenForProxyAppsPackageUpdates() { + // Listen for proxy apps package installation, removal events. IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); @@ -143,11 +150,22 @@ class GnssVisibilityControl { }, UserHandle.ALL, intentFilter, null, mHandler); } + private void listenForDeviceLocationSettingsUpdate() { + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE), + true, + new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + handleDeviceLocationSettingsUpdated(); + } + }, UserHandle.USER_ALL); + } + private void handleProxyAppPackageUpdate(String pkgName, String action) { final Boolean locationPermission = mProxyAppToLocationPermissions.get(pkgName); - // pkgName is not one of the proxy apps in our list. if (locationPermission == null) { - return; + return; // ignore, pkgName is not one of the proxy apps in our list. } Log.i(TAG, "Proxy app " + pkgName + " package changed: " + action); @@ -197,15 +215,33 @@ class GnssVisibilityControl { return true; } } - return false; } - private void handleMasterLocationSettingsUpdated(boolean enabled) { - mIsMasterLocationSettingsEnabled = enabled; - Log.i(TAG, "Master location settings switch changed to " - + (enabled ? "enabled" : "disabled")); - updateNfwLocationAccessProxyAppsInGnssHal(); + private void handleDeviceLocationSettingsUpdated() { + final boolean enabled = getDeviceLocationSettings(); + Log.i(TAG, "Device location settings enabled: " + enabled); + + if (mIsDeviceLocationSettingsEnabled == enabled) { + return; + } + + mIsDeviceLocationSettingsEnabled = enabled; + if (!mIsDeviceLocationSettingsEnabled) { + disableNfwLocationAccess(); + return; + } + + // When device location settings was disabled, we already set the proxy app list + // to empty in GNSS HAL. Update only if the proxy app list is not empty. + String[] locationPermissionEnabledProxyApps = getLocationPermissionEnabledProxyApps(); + if (locationPermissionEnabledProxyApps.length != 0) { + setNfwLocationAccessProxyAppsInGnssHal(locationPermissionEnabledProxyApps); + } + } + + private void disableNfwLocationAccess() { + setNfwLocationAccessProxyAppsInGnssHal(NO_LOCATION_ENABLED_PROXY_APPS); } // Represents NfwNotification structure in IGnssVisibilityControlCallback.hal @@ -316,8 +352,7 @@ class GnssVisibilityControl { return mPackageManager.getApplicationInfo(pkgName, 0).uid; } catch (PackageManager.NameNotFoundException e) { if (DEBUG) { - Log.d(TAG, "Non-framework location access proxy app " - + pkgName + " is not found."); + Log.d(TAG, "Non-framework location access proxy app " + pkgName + " is not found."); } return null; } @@ -329,8 +364,14 @@ class GnssVisibilityControl { } private void updateNfwLocationAccessProxyAppsInGnssHal() { - final String[] locationPermissionEnabledProxyApps = shouldDisableNfwLocationAccess() - ? NO_LOCATION_ENABLED_PROXY_APPS : getLocationPermissionEnabledProxyApps(); + if (!mIsDeviceLocationSettingsEnabled) { + return; // Keep non-framework location access disabled. + } + setNfwLocationAccessProxyAppsInGnssHal(getLocationPermissionEnabledProxyApps()); + } + + private void setNfwLocationAccessProxyAppsInGnssHal( + String[] locationPermissionEnabledProxyApps) { final String proxyAppsStr = Arrays.toString(locationPermissionEnabledProxyApps); Log.i(TAG, "Updating non-framework location access proxy apps in the GNSS HAL to: " + proxyAppsStr); @@ -341,12 +382,8 @@ class GnssVisibilityControl { } } - private boolean shouldDisableNfwLocationAccess() { - return !mIsMasterLocationSettingsEnabled; - } - private String[] getLocationPermissionEnabledProxyApps() { - // Get a count of proxy apps with location permission enabled to array creation size. + // Get a count of proxy apps with location permission enabled for array creation size. int countLocationPermissionEnabledProxyApps = 0; for (Boolean hasLocationPermissionEnabled : mProxyAppToLocationPermissions.values()) { if (hasLocationPermissionEnabled) { diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index a9ae74f67de7..4b4788cd4a16 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -55,6 +55,8 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.database.sqlite.SQLiteDatabase; import android.hardware.authsecret.V1_0.IAuthSecret; +import android.hardware.biometrics.BiometricManager; +import android.hardware.face.FaceManager; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -671,7 +673,6 @@ public class LockSettingsService extends ILockSettings.Stub { mDeviceProvisionedObserver.onSystemReady(); // TODO: maybe skip this for split system user mode. mStorage.prefetchUser(UserHandle.USER_SYSTEM); - mStrongAuth.systemReady(); } private void migrateOldData() { @@ -2375,6 +2376,14 @@ public class LockSettingsService extends ILockSettings.Stub { userCredential = null; } + final PackageManager pm = mContext.getPackageManager(); + // TODO: When lockout is handled under the HAL for all biometrics (fingerprint), + // we need to generate challenge for each one, have it signed by GK and reset lockout + // for each modality. + if (!hasChallenge && pm.hasSystemFeature(PackageManager.FEATURE_FACE)) { + challenge = mContext.getSystemService(FaceManager.class).generateChallenge(); + } + final AuthenticationResult authResult; VerifyCredentialResponse response; synchronized (mSpManager) { @@ -2413,6 +2422,17 @@ public class LockSettingsService extends ILockSettings.Stub { if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { notifyActivePasswordMetricsAvailable(userCredential, userId); unlockKeystore(authResult.authToken.deriveKeyStorePassword(), userId); + // Reset lockout + if (BiometricManager.hasBiometrics(mContext)) { + BiometricManager bm = mContext.getSystemService(BiometricManager.class); + Slog.i(TAG, "Resetting lockout, length: " + + authResult.gkResponse.getPayload().length); + bm.resetLockout(authResult.gkResponse.getPayload()); + + if (!hasChallenge && pm.hasSystemFeature(PackageManager.FEATURE_FACE)) { + mContext.getSystemService(FaceManager.class).revokeChallenge(); + } + } final byte[] secret = authResult.authToken.deriveDiskEncryptionKey(); Slog.i(TAG, "Unlocking user " + userId + " with secret only, length " + secret.length); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java index 44804350309d..a84306c97dc0 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java @@ -16,15 +16,16 @@ package com.android.server.locksettings; -import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED; -import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker + .STRONG_AUTH_NOT_REQUIRED; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker + .STRONG_AUTH_REQUIRED_AFTER_TIMEOUT; import android.app.AlarmManager; import android.app.AlarmManager.OnAlarmListener; import android.app.admin.DevicePolicyManager; import android.app.trust.IStrongAuthTracker; import android.content.Context; -import android.hardware.biometrics.BiometricManager; import android.os.Handler; import android.os.Message; import android.os.RemoteCallbackList; @@ -61,7 +62,6 @@ public class LockSettingsStrongAuth { private final Context mContext; private AlarmManager mAlarmManager; - private BiometricManager mBiometricManager; public LockSettingsStrongAuth(Context context) { mContext = context; @@ -69,12 +69,6 @@ public class LockSettingsStrongAuth { mAlarmManager = context.getSystemService(AlarmManager.class); } - public void systemReady() { - if (BiometricManager.hasBiometrics(mContext)) { - mBiometricManager = mContext.getSystemService(BiometricManager.class); - } - } - private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) { mTrackers.register(tracker); @@ -185,11 +179,6 @@ public class LockSettingsStrongAuth { } public void reportSuccessfulStrongAuthUnlock(int userId) { - if (mBiometricManager != null) { - byte[] token = null; /* TODO: pass real auth token once HAL supports it */ - mBiometricManager.resetTimeout(token); - } - final int argNotUsed = 0; mHandler.obtainMessage(MSG_SCHEDULE_STRONG_AUTH_TIMEOUT, userId, argNotUsed).sendToTarget(); } diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index b6ef180f4b59..b221241c25e2 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -1207,6 +1207,15 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } } + public void setPlaybackSpeed(String packageName, int pid, int uid, + ControllerCallbackLink caller, float speed) { + try { + mCb.notifySetPlaybackSpeed(packageName, pid, uid, caller, speed); + } catch (RuntimeException e) { + Slog.e(TAG, "Remote failure in setPlaybackSpeed.", e); + } + } + public void adjustVolume(String packageName, int pid, int uid, ControllerCallbackLink caller, boolean asSystemService, int direction) { try { @@ -1446,6 +1455,13 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override + public void setPlaybackSpeed(String packageName, ControllerCallbackLink caller, + float speed) { + mSessionCb.setPlaybackSpeed(packageName, Binder.getCallingPid(), Binder.getCallingUid(), + caller, speed); + } + + @Override public void sendCustomAction(String packageName, ControllerCallbackLink caller, String action, Bundle args) { mSessionCb.sendCustomAction(packageName, Binder.getCallingPid(), Binder.getCallingUid(), diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java index 9e5b92a6b944..3f15b381c18b 100644 --- a/services/core/java/com/android/server/net/LockdownVpnTracker.java +++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java @@ -17,9 +17,6 @@ package com.android.server.net; import static android.Manifest.permission.CONNECTIVITY_INTERNAL; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.provider.Settings.ACTION_VPN_SETTINGS; import android.app.Notification; @@ -30,17 +27,14 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; -import android.net.LinkProperties; import android.net.LinkAddress; +import android.net.LinkProperties; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkInfo.State; -import android.net.NetworkPolicyManager; import android.os.INetworkManagementService; -import android.os.RemoteException; import android.security.Credentials; import android.security.KeyStore; -import android.system.Os; import android.text.TextUtils; import android.util.Slog; diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java index 4bd8f450c76b..6d82c1c257ab 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -15,15 +15,15 @@ */ package com.android.server.net; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; +import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; +import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; +import static android.net.INetd.FIREWALL_CHAIN_STANDBY; +import static android.net.INetd.FIREWALL_RULE_ALLOW; +import static android.net.INetd.FIREWALL_RULE_DENY; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY; import android.app.ActivityManager; import android.net.NetworkPolicyManager; diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index af55605975ca..75b62cb349af 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -38,6 +38,11 @@ import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLE import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED; import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED; import static android.net.ConnectivityManager.TYPE_MOBILE; +import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; +import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; +import static android.net.INetd.FIREWALL_CHAIN_STANDBY; +import static android.net.INetd.FIREWALL_RULE_ALLOW; +import static android.net.INetd.FIREWALL_RULE_DENY; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; @@ -45,12 +50,7 @@ import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.SNOOZE_NEVER; import static android.net.NetworkPolicy.WARNING_DISABLED; import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY; import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS; import static android.net.NetworkPolicyManager.MASK_METERED_NETWORKS; import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND; diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index e479a1539e25..d0c59c1b002e 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -320,6 +320,11 @@ public final class OverlayManagerService extends SystemService { private final class PackageReceiver extends BroadcastReceiver { @Override public void onReceive(@NonNull final Context context, @NonNull final Intent intent) { + final String action = intent.getAction(); + if (action == null) { + Slog.e(TAG, "Cannot handle package broadcast with null action"); + return; + } final Uri data = intent.getData(); if (data == null) { Slog.e(TAG, "Cannot handle package broadcast with null data"); @@ -337,7 +342,7 @@ public final class OverlayManagerService extends SystemService { userIds = new int[] { UserHandle.getUserId(extraUid) }; } - switch (intent.getAction()) { + switch (action) { case ACTION_PACKAGE_ADDED: if (replacing) { onPackageUpgraded(packageName, userIds); diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS index 33b8641c145e..640b155e69d5 100644 --- a/services/core/java/com/android/server/pm/OWNERS +++ b/services/core/java/com/android/server/pm/OWNERS @@ -19,6 +19,9 @@ per-file BackgroundDexOptService.java = ngeoffray@google.com per-file CompilerStats.java = agampe@google.com per-file CompilerStats.java = calin@google.com per-file CompilerStats.java = ngeoffray@google.com +per-file DynamicCodeLoggingService.java = agampe@google.com +per-file DynamicCodeLoggingService.java = calin@google.com +per-file DynamicCodeLoggingService.java = ngeoffray@google.com per-file InstructionSets.java = agampe@google.com per-file InstructionSets.java = calin@google.com per-file InstructionSets.java = ngeoffray@google.com diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 86083494f3d6..f5d88e3cf56f 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -84,7 +84,7 @@ import com.android.internal.util.ImageUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.IoThread; import com.android.server.LocalServices; -import com.android.server.pm.permission.PermissionManagerInternal; +import com.android.server.pm.permission.PermissionManagerServiceInternal; import libcore.io.IoUtils; @@ -131,7 +131,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements private final Context mContext; private final PackageManagerService mPm; private final StagingManager mStagingManager; - private final PermissionManagerInternal mPermissionManager; + private final PermissionManagerServiceInternal mPermissionManager; private AppOpsManager mAppOps; @@ -189,7 +189,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements public PackageInstallerService(Context context, PackageManagerService pm, ApexManager am) { mContext = context; mPm = pm; - mPermissionManager = LocalServices.getService(PermissionManagerInternal.class); + mPermissionManager = LocalServices.getService(PermissionManagerServiceInternal.class); mInstallThread = new HandlerThread(TAG); mInstallThread.start(); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 941de895e513..b1c186e9e9f8 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -238,7 +238,6 @@ import android.os.storage.StorageManager; import android.os.storage.StorageManagerInternal; import android.os.storage.VolumeInfo; import android.os.storage.VolumeRecord; -import android.permission.PermissionControllerManager; import android.provider.DeviceConfig; import android.provider.MediaStore; import android.provider.Settings.Global; @@ -291,7 +290,6 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; -import com.android.internal.util.XmlUtils; import com.android.server.AttributeCache; import com.android.server.DeviceIdleController; import com.android.server.EventLogTags; @@ -315,9 +313,9 @@ import com.android.server.pm.dex.ViewCompiler; import com.android.server.pm.permission.BasePermission; import com.android.server.pm.permission.DefaultPermissionGrantPolicy; import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback; -import com.android.server.pm.permission.PermissionManagerInternal; -import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback; import com.android.server.pm.permission.PermissionManagerService; +import com.android.server.pm.permission.PermissionManagerServiceInternal; +import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback; import com.android.server.pm.permission.PermissionsState; import com.android.server.security.VerityUtils; import com.android.server.storage.DeviceStorageMonitorInternal; @@ -371,7 +369,6 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; @@ -445,8 +442,6 @@ public class PackageManagerService extends IPackageManager.Stub private static final boolean ENABLE_FREE_CACHE_V2 = SystemProperties.getBoolean("fw.free_cache_v2", true); - private static final long BACKUP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60); - private static final String PRECOMPILE_LAYOUTS = "pm.precompile_layouts"; private static final int RADIO_UID = Process.PHONE_UID; @@ -940,7 +935,7 @@ public class PackageManagerService extends IPackageManager.Stub // TODO remove this and go through mPermissonManager directly final DefaultPermissionGrantPolicy mDefaultPermissionPolicy; - private final PermissionManagerInternal mPermissionManager; + private final PermissionManagerServiceInternal mPermissionManager; private final ComponentResolver mComponentResolver; // List of packages names to keep cached, even if they are uninstalled for all users @@ -985,6 +980,9 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mPackages") private PackageManagerInternal.DefaultBrowserProvider mDefaultBrowserProvider; + @GuardedBy("mPackages") + private PackageManagerInternal.DefaultHomeProvider mDefaultHomeProvider; + private class IntentVerifierProxy implements IntentFilterVerifier<ActivityIntentInfo> { private Context mContext; private ComponentName mIntentFilterVerifierComponent; @@ -1349,7 +1347,7 @@ public class PackageManagerService extends IPackageManager.Stub final @Nullable String mRequiredVerifierPackage; final @NonNull String mRequiredInstallerPackage; final @NonNull String mRequiredUninstallerPackage; - final String mRequiredPermissionControllerPackage; + final @NonNull String mRequiredPermissionControllerPackage; final @Nullable String mSetupWizardPackage; final @Nullable String mStorageManagerPackage; final @Nullable String mSystemTextClassifierPackage; @@ -1937,9 +1935,14 @@ public class PackageManagerService extends IPackageManager.Stub } } - // We may also need to apply pending (restored) runtime - // permission grants within these users. - mSettings.applyPendingPermissionGrantsLPw(packageName, userId); + // We may also need to apply pending (restored) runtime permission grants + // within these users. + mPermissionManager.restoreDelayedRuntimePermissions(packageName, + UserHandle.of(userId)); + + // Persistent preferred activity might have came into effect due to this + // install. + updateDefaultHomeLPw(userId); } } } @@ -18818,7 +18821,7 @@ public class PackageManagerService extends IPackageManager.Stub // permission as requiring a review as this is the initial state. int flags = 0; if (ps.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M && bp.isRuntime()) { - flags |= FLAG_PERMISSION_REVIEW_REQUIRED; + flags |= FLAG_PERMISSION_REVIEW_REQUIRED | FLAG_PERMISSION_REVOKE_ON_UPGRADE; } if (permissionsState.updatePermissionFlags(bp, userId, userSettableMask, flags)) { if (hasInstallState) { @@ -19077,6 +19080,7 @@ public class PackageManagerService extends IPackageManager.Stub pir.addFilter(new PreferredActivity(filter, match, set, activity, always)); scheduleWritePackageRestrictionsLocked(userId); postPreferredActivityChangedBroadcast(userId); + updateDefaultHomeLPw(userId); } } @@ -19227,6 +19231,13 @@ public class PackageManagerService extends IPackageManager.Stub /** This method takes a specific user id as well as UserHandle.USER_ALL. */ @GuardedBy("mPackages") boolean clearPackagePreferredActivitiesLPw(String packageName, int userId) { + return clearPackagePreferredActivitiesLPw(packageName, false, userId); + } + + /** This method takes a specific user id as well as UserHandle.USER_ALL. */ + @GuardedBy("mPackages") + private boolean clearPackagePreferredActivitiesLPw(String packageName, + boolean skipUpdateDefaultHome, int userId) { ArrayList<PreferredActivity> removed = null; boolean changed = false; for (int i=0; i<mSettings.mPreferredActivities.size(); i++) { @@ -19255,6 +19266,9 @@ public class PackageManagerService extends IPackageManager.Stub pir.removeFilter(pa); } changed = true; + if (!skipUpdateDefaultHome) { + updateDefaultHomeLPw(thisUserId); + } } } if (changed) { @@ -19314,8 +19328,9 @@ public class PackageManagerService extends IPackageManager.Stub // writer try { synchronized (mPackages) { - clearPackagePreferredActivitiesLPw(null, userId); + clearPackagePreferredActivitiesLPw(null, true, userId); mSettings.applyDefaultPreferredAppsLPw(userId); + updateDefaultHomeLPw(userId); // TODO: We have to reset the default SMS and Phone. This requires // significant refactoring to keep all default apps in the package // manager (cleaner but more work) or have the services provide @@ -19384,6 +19399,7 @@ public class PackageManagerService extends IPackageManager.Stub new PersistentPreferredActivity(filter, activity)); scheduleWritePackageRestrictionsLocked(userId); postPreferredActivityChangedBroadcast(userId); + updateDefaultHomeLPw(userId); } } @@ -19427,6 +19443,7 @@ public class PackageManagerService extends IPackageManager.Stub if (changed) { scheduleWritePackageRestrictionsLocked(userId); postPreferredActivityChangedBroadcast(userId); + updateDefaultHomeLPw(userId); } } } @@ -19514,6 +19531,7 @@ public class PackageManagerService extends IPackageManager.Stub (readParser, readUserId) -> { synchronized (mPackages) { mSettings.readPreferredActivitiesLPw(readParser, readUserId); + updateDefaultHomeLPw(readUserId); } }); } catch (Exception e) { @@ -19569,8 +19587,17 @@ public class PackageManagerService extends IPackageManager.Stub parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name()); restoreFromXml(parser, userId, TAG_DEFAULT_APPS, (parser1, userId1) -> { + String defaultBrowser; synchronized (mPackages) { mSettings.readDefaultAppsLPw(parser1, userId1); + defaultBrowser = mSettings.removeDefaultBrowserPackageNameLPw(userId1); + } + if (defaultBrowser != null) { + PackageManagerInternal.DefaultBrowserProvider provider; + synchronized (mPackages) { + provider = mDefaultBrowserProvider; + } + provider.setDefaultBrowser(defaultBrowser, userId1); } }); } catch (Exception e) { @@ -19634,139 +19661,6 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public byte[] getPermissionGrantBackup(int userId) { - if (Binder.getCallingUid() != Process.SYSTEM_UID) { - throw new SecurityException("Only the system may call getPermissionGrantBackup()"); - } - - AtomicReference<byte[]> backup = new AtomicReference<>(); - mContext.getSystemService(PermissionControllerManager.class).getRuntimePermissionBackup( - UserHandle.of(userId), mContext.getMainExecutor(), (b) -> { - synchronized (backup) { - backup.set(b); - backup.notifyAll(); - } - }); - - long start = System.currentTimeMillis(); - synchronized (backup) { - while (backup.get() == null) { - long timeLeft = start + BACKUP_TIMEOUT_MILLIS - System.currentTimeMillis(); - if (timeLeft <= 0) { - return null; - } - - try { - backup.wait(timeLeft); - } catch (InterruptedException ignored) { - return null; - } - } - } - - return backup.get(); - } - - @Override - public void restorePermissionGrants(byte[] backup, int userId) { - if (Binder.getCallingUid() != Process.SYSTEM_UID) { - throw new SecurityException("Only the system may call restorePermissionGrants()"); - } - - try { - final XmlPullParser parser = Xml.newPullParser(); - parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name()); - restoreFromXml(parser, userId, TAG_PERMISSION_BACKUP, - (parser1, userId1) -> { - synchronized (mPackages) { - processRestoredPermissionGrantsLPr(parser1, userId1); - } - }); - } catch (Exception e) { - if (DEBUG_BACKUP) { - Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage()); - } - } - } - - @GuardedBy("mPackages") - private void processRestoredPermissionGrantsLPr(XmlPullParser parser, int userId) - throws XmlPullParserException, IOException { - String pkgName = null; - int outerDepth = parser.getDepth(); - int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue; - } - - final String tagName = parser.getName(); - if (tagName.equals(TAG_GRANT)) { - pkgName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); - if (DEBUG_BACKUP) { - Slog.v(TAG, "+++ Restoring grants for package " + pkgName); - } - } else if (tagName.equals(TAG_PERMISSION)) { - - final boolean isGranted = "true".equals(parser.getAttributeValue(null, ATTR_IS_GRANTED)); - final String permName = parser.getAttributeValue(null, ATTR_PERMISSION_NAME); - - int newFlagSet = 0; - if ("true".equals(parser.getAttributeValue(null, ATTR_USER_SET))) { - newFlagSet |= FLAG_PERMISSION_USER_SET; - } - if ("true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED))) { - newFlagSet |= FLAG_PERMISSION_USER_FIXED; - } - if ("true".equals(parser.getAttributeValue(null, ATTR_REVOKE_ON_UPGRADE))) { - newFlagSet |= FLAG_PERMISSION_REVOKE_ON_UPGRADE; - } - if (DEBUG_BACKUP) { - Slog.v(TAG, " + Restoring grant:" - + " pkg=" + pkgName - + " perm=" + permName - + " granted=" + isGranted - + " bits=0x" + Integer.toHexString(newFlagSet)); - } - final PackageSetting ps = mSettings.mPackages.get(pkgName); - if (ps != null) { - // Already installed so we apply the grant immediately - if (DEBUG_BACKUP) { - Slog.v(TAG, " + already installed; applying"); - } - PermissionsState perms = ps.getPermissionsState(); - BasePermission bp = - (BasePermission) mPermissionManager.getPermissionTEMP(permName); - if (bp != null) { - if (isGranted) { - perms.grantRuntimePermission(bp, userId); - } - if (newFlagSet != 0) { - perms.updatePermissionFlags( - bp, userId, USER_RUNTIME_GRANT_MASK, newFlagSet); - } - } - } else { - // Need to wait for post-restore install to apply the grant - if (DEBUG_BACKUP) { - Slog.v(TAG, " - not yet installed; saving for later"); - } - mSettings.processRestoredPermissionGrantLPr(pkgName, permName, - isGranted, newFlagSet, userId); - } - } else { - PackageManagerService.reportSettingsProblem(Log.WARN, - "Unknown element under <" + TAG_PERMISSION_BACKUP + ">: " + tagName); - XmlUtils.skipCurrentTag(parser); - } - } - - scheduleWriteSettingsLocked(); - mSettings.writeRuntimePermissionsForUserLPr(userId, false); - } - - @Override public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage, int sourceUserId, int targetUserId, int flags) { mContext.enforceCallingOrSelfPermission( @@ -19928,19 +19822,59 @@ public class PackageManagerService extends IPackageManager.Stub ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates, int userId) { Intent intent = getHomeIntent(); - List<ResolveInfo> list = queryIntentActivitiesInternal(intent, null, + List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null, PackageManager.GET_META_DATA, userId); - ResolveInfo preferred = findPreferredActivity(intent, null, 0, list, 0, - true, false, false, userId); - allHomeCandidates.clear(); - if (list != null) { - allHomeCandidates.addAll(list); + if (resolveInfos == null) { + return null; } - return (preferred == null || preferred.activityInfo == null) - ? null - : new ComponentName(preferred.activityInfo.packageName, - preferred.activityInfo.name); + allHomeCandidates.addAll(resolveInfos); + + PackageManagerInternal.DefaultHomeProvider provider; + synchronized (mPackages) { + provider = mDefaultHomeProvider; + } + if (provider == null) { + Slog.e(TAG, "mDefaultHomeProvider is null"); + return null; + } + String packageName = provider.getDefaultHome(userId); + if (packageName == null) { + return null; + } + int resolveInfosSize = resolveInfos.size(); + for (int i = 0; i < resolveInfosSize; i++) { + ResolveInfo resolveInfo = resolveInfos.get(i); + + if (resolveInfo.activityInfo != null && TextUtils.equals( + resolveInfo.activityInfo.packageName, packageName)) { + return new ComponentName(resolveInfo.activityInfo.packageName, + resolveInfo.activityInfo.name); + } + } + return null; + } + + private void updateDefaultHomeLPw(int userId) { + Intent intent = getHomeIntent(); + List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null, + PackageManager.GET_META_DATA, userId); + ResolveInfo preferredResolveInfo = findPreferredActivity(intent, null, 0, resolveInfos, + 0, true, false, false, userId); + String packageName = preferredResolveInfo != null + && preferredResolveInfo.activityInfo != null + ? preferredResolveInfo.activityInfo.packageName : null; + String currentPackageName = mDefaultHomeProvider.getDefaultHome(userId); + if (TextUtils.equals(currentPackageName, packageName)) { + return; + } + String[] callingPackages = getPackagesForUid(Binder.getCallingUid()); + if (callingPackages != null && ArrayUtils.contains(callingPackages, + mRequiredPermissionControllerPackage)) { + // PermissionController manages default home directly. + return; + } + mDefaultHomeProvider.setDefaultHomeAsync(packageName, userId); } @Override @@ -21214,10 +21148,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS) && packageName == null) { - mSettings.dumpRestoredPermissionGrantsLPr(pw, dumpState); - } - if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) { // XXX should handle packageName != null by dumping only install data that // the given package is involved with. @@ -23830,6 +23760,13 @@ public class PackageManagerService extends IPackageManager.Stub mDefaultBrowserProvider = provider; } } + + @Override + public void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider) { + synchronized (mPackages) { + mDefaultHomeProvider = provider; + } + } } @GuardedBy("mPackages") diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 975ffb25a784..92fe377e9495 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -19,10 +19,6 @@ package com.android.server.pm; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; -import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE; -import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; -import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED; -import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET; import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; @@ -249,23 +245,6 @@ public final class Settings { private static final String ATTR_SDK_VERSION = "sdkVersion"; private static final String ATTR_DATABASE_VERSION = "databaseVersion"; - // Bookkeeping for restored permission grants - private static final String TAG_RESTORED_RUNTIME_PERMISSIONS = "restored-perms"; - // package name: ATTR_PACKAGE_NAME - private static final String TAG_PERMISSION_ENTRY = "perm"; - // permission name: ATTR_NAME - // permission granted (boolean): ATTR_GRANTED - private static final String ATTR_USER_SET = "set"; - private static final String ATTR_USER_FIXED = "fixed"; - private static final String ATTR_REVOKE_ON_UPGRADE = "rou"; - private static final String ATTR_REVOKE_WHEN_REQUESTED = "rwr"; - - // Flag mask of restored permission grants that are applied at install time - private static final int USER_RUNTIME_GRANT_MASK = - FLAG_PERMISSION_USER_SET - | FLAG_PERMISSION_USER_FIXED - | FLAG_PERMISSION_REVOKE_ON_UPGRADE; - private final Object mLock; private final RuntimePermissionPersistence mRuntimePermissionsPersistence; @@ -303,26 +282,6 @@ public final class Settings { int[] excludedUserIds; } - // Bookkeeping for restored user permission grants - final class RestoredPermissionGrant { - String permissionName; - boolean granted; - int grantBits; - - RestoredPermissionGrant(String name, boolean isGranted, int theGrantBits) { - permissionName = name; - granted = isGranted; - grantBits = theGrantBits; - } - } - - // This would be more compact as a flat array of restored grants or something, but we - // may have quite a few, especially during early device lifetime, and avoiding all those - // linear lookups will be important. - private final SparseArray<ArrayMap<String, ArraySet<RestoredPermissionGrant>>> - mRestoredUserGrants = - new SparseArray<ArrayMap<String, ArraySet<RestoredPermissionGrant>>>(); - private static int mFirstAvailableUid = 0; /** Map from volume UUID to {@link VersionInfo} */ @@ -461,43 +420,6 @@ public final class Settings { return mRenamedPackages.put(pkgName, origPkgName); } - void applyPendingPermissionGrantsLPw(String packageName, int userId) { - ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage = - mRestoredUserGrants.get(userId); - if (grantsByPackage == null || grantsByPackage.size() == 0) { - return; - } - - ArraySet<RestoredPermissionGrant> grants = grantsByPackage.get(packageName); - if (grants == null || grants.size() == 0) { - return; - } - - final PackageSetting ps = mPackages.get(packageName); - if (ps == null) { - Slog.e(TAG, "Can't find supposedly installed package " + packageName); - return; - } - final PermissionsState perms = ps.getPermissionsState(); - - for (RestoredPermissionGrant grant : grants) { - BasePermission bp = mPermissions.getPermission(grant.permissionName); - if (bp != null) { - if (grant.granted) { - perms.grantRuntimePermission(bp, userId); - } - perms.updatePermissionFlags(bp, userId, USER_RUNTIME_GRANT_MASK, grant.grantBits); - } - } - - // And remove it from the pending-grant bookkeeping - grantsByPackage.remove(packageName); - if (grantsByPackage.size() < 1) { - mRestoredUserGrants.remove(userId); - } - writeRuntimePermissionsForUserLPr(userId, false); - } - public boolean canPropagatePermissionToInstantApp(String permName) { return mPermissions.canPropagatePermissionToInstantApp(permName); } @@ -1982,13 +1904,6 @@ public final class Settings { } } - // Specifically for backup/restore - public void processRestoredPermissionGrantLPr(String pkgName, String permission, - boolean isGranted, int restoredFlagSet, int userId) { - mRuntimePermissionsPersistence.rememberRestoredUserGrantLPr( - pkgName, permission, isGranted, restoredFlagSet, userId); - } - void writeDefaultAppsLPr(XmlSerializer serializer, int userId) throws IllegalArgumentException, IllegalStateException, IOException { serializer.startTag(null, TAG_DEFAULT_APPS); @@ -5014,51 +4929,6 @@ public final class Settings { pw.print(mReadMessages.toString()); } - void dumpRestoredPermissionGrantsLPr(PrintWriter pw, DumpState dumpState) { - if (mRestoredUserGrants.size() > 0) { - pw.println(); - pw.println("Restored (pending) permission grants:"); - for (int userIndex = 0; userIndex < mRestoredUserGrants.size(); userIndex++) { - ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage = - mRestoredUserGrants.valueAt(userIndex); - if (grantsByPackage != null && grantsByPackage.size() > 0) { - final int userId = mRestoredUserGrants.keyAt(userIndex); - pw.print(" User "); pw.println(userId); - - for (int pkgIndex = 0; pkgIndex < grantsByPackage.size(); pkgIndex++) { - ArraySet<RestoredPermissionGrant> grants = grantsByPackage.valueAt(pkgIndex); - if (grants != null && grants.size() > 0) { - final String pkgName = grantsByPackage.keyAt(pkgIndex); - pw.print(" "); pw.print(pkgName); pw.println(" :"); - - for (RestoredPermissionGrant g : grants) { - pw.print(" "); - pw.print(g.permissionName); - if (g.granted) { - pw.print(" GRANTED"); - } - if ((g.grantBits&FLAG_PERMISSION_USER_SET) != 0) { - pw.print(" user_set"); - } - if ((g.grantBits&FLAG_PERMISSION_USER_FIXED) != 0) { - pw.print(" user_fixed"); - } - if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) { - pw.print(" revoke_on_upgrade"); - } - if ((g.grantBits & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) { - pw.print(" revoke_when_requested"); - } - pw.println(); - } - } - } - } - } - pw.println(); - } - } - private static void dumpSplitNames(PrintWriter pw, PackageParser.Package pkg) { if (pkg == null) { pw.print("unknown"); @@ -5328,55 +5198,6 @@ public final class Settings { serializer.endTag(null, TAG_RUNTIME_PERMISSIONS); - // Now any restored permission grants that are waiting for the apps - // in question to be installed. These are stored as per-package - // TAG_RESTORED_RUNTIME_PERMISSIONS blocks, each containing some - // number of individual permission grant entities. - if (mRestoredUserGrants.get(userId) != null) { - ArrayMap<String, ArraySet<RestoredPermissionGrant>> restoredGrants = - mRestoredUserGrants.get(userId); - if (restoredGrants != null) { - final int pkgCount = restoredGrants.size(); - for (int i = 0; i < pkgCount; i++) { - final ArraySet<RestoredPermissionGrant> pkgGrants = - restoredGrants.valueAt(i); - if (pkgGrants != null && pkgGrants.size() > 0) { - final String pkgName = restoredGrants.keyAt(i); - serializer.startTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS); - serializer.attribute(null, ATTR_PACKAGE_NAME, pkgName); - - final int N = pkgGrants.size(); - for (int z = 0; z < N; z++) { - RestoredPermissionGrant g = pkgGrants.valueAt(z); - serializer.startTag(null, TAG_PERMISSION_ENTRY); - serializer.attribute(null, ATTR_NAME, g.permissionName); - - if (g.granted) { - serializer.attribute(null, ATTR_GRANTED, "true"); - } - - if ((g.grantBits&FLAG_PERMISSION_USER_SET) != 0) { - serializer.attribute(null, ATTR_USER_SET, "true"); - } - if ((g.grantBits&FLAG_PERMISSION_USER_FIXED) != 0) { - serializer.attribute(null, ATTR_USER_FIXED, "true"); - } - if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) { - serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true"); - } - if ((g.grantBits & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) - != 0) { - serializer.attribute(null, ATTR_REVOKE_WHEN_REQUESTED, - "true"); - } - serializer.endTag(null, TAG_PERMISSION_ENTRY); - } - serializer.endTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS); - } - } - } - } - serializer.endDocument(); destination.finishWrite(out); @@ -5455,29 +5276,6 @@ public final class Settings { } } - // Backup/restore support - - public void rememberRestoredUserGrantLPr(String pkgName, String permission, - boolean isGranted, int restoredFlagSet, int userId) { - // This change will be remembered at write-settings time - ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage = - mRestoredUserGrants.get(userId); - if (grantsByPackage == null) { - grantsByPackage = new ArrayMap<String, ArraySet<RestoredPermissionGrant>>(); - mRestoredUserGrants.put(userId, grantsByPackage); - } - - ArraySet<RestoredPermissionGrant> grants = grantsByPackage.get(pkgName); - if (grants == null) { - grants = new ArraySet<RestoredPermissionGrant>(); - grantsByPackage.put(pkgName, grants); - } - - RestoredPermissionGrant grant = new RestoredPermissionGrant(permission, - isGranted, restoredFlagSet); - grants.add(grant); - } - // Private internals @GuardedBy("Settings.this.mLock") @@ -5520,50 +5318,6 @@ public final class Settings { } parsePermissionsLPr(parser, sus.getPermissionsState(), userId); } break; - - case TAG_RESTORED_RUNTIME_PERMISSIONS: { - final String pkgName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); - parseRestoredRuntimePermissionsLPr(parser, pkgName, userId); - } break; - } - } - } - - private void parseRestoredRuntimePermissionsLPr(XmlPullParser parser, - final String pkgName, final int userId) throws IOException, XmlPullParserException { - final int outerDepth = parser.getDepth(); - int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue; - } - - switch (parser.getName()) { - case TAG_PERMISSION_ENTRY: { - final String permName = parser.getAttributeValue(null, ATTR_NAME); - final boolean isGranted = "true".equals( - parser.getAttributeValue(null, ATTR_GRANTED)); - - int permBits = 0; - if ("true".equals(parser.getAttributeValue(null, ATTR_USER_SET))) { - permBits |= FLAG_PERMISSION_USER_SET; - } - if ("true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED))) { - permBits |= FLAG_PERMISSION_USER_FIXED; - } - if ("true".equals(parser.getAttributeValue(null, ATTR_REVOKE_ON_UPGRADE))) { - permBits |= FLAG_PERMISSION_REVOKE_ON_UPGRADE; - } - if ("true".equals(parser.getAttributeValue(null, - ATTR_REVOKE_WHEN_REQUESTED))) { - permBits |= FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; - } - - if (isGranted || permBits != 0) { - rememberRestoredUserGrantLPr(pkgName, permName, isGranted, permBits, userId); - } - } break; } } } diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 30c2281b07f1..6c212d63d77c 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -156,12 +156,8 @@ public class StagingManager { boolean success = true; // STOPSHIP: TODO(b/123753157): Verify APKs through Package Verifier. - if (!sessionContainsApex(session)) { - // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs, - // right away. - session.setStagedSessionReady(); - return; - } + // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs, + // right away. final ApexInfoList apexInfoList = new ApexInfoList(); // APEX checks. For single-package sessions, check if they contain an APEX. For @@ -227,7 +223,8 @@ public class StagingManager { } session.setStagedSessionReady(); - if (!mApexManager.markStagedSessionReady(session.sessionId)) { + if (sessionContainsApex(session) + && !mApexManager.markStagedSessionReady(session.sessionId)) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, "APEX staging failed, check logcat messages from apexd for more " + "details."); diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 38940d6241a6..f56b98464cc3 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -30,6 +30,7 @@ import static android.app.AppOpsManager.permissionToOpCode; import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; +import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE; import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED; @@ -43,6 +44,9 @@ import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING import static com.android.server.pm.PackageManagerService.DEBUG_PERMISSIONS; import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; +import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE; + +import static java.util.concurrent.TimeUnit.SECONDS; import android.Manifest; import android.annotation.NonNull; @@ -69,7 +73,9 @@ import android.os.UserManager; import android.os.UserManagerInternal; import android.os.storage.StorageManager; import android.os.storage.StorageManagerInternal; +import android.permission.PermissionControllerManager; import android.permission.PermissionManager; +import android.permission.PermissionManagerInternal; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -77,6 +83,7 @@ import android.util.EventLog; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; @@ -91,9 +98,8 @@ import com.android.server.pm.PackageManagerServiceUtils; import com.android.server.pm.PackageSetting; import com.android.server.pm.SharedUserSetting; import com.android.server.pm.UserManagerService; -import com.android.server.pm.permission.DefaultPermissionGrantPolicy - .DefaultPermissionGrantedCallback; -import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback; +import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback; +import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback; import com.android.server.pm.permission.PermissionsState.PermissionState; import libcore.util.EmptyArray; @@ -106,6 +112,10 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * Manages all permissions and handles permissions related tasks. @@ -122,6 +132,8 @@ public class PermissionManagerService { /** Permission grant: grant as runtime a permission that was granted as an install time one. */ private static final int GRANT_UPGRADE = 4; + private static final long BACKUP_TIMEOUT_MILLIS = SECONDS.toMillis(60); + /** Cap the size of permission trees that 3rd party apps can define; in characters of text */ private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768; /** Empty array to avoid allocations */ @@ -146,6 +158,9 @@ public class PermissionManagerService { /** Internal connection to the user manager */ private final UserManagerInternal mUserManagerInt; + /** Permission controller: User space permission management */ + private PermissionControllerManager mPermissionControllerManager; + /** Default permission policy to provide proper behaviour out-of-the-box */ private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy; @@ -180,6 +195,16 @@ public class PermissionManagerService { @GuardedBy("mLock") private ArrayMap<String, List<String>> mBackgroundPermissions; + /** + * A permission backup might contain apps that are not installed. In this case we delay the + * restoration until the app is installed. + * + * <p>This array ({@code userId -> noDelayedBackupLeft}) is {@code true} for all the users where + * there is <u>no more</u> delayed backup left. + */ + @GuardedBy("mLock") + private final SparseBooleanArray mHasNoDelayedPermBackup = new SparseBooleanArray(); + PermissionManagerService(Context context, @Nullable DefaultPermissionGrantedCallback defaultGrantCallback, @NonNull Object externalLock) { @@ -218,29 +243,31 @@ public class PermissionManagerService { } } - LocalServices.addService( - PermissionManagerInternal.class, new PermissionManagerInternalImpl()); + PermissionManagerServiceInternalImpl localService = + new PermissionManagerServiceInternalImpl(); + LocalServices.addService(PermissionManagerServiceInternal.class, localService); + LocalServices.addService(PermissionManagerInternal.class, localService); } /** * Creates and returns an initialized, internal service for use by other components. * <p> * The object returned is identical to the one returned by the LocalServices class using: - * {@code LocalServices.getService(PermissionManagerInternal.class);} + * {@code LocalServices.getService(PermissionManagerServiceInternal.class);} * <p> * NOTE: The external lock is temporary and should be removed. This needs to be a * lock created by the permission manager itself. */ - public static PermissionManagerInternal create(Context context, + public static PermissionManagerServiceInternal create(Context context, @Nullable DefaultPermissionGrantedCallback defaultGrantCallback, @NonNull Object externalLock) { - final PermissionManagerInternal permMgrInt = - LocalServices.getService(PermissionManagerInternal.class); + final PermissionManagerServiceInternal permMgrInt = + LocalServices.getService(PermissionManagerServiceInternal.class); if (permMgrInt != null) { return permMgrInt; } new PermissionManagerService(context, defaultGrantCallback, externalLock); - return LocalServices.getService(PermissionManagerInternal.class); + return LocalServices.getService(PermissionManagerServiceInternal.class); } @Nullable BasePermission getPermission(String permName) { @@ -332,6 +359,74 @@ public class PermissionManagerService { } /** + * Get the state of the runtime permissions as xml file. + * + * <p>Can not be called on main thread. + * + * @param user The user the data should be extracted for + * + * @return The state as a xml file + */ + private @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user) { + CompletableFuture<byte[]> backup = new CompletableFuture<>(); + mPermissionControllerManager.getRuntimePermissionBackup(user, mContext.getMainExecutor(), + backup::complete); + + try { + return backup.get(BACKUP_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + Slog.e(TAG, "Cannot create permission backup for " + user, e); + return null; + } + } + + /** + * Restore a permission state previously backed up via {@link #backupRuntimePermissions}. + * + * <p>If not all state can be restored, the un-appliable state will be delayed and can be + * applied via {@link #restoreDelayedRuntimePermissions}. + * + * @param backup The state as an xml file + * @param user The user the data should be restored for + */ + private void restoreRuntimePermissions(@NonNull byte[] backup, @NonNull UserHandle user) { + synchronized (mLock) { + mHasNoDelayedPermBackup.delete(user.getIdentifier()); + mPermissionControllerManager.restoreRuntimePermissionBackup(backup, user); + } + } + + /** + * Try to apply permission backup that was previously not applied. + * + * <p>Can not be called on main thread. + * + * @param packageName The package that is newly installed + * @param user The user the package is installed for + * + * @see #restoreRuntimePermissions + */ + private void restoreDelayedRuntimePermissions(@NonNull String packageName, + @NonNull UserHandle user) { + synchronized (mLock) { + if (mHasNoDelayedPermBackup.get(user.getIdentifier(), false)) { + return; + } + + mPermissionControllerManager.restoreDelayedRuntimePermissionBackup(packageName, user, + mContext.getMainExecutor(), (hasMoreBackup) -> { + if (hasMoreBackup) { + return; + } + + synchronized (mLock) { + mHasNoDelayedPermBackup.put(user.getIdentifier(), true); + } + }); + } + } + + /** * Returns {@code true} if the permission can be implied from another granted permission. * <p>Some permissions, such as ACCESS_FINE_LOCATION, imply other permissions, * such as ACCESS_COURSE_LOCATION. If the caller holds an umbrella permission, give @@ -741,7 +836,6 @@ public class PermissionManagerService { if (ps == null) { return; } - final boolean isLegacySystemApp = mPackageManagerInt.isLegacySystemApp(pkg); final PermissionsState permissionsState = ps.getPermissionsState(); PermissionsState origPermissions = permissionsState; @@ -828,17 +922,9 @@ public class PermissionManagerService { // For all apps normal permissions are install time ones. grant = GRANT_INSTALL; } else if (bp.isRuntime()) { - // If a permission review is required for legacy apps we represent - // their permissions as always granted runtime ones since we need - // to keep the review required permission flag per user while an - // install permission's state is shared across all users. if (origPermissions.hasInstallPermission(bp.getName())) { - // For legacy apps that became modern, install becomes runtime. - grant = GRANT_UPGRADE; - } else if (isLegacySystemApp) { - // For legacy system apps, install becomes runtime. - // We cannot check hasInstallPermission() for system apps since those - // permissions were granted implicitly and not persisted pre-M. + // Before Q we represented some runtime permissions as install permissions, + // in Q we cannot do this anymore. Hence upgrade them all. grant = GRANT_UPGRADE; } else { // For modern apps keep runtime permissions unchanged. @@ -891,110 +977,111 @@ public class PermissionManagerService { } // Grant an install permission. if (permissionsState.grantInstallPermission(bp) != - PermissionsState.PERMISSION_OPERATION_FAILURE) { + PERMISSION_OPERATION_FAILURE) { changedInstallPermission = true; } } break; case GRANT_RUNTIME: { - // Grant previously granted runtime permissions. - for (int userId : UserManagerService.getInstance().getUserIds()) { - final PermissionState permissionState = origPermissions + for (int userId : currentUserIds) { + PermissionState permState = origPermissions .getRuntimePermissionState(perm, userId); - int flags = permissionState != null - ? permissionState.getFlags() : 0; - if (origPermissions.hasRuntimePermission(perm, userId)) { - // Don't propagate the permission in a permission review - // mode if the former was revoked, i.e. marked to not - // propagate on upgrade. Note that in a permission review - // mode install permissions are represented as constantly - // granted runtime ones since we need to keep a per user - // state associated with the permission. Also the revoke - // on upgrade flag is no longer applicable and is reset. - final boolean revokeOnUpgrade = (flags & PackageManager - .FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0; - if (revokeOnUpgrade) { - flags &= ~PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE; - // Since we changed the flags, we have to write. - updatedUserIds = ArrayUtils.appendInt( - updatedUserIds, userId); - } - if (!revokeOnUpgrade) { - if (permissionsState.grantRuntimePermission(bp, userId) == - PermissionsState.PERMISSION_OPERATION_FAILURE) { - // If we cannot put the permission as it was, - // we have to write. - updatedUserIds = ArrayUtils.appendInt( - updatedUserIds, userId); - } + int flags = permState != null ? permState.getFlags() : 0; + + boolean wasChanged = false; + + if (appSupportsRuntimePermissions) { + // Remove review flag as it is not necessary anymore + if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { + flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED; + wasChanged = true; } - // If the app supports runtime permissions no need for a review. - if (appSupportsRuntimePermissions - && (flags & PackageManager - .FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { - flags &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; - // Since we changed the flags, we have to write. - updatedUserIds = ArrayUtils.appendInt( - updatedUserIds, userId); + if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) { + flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE; + wasChanged = true; + } else { + if (permState != null && permState.isGranted()) { + if (permissionsState.grantRuntimePermission(bp, userId) + == PERMISSION_OPERATION_FAILURE) { + wasChanged = true; + } + } } - } else if (!appSupportsRuntimePermissions) { - // For legacy apps that need a permission review, every new - // runtime permission is granted but it is pending a review. - // We also need to review only platform defined runtime - // permissions as these are the only ones the platform knows - // how to disable the API to simulate revocation as legacy - // apps don't expect to run with revoked permissions. - if (PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName())) { - if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0 - && !bp.isRemoved()) { - flags |= FLAG_PERMISSION_REVIEW_REQUIRED; - // We changed the flags, hence have to write. - updatedUserIds = ArrayUtils.appendInt( - updatedUserIds, userId); + } else { + if (permState == null) { + // New permission + if (PLATFORM_PACKAGE_NAME.equals( + bp.getSourcePackageName())) { + if (!bp.isRemoved()) { + flags |= FLAG_PERMISSION_REVIEW_REQUIRED + | FLAG_PERMISSION_REVOKE_ON_UPGRADE; + wasChanged = true; + } } } + if (permissionsState.grantRuntimePermission(bp, userId) - != PermissionsState.PERMISSION_OPERATION_FAILURE) { - // We changed the permission, hence have to write. - updatedUserIds = ArrayUtils.appendInt( - updatedUserIds, userId); + != PERMISSION_OPERATION_FAILURE) { + wasChanged = true; } } - // Propagate the permission flags. + + if (wasChanged) { + updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); + } + permissionsState.updatePermissionFlags(bp, userId, flags, flags); } } break; case GRANT_UPGRADE: { - // Grant runtime permissions for a previously held install permission. - final PermissionState permissionState = origPermissions + // Upgrade from Pre-Q to Q permission model. Make all permissions + // runtime + PermissionState permState = origPermissions .getInstallPermissionState(perm); - final int flags = - (permissionState != null) ? permissionState.getFlags() : 0; + int flags = (permState != null) ? permState.getFlags() : 0; + // Remove install permission if (origPermissions.revokeInstallPermission(bp) - != PermissionsState.PERMISSION_OPERATION_FAILURE) { - // We will be transferring the permission flags, so clear them. + != PERMISSION_OPERATION_FAILURE) { origPermissions.updatePermissionFlags(bp, UserHandle.USER_ALL, PackageManager.MASK_PERMISSION_FLAGS, 0); changedInstallPermission = true; } - // If the permission is not to be promoted to runtime we ignore it and - // also its other flags as they are not applicable to install permissions. - if ((flags & PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE) == 0) { - for (int userId : currentUserIds) { + for (int userId : currentUserIds) { + boolean wasChanged = false; + + if (appSupportsRuntimePermissions) { + // Remove review flag as it is not necessary anymore + if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { + flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED; + wasChanged = true; + } + + if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) { + flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE; + wasChanged = true; + } else { + if (permissionsState.grantRuntimePermission(bp, userId) != + PERMISSION_OPERATION_FAILURE) { + wasChanged = true; + } + } + } else { if (permissionsState.grantRuntimePermission(bp, userId) != - PermissionsState.PERMISSION_OPERATION_FAILURE) { - // Transfer the permission flags. - permissionsState.updatePermissionFlags(bp, userId, - flags, flags); - // If we granted the permission, we have to write. - updatedUserIds = ArrayUtils.appendInt( - updatedUserIds, userId); + PERMISSION_OPERATION_FAILURE) { + flags |= FLAG_PERMISSION_REVIEW_REQUIRED; + wasChanged = true; } } + + if (wasChanged) { + updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); + } + + permissionsState.updatePermissionFlags(bp, userId, flags, flags); } } break; @@ -1011,7 +1098,7 @@ public class PermissionManagerService { } } else { if (permissionsState.revokeInstallPermission(bp) != - PermissionsState.PERMISSION_OPERATION_FAILURE) { + PERMISSION_OPERATION_FAILURE) { // Also drop the permission flags. permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, PackageManager.MASK_PERMISSION_FLAGS, 0); @@ -1094,6 +1181,10 @@ public class PermissionManagerService { @NonNull int[] updatedUserIds) { AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class); + if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { + return updatedUserIds; + } + String pkgName = pkg.packageName; int[] users = UserManagerService.getInstance().getUserIds(); @@ -1119,26 +1210,14 @@ public class PermissionManagerService { if ((flags & (FLAG_PERMISSION_GRANTED_BY_DEFAULT | FLAG_PERMISSION_POLICY_FIXED | FLAG_PERMISSION_SYSTEM_FIXED)) == 0) { - if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { - if (permissionToOpCode(permission) != OP_NONE) { - setAppOpMode(permission, pkg, userId, MODE_IGNORED); - - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, "Revoking app-op " - + permissionToOp(permission) + " for " + pkgName - + " as it is now requested"); - } - } - } else { - int revokeResult = ps.revokeRuntimePermission(bp, userId); - if (revokeResult - != PermissionsState.PERMISSION_OPERATION_FAILURE) { - - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, "Revoking runtime permission " + permission - + " for " + pkgName - + " as it is now requested"); - } + int revokeResult = ps.revokeRuntimePermission(bp, userId); + if (revokeResult + != PERMISSION_OPERATION_FAILURE) { + + if (DEBUG_PERMISSIONS) { + Slog.i(TAG, "Revoking runtime permission " + permission + + " for " + pkgName + + " as it is now requested"); } } @@ -1925,7 +2004,7 @@ public class PermissionManagerService { // Development permissions must be handled specially, since they are not // normal runtime permissions. For now they apply to all users. if (permissionsState.grantInstallPermission(bp) != - PermissionsState.PERMISSION_OPERATION_FAILURE) { + PERMISSION_OPERATION_FAILURE) { if (callback != null) { callback.onInstallPermissionGranted(); } @@ -1945,7 +2024,7 @@ public class PermissionManagerService { final int result = permissionsState.grantRuntimePermission(bp, userId); switch (result) { - case PermissionsState.PERMISSION_OPERATION_FAILURE: { + case PERMISSION_OPERATION_FAILURE: { return; } @@ -2045,7 +2124,7 @@ public class PermissionManagerService { // Development permissions must be handled specially, since they are not // normal runtime permissions. For now they apply to all users. if (permissionsState.revokeInstallPermission(bp) != - PermissionsState.PERMISSION_OPERATION_FAILURE) { + PERMISSION_OPERATION_FAILURE) { if (callback != null) { callback.onInstallPermissionRevoked(); } @@ -2054,7 +2133,7 @@ public class PermissionManagerService { } if (permissionsState.revokeRuntimePermission(bp, userId) == - PermissionsState.PERMISSION_OPERATION_FAILURE) { + PERMISSION_OPERATION_FAILURE) { return; } @@ -2522,6 +2601,8 @@ public class PermissionManagerService { throw new IllegalStateException("Signature|privileged permissions not in " + "privapp-permissions whitelist: " + mPrivappPermissionsViolations); } + + mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class); } private static String getVolumeUuidForPackage(PackageParser.Package pkg) { @@ -2574,7 +2655,7 @@ public class PermissionManagerService { return mBackgroundPermissions; } - private class PermissionManagerInternalImpl extends PermissionManagerInternal { + private class PermissionManagerServiceInternalImpl extends PermissionManagerServiceInternal { @Override public void systemReady() { PermissionManagerService.this.systemReady(); @@ -2737,5 +2818,21 @@ public class PermissionManagerService { return mSettings.getPermissionLocked(permName); } } + + @Override + public @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user) { + return PermissionManagerService.this.backupRuntimePermissions(user); + } + + @Override + public void restoreRuntimePermissions(@NonNull byte[] backup, @NonNull UserHandle user) { + PermissionManagerService.this.restoreRuntimePermissions(backup, user); + } + + @Override + public void restoreDelayedRuntimePermissions(@NonNull String packageName, + @NonNull UserHandle user) { + PermissionManagerService.this.restoreDelayedRuntimePermissions(packageName, user); + } } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index f4979746bae3..1dd2408686c1 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,19 +18,22 @@ package com.android.server.pm.permission; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.pm.PackageManager.PermissionInfoFlags; import android.content.pm.PackageParser; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; -import android.content.pm.PackageManager.PermissionInfoFlags; +import android.permission.PermissionManagerInternal; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** - * Internal interfaces to be used by other components within the system server. + * Internal interfaces services. + * + * TODO: Should be merged into PermissionManagerInternal, but currently uses internal classes. */ -public abstract class PermissionManagerInternal { +public abstract class PermissionManagerServiceInternal extends PermissionManagerInternal { /** * Callbacks invoked when interesting actions have been taken on a permission. * <p> diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING index 0892b32a6b91..2280d3fd9134 100644 --- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING @@ -16,6 +16,9 @@ }, { "include-filter": "android.permission.cts.SplitPermissionTest" + }, + { + "include-filter": "android.permission.cts.PermissionFlagsTest" } ] }, diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java index fdcafa77a378..0c6b773396c8 100644 --- a/services/core/java/com/android/server/policy/DisplayFoldController.java +++ b/services/core/java/com/android/server/policy/DisplayFoldController.java @@ -42,10 +42,12 @@ class DisplayFoldController { private final WindowManagerInternal mWindowManagerInternal; private final DisplayManagerInternal mDisplayManagerInternal; private final int mDisplayId; + private final Handler mHandler; /** The display area while device is folded. */ private final Rect mFoldedArea; - private final Handler mHandler; + /** The display area to override the original folded area. */ + private Rect mOverrideFoldedArea = new Rect(); private final DisplayInfo mNonOverrideDisplayInfo = new DisplayInfo(); private final RemoteCallbackList<IDisplayFoldListener> mListeners = new RemoteCallbackList<>(); @@ -70,14 +72,23 @@ class DisplayFoldController { return; } if (folded) { + Rect foldedArea; + if (!mOverrideFoldedArea.isEmpty()) { + foldedArea = mOverrideFoldedArea; + } else if (!mFoldedArea.isEmpty()) { + foldedArea = mFoldedArea; + } else { + return; + } + mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mNonOverrideDisplayInfo); - final int dx = (mNonOverrideDisplayInfo.logicalWidth - mFoldedArea.width()) / 2 - - mFoldedArea.left; - final int dy = (mNonOverrideDisplayInfo.logicalHeight - mFoldedArea.height()) / 2 - - mFoldedArea.top; + final int dx = (mNonOverrideDisplayInfo.logicalWidth - foldedArea.width()) / 2 + - foldedArea.left; + final int dy = (mNonOverrideDisplayInfo.logicalHeight - foldedArea.height()) / 2 + - foldedArea.top; - mWindowManagerInternal.setForcedDisplaySize(mDisplayId, mFoldedArea.width(), - mFoldedArea.height()); + mWindowManagerInternal.setForcedDisplaySize(mDisplayId, + foldedArea.width(), foldedArea.height()); mDisplayManagerInternal.setDisplayOffsets(mDisplayId, -dx, -dy); } else { mWindowManagerInternal.clearForcedDisplaySize(mDisplayId); @@ -114,6 +125,18 @@ class DisplayFoldController { mListeners.unregister(listener); } + void setOverrideFoldedArea(Rect area) { + mOverrideFoldedArea.set(area); + } + + Rect getFoldedArea() { + if (!mOverrideFoldedArea.isEmpty()) { + return mOverrideFoldedArea; + } else { + return mFoldedArea; + } + } + /** * Only used for the case that persist.debug.force_foldable is set. * This is using proximity sensor to simulate the fold state switch. @@ -125,7 +148,7 @@ class DisplayFoldController { return null; } - final DisplayFoldController result = create(displayId); + final DisplayFoldController result = create(context, displayId); sensorManager.registerListener(new SensorEventListener() { @Override public void onSensorChanged(SensorEvent event) { @@ -141,13 +164,17 @@ class DisplayFoldController { return result; } - static DisplayFoldController create(int displayId) { + static DisplayFoldController create(Context context, int displayId) { final DisplayManagerInternal displayService = LocalServices.getService(DisplayManagerInternal.class); - final DisplayInfo displayInfo = new DisplayInfo(); - displayService.getNonOverrideDisplayInfo(displayId, displayInfo); - final Rect foldedArea = new Rect(0, displayInfo.logicalHeight / 2, - displayInfo.logicalWidth, displayInfo.logicalHeight); + final String configFoldedArea = context.getResources().getString( + com.android.internal.R.string.config_foldedArea); + final Rect foldedArea; + if (configFoldedArea == null || configFoldedArea.isEmpty()) { + foldedArea = new Rect(); + } else { + foldedArea = Rect.unflattenFromString(configFoldedArea); + } return new DisplayFoldController(LocalServices.getService(WindowManagerInternal.class), displayService, displayId, foldedArea, DisplayThread.getHandler()); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 2e3e3e430839..c87a81db16e4 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -125,6 +125,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.database.ContentObserver; import android.graphics.PixelFormat; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManager; import android.hardware.hdmi.HdmiAudioSystemClient; @@ -1858,7 +1859,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { readConfigurationDependentBehaviors(); if (mLidControlsDisplayFold) { - mDisplayFoldController = DisplayFoldController.create(DEFAULT_DISPLAY); + mDisplayFoldController = DisplayFoldController.create(context, DEFAULT_DISPLAY); } else if (SystemProperties.getBoolean("persist.debug.force_foldable", false)) { mDisplayFoldController = DisplayFoldController.createWithProxSensor(context, DEFAULT_DISPLAY); @@ -3221,6 +3222,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override + public void setOverrideFoldedArea(Rect area) { + if (mDisplayFoldController != null) { + mDisplayFoldController.setOverrideFoldedArea(area); + } + } + + @Override + public Rect getFoldedArea() { + if (mDisplayFoldController != null) { + return mDisplayFoldController.getFoldedArea(); + } + return new Rect(); + } + + @Override public void registerShortcutKey(long shortcutCode, IShortcutService shortcutService) throws RemoteException { synchronized (mLock) { diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 870d61b2ab90..d7e4b6cff4d8 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -64,6 +64,7 @@ import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.WindowConfiguration; import android.content.Context; @@ -1470,6 +1471,20 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { default void unregisterDisplayFoldListener(IDisplayFoldListener listener) {} /** + * Overrides the folded area. + * + * @param area the overriding folded area or an empty {@code Rect} to clear the override. + */ + default void setOverrideFoldedArea(@NonNull Rect area) {} + + /** + * Get the display folded area. + */ + default @NonNull Rect getFoldedArea() { + return new Rect(); + } + + /** * Updates the flag about whether AOD is showing. * * @return whether the value was changed. diff --git a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java index 3534cf30e2bf..888dd9992bdf 100644 --- a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java +++ b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java @@ -17,10 +17,13 @@ package com.android.server.policy.role; import android.annotation.NonNull; +import android.annotation.UserIdInt; import android.app.role.RoleManager; import android.content.ComponentName; import android.content.Context; +import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; +import android.content.pm.ResolveInfo; import android.os.Debug; import android.provider.Settings; import android.telecom.TelecomManager; @@ -33,6 +36,7 @@ import com.android.internal.util.CollectionUtils; import com.android.server.LocalServices; import com.android.server.role.RoleManagerService; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -54,19 +58,44 @@ public class LegacyRoleResolutionPolicy implements RoleManagerService.RoleHolder @NonNull private final Context mContext; - public LegacyRoleResolutionPolicy(Context context) { + public LegacyRoleResolutionPolicy(@NonNull Context context) { mContext = context; } + @NonNull @Override - public List<String> getRoleHolders(String roleName, int userId) { + public List<String> getRoleHolders(@NonNull String roleName, @UserIdInt int userId) { switch (roleName) { + case RoleManager.ROLE_ASSISTANT: { + String legacyAssistant = Settings.Secure.getStringForUser( + mContext.getContentResolver(), Settings.Secure.ASSISTANT, userId); + if (legacyAssistant == null || legacyAssistant.isEmpty()) { + return Collections.emptyList(); + } else { + return Collections.singletonList( + ComponentName.unflattenFromString(legacyAssistant).getPackageName()); + } + } + case RoleManager.ROLE_BROWSER: { + PackageManagerInternal packageManagerInternal = LocalServices.getService( + PackageManagerInternal.class); + String packageName = packageManagerInternal.removeLegacyDefaultBrowserPackageName( + userId); + return CollectionUtils.singletonOrEmpty(packageName); + } + case RoleManager.ROLE_DIALER: { + String setting = Settings.Secure.getStringForUser( + mContext.getContentResolver(), + Settings.Secure.DIALER_DEFAULT_APPLICATION, userId); + return CollectionUtils.singletonOrEmpty(!TextUtils.isEmpty(setting) + ? setting + : mContext.getSystemService(TelecomManager.class).getSystemDialerPackage()); + } case RoleManager.ROLE_SMS: { // Moved over from SmsApplication#getApplication String result = Settings.Secure.getStringForUser( mContext.getContentResolver(), Settings.Secure.SMS_DEFAULT_APPLICATION, userId); - // TODO: STOPSHIP: Remove the following code once we read the value of // config_defaultSms in RoleControllerService. if (result == null) { @@ -92,34 +121,13 @@ public class LegacyRoleResolutionPolicy implements RoleManagerService.RoleHolder SmsApplication.SmsApplicationData app = applicationData; result = app == null ? null : app.mPackageName; } - return CollectionUtils.singletonOrEmpty(result); } - case RoleManager.ROLE_ASSISTANT: { - String legacyAssistant = Settings.Secure.getStringForUser( - mContext.getContentResolver(), Settings.Secure.ASSISTANT, userId); - - if (legacyAssistant == null || legacyAssistant.isEmpty()) { - return Collections.emptyList(); - } else { - return Collections.singletonList( - ComponentName.unflattenFromString(legacyAssistant).getPackageName()); - } - } - case RoleManager.ROLE_DIALER: { - String setting = Settings.Secure.getStringForUser( - mContext.getContentResolver(), - Settings.Secure.DIALER_DEFAULT_APPLICATION, userId); - - return CollectionUtils.singletonOrEmpty(!TextUtils.isEmpty(setting) - ? setting - : mContext.getSystemService(TelecomManager.class).getSystemDialerPackage()); - } - case RoleManager.ROLE_BROWSER: { - PackageManagerInternal packageManagerInternal = LocalServices.getService( - PackageManagerInternal.class); - String packageName = packageManagerInternal.removeLegacyDefaultBrowserPackageName( - userId); + case RoleManager.ROLE_HOME: { + PackageManager packageManager = mContext.getPackageManager(); + List<ResolveInfo> resolveInfos = new ArrayList<>(); + ComponentName componentName = packageManager.getHomeActivities(resolveInfos); + String packageName = componentName != null ? componentName.getPackageName() : null; return CollectionUtils.singletonOrEmpty(packageName); } default: { diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java index 4186154016e2..8740256af04d 100644 --- a/services/core/java/com/android/server/power/AttentionDetector.java +++ b/services/core/java/com/android/server/power/AttentionDetector.java @@ -123,6 +123,9 @@ public class AttentionDetector { public AttentionDetector(Runnable onUserAttention, Object lock) { mOnUserAttention = onUserAttention; mLock = lock; + + // Device starts with an awake state upon boot. + mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; } public void systemReady(Context context) { @@ -145,7 +148,7 @@ public class AttentionDetector { if (DEBUG) { Slog.d(TAG, "Do not check for attention yet, wait " + (whenToCheck - now)); } - return nextScreenDimming; + return whenToCheck; } else if (whenToStopExtending < whenToCheck) { if (DEBUG) { Slog.d(TAG, "Let device sleep to avoid false results and improve security " diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index 21bf9de5c8b0..c145a22de6cd 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -111,7 +111,8 @@ public class RoleManagerService extends SystemService implements RoleUserState.C /** @see #getRoleHolders(String, int) */ public interface RoleHoldersResolver { /** @return a list of packages that hold a given role for a given user */ - List<String> getRoleHolders(String roleName, int userId); + @NonNull + List<String> getRoleHolders(@NonNull String roleName, @UserIdInt int userId); } /** @@ -154,6 +155,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C PackageManagerInternal packageManagerInternal = LocalServices.getService( PackageManagerInternal.class); packageManagerInternal.setDefaultBrowserProvider(new DefaultBrowserProvider()); + packageManagerInternal.setDefaultHomeProvider(new DefaultHomeProvider()); registerUserRemovedReceiver(); } @@ -194,7 +196,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } performInitialGrantsIfNecessary(userId); } - }, UserHandle.SYSTEM, intentFilter, null /* broadcastPermission */, null /* handler */); + }, UserHandle.ALL, intentFilter, null, null); getContext().getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED), false, @@ -741,4 +743,33 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } } } + + private class DefaultHomeProvider implements PackageManagerInternal.DefaultHomeProvider { + + @Nullable + @Override + public String getDefaultHome(@UserIdInt int userId) { + return CollectionUtils.firstOrNull(getOrCreateUserState(userId).getRoleHolders( + RoleManager.ROLE_HOME)); + } + + @Override + public void setDefaultHomeAsync(@Nullable String packageName, @UserIdInt int userId) { + IRoleManagerCallback callback = new IRoleManagerCallback.Stub() { + @Override + public void onSuccess() {} + @Override + public void onFailure() { + Slog.e(LOG_TAG, "Failed to set default home: " + packageName); + } + }; + if (packageName != null) { + getOrCreateControllerService(userId).onAddRoleHolder(RoleManager.ROLE_HOME, + packageName, 0, callback); + } else { + getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_HOME, 0, + callback); + } + } + } } diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 95c3f4c43313..ceaf829290d7 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -40,6 +40,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.ParcelFileDescriptor; import android.os.Process; +import android.provider.DeviceConfig; import android.util.IntArray; import android.util.Log; import android.util.SparseBooleanArray; @@ -61,6 +62,7 @@ import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; /** * Implementation of service that manages APK level rollbacks. @@ -71,13 +73,19 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // Rollbacks expire after 48 hours. // TODO: How to test rollback expiration works properly? - private static final long ROLLBACK_LIFETIME_DURATION_MILLIS = 48 * 60 * 60 * 1000; + private static final long DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS = + TimeUnit.HOURS.toMillis(48); // Lock used to synchronize accesses to in-memory rollback data // structures. By convention, methods with the suffix "Locked" require // mLock is held when they are called. private final Object mLock = new Object(); + // No need for guarding with lock because value is only accessed in handler thread + // and the value will be written on boot complete. Initialization here happens before + // handler threads are running so that's fine. + private long mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS; + // Used for generating rollback IDs. private final Random mRandom = new SecureRandom(); @@ -484,7 +492,25 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { }); } + private void updateRollbackLifetimeDurationInMillis() { + String strRollbackLifetimeInMillis = DeviceConfig.getProperty( + DeviceConfig.Rollback.BOOT_NAMESPACE, + DeviceConfig.Rollback.ROLLBACK_LIFETIME_IN_MILLIS); + + try { + mRollbackLifetimeDurationInMillis = (strRollbackLifetimeInMillis == null) + ? DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS + : Long.parseLong(strRollbackLifetimeInMillis); + } catch (NumberFormatException e) { + mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS; + } + } + void onBootCompleted() { + getHandler().post(() -> updateRollbackLifetimeDurationInMillis()); + // Also posts to handler thread + scheduleExpiration(0); + getHandler().post(() -> { // Check to see if any staged sessions with rollback enabled have // been applied. @@ -565,8 +591,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { for (RollbackInfo info : mRecentlyExecutedRollbacks) { mAllocatedRollbackIds.put(info.getRollbackId(), true); } - - scheduleExpiration(0); } /** @@ -700,8 +724,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { if (!data.isAvailable) { continue; } - - if (!now.isBefore(data.timestamp.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS))) { + if (!now.isBefore(data.timestamp.plusMillis(mRollbackLifetimeDurationInMillis))) { iter.remove(); deleteRollback(data); } else if (oldest == null || oldest.isAfter(data.timestamp)) { @@ -711,7 +734,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } if (oldest != null) { - scheduleExpiration(now.until(oldest.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS), + scheduleExpiration(now.until(oldest.plusMillis(mRollbackLifetimeDurationInMillis), ChronoUnit.MILLIS)); } } @@ -1144,8 +1167,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { packages.add(data.packages.get(i).getPackageName()); } mPackageHealthObserver.startObservingHealth(packages, - ROLLBACK_LIFETIME_DURATION_MILLIS); - scheduleExpiration(ROLLBACK_LIFETIME_DURATION_MILLIS); + mRollbackLifetimeDurationInMillis); + scheduleExpiration(mRollbackLifetimeDurationInMillis); } catch (IOException e) { Log.e(TAG, "Unable to enable rollback", e); deleteRollback(data); diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index f3393e2f29da..4815e5cd7b83 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -85,7 +85,9 @@ import android.os.SystemProperties; import android.os.Temperature; import android.os.UserHandle; import android.os.UserManager; +import android.os.storage.DiskInfo; import android.os.storage.StorageManager; +import android.os.storage.VolumeInfo; import android.telephony.ModemActivityInfo; import android.telephony.TelephonyManager; import android.util.ArrayMap; @@ -1942,6 +1944,41 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } + private void pullTimeZoneDataInfo(int tagId, + long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) { + String tzDbVersion = "Unknown"; + try { + tzDbVersion = android.icu.util.TimeZone.getTZDataVersion(); + } catch (Exception e) { + Log.e(TAG, "Getting tzdb version failed: ", e); + } + + StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); + e.writeString(tzDbVersion); + pulledData.add(e); + } + + private void pullSDCardInfo(int tagId, long elapsedNanos, long wallClockNanos, + List<StatsLogEventWrapper> pulledData) { + StorageManager storageManager = mContext.getSystemService(StorageManager.class); + if (storageManager != null) { + List<VolumeInfo> volumes = storageManager.getVolumes(); + for (VolumeInfo vol : volumes) { + final String envState = VolumeInfo.getEnvironmentForState(vol.getState()); + final DiskInfo diskInfo = vol.getDisk(); + if (diskInfo != null && diskInfo.isSd()) { + if (envState.equals(Environment.MEDIA_MOUNTED)) { + StatsLogEventWrapper e = + new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); + e.writeInt(vol.getType() + 1); + e.writeLong(diskInfo.size); + pulledData.add(e); + } + } + } + } + } + /** * Pulls various data. */ @@ -2130,6 +2167,14 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pullDangerousPermissionState(elapsedNanos, wallClockNanos, ret); break; } + case StatsLog.TIME_ZONE_DATA_INFO: { + pullTimeZoneDataInfo(tagId, elapsedNanos, wallClockNanos, ret); + break; + } + case StatsLog.SDCARD_INFO: { + pullSDCardInfo(tagId, elapsedNanos, wallClockNanos, ret); + break; + } default: Slog.w(TAG, "No such tagId data as " + tagId); return null; diff --git a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java index ff01d46e6909..b12129835ca1 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java +++ b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java @@ -19,6 +19,7 @@ import static android.app.StatusBarManager.DEFAULT_SETUP_DISABLE_FLAGS; import static android.app.StatusBarManager.DISABLE2_NONE; import static android.app.StatusBarManager.DISABLE_NONE; +import android.app.StatusBarManager.DisableInfo; import android.content.ComponentName; import android.content.Context; import android.os.Binder; @@ -26,6 +27,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.ShellCommand; import android.service.quicksettings.TileService; +import android.util.Pair; import java.io.PrintWriter; @@ -68,6 +70,8 @@ public class StatusBarShellCommand extends ShellCommand { return runGetStatusIcons(); case "disable-for-setup": return runDisableForSetup(); + case "send-disable-flag": + return runSendDisableFlag(); default: return handleDefaultCommands(cmd); } @@ -132,6 +136,47 @@ public class StatusBarShellCommand extends ShellCommand { return 0; } + private int runSendDisableFlag() { + String pkg = mContext.getPackageName(); + int disable1 = DISABLE_NONE; + int disable2 = DISABLE2_NONE; + + DisableInfo info = new DisableInfo(); + + String arg = getNextArg(); + while (arg != null) { + switch (arg) { + case "search": + info.setSearchDisabled(true); + break; + case "home": + info.setNagivationHomeDisabled(true); + break; + case "recents": + info.setRecentsDisabled(true); + break; + case "notification-alerts": + info.setNotificationPeekingDisabled(true); + break; + case "statusbar-expansion": + info.setStatusBarExpansionDisabled(true); + break; + + default: + break; + } + + arg = getNextArg(); + } + + Pair<Integer, Integer> flagPair = info.toFlags(); + + mInterface.disable(flagPair.first, sToken, pkg); + mInterface.disable2(flagPair.second, sToken, pkg); + + return 0; + } + @Override public void onHelp() { final PrintWriter pw = getOutPrintWriter(); @@ -166,6 +211,17 @@ public class StatusBarShellCommand extends ShellCommand { pw.println(" disable-for-setup DISABLE"); pw.println(" If true, disable status bar components unsuitable for device setup"); pw.println(""); + pw.println(" send-disable-flag FLAG..."); + pw.println(" Send zero or more disable flags (parsed individually) to StatusBarManager"); + pw.println(" Valid options:"); + pw.println(" <blank> - equivalent to \"none\""); + pw.println(" none - re-enables all components"); + pw.println(" search - disable search"); + pw.println(" home - disable naviagation home"); + pw.println(" recents - disable recents/overview"); + pw.println(" notification-peek - disable notification peeking"); + pw.println(" statusbar-expansion - disable status bar expansion"); + pw.println(""); } /** diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java index 7a3f030f9dd7..f581bc0bca46 100644 --- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java +++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java @@ -44,7 +44,7 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.UserManagerService; import com.android.server.pm.permission.DefaultPermissionGrantPolicy; -import com.android.server.pm.permission.PermissionManagerInternal; +import com.android.server.pm.permission.PermissionManagerServiceInternal; /** * Starts the telecom component by binding to its ITelecomService implementation. Telecom is setup @@ -133,7 +133,7 @@ public class TelecomLoaderService extends SystemService { } private DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy() { - return LocalServices.getService(PermissionManagerInternal.class) + return LocalServices.getService(PermissionManagerServiceInternal.class) .getDefaultPermissionGrantPolicy(); } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index b0ef8a0d4209..071dde74f103 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -2243,12 +2243,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub synchronized (mLock) { mInAmbientMode = inAmbientMode; final WallpaperData data = mWallpaperMap.get(mCurrentUserId); - final boolean hasConnection = data != null && data.connection != null; - final WallpaperInfo info = hasConnection ? data.connection.mInfo : null; - // The wallpaper info is null for image wallpaper, also use the engine in this case. - if (hasConnection && (info == null && isAodImageWallpaperEnabled() - || info != null && info.supportsAmbientMode())) { + if (data != null && data.connection != null && (data.connection.mInfo == null + || data.connection.mInfo.supportsAmbientMode())) { // TODO(multi-display) Extends this method with specific display. engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine; } else { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 0251efb872bc..f33c518941e5 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2677,8 +2677,9 @@ final class ActivityRecord extends ConfigurationContainer { * Get the configuration orientation by the requested screen orientation * ({@link ActivityInfo.ScreenOrientation}) of this activity. * - * @return orientation in ({@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT}, - * {@link #ORIENTATION_UNDEFINED}). + * @return orientation in ({@link Configuration#ORIENTATION_LANDSCAPE}, + * {@link Configuration#ORIENTATION_PORTRAIT}, + * {@link Configuration#ORIENTATION_UNDEFINED}). */ int getRequestedConfigurationOrientation() { final int screenOrientation = getOrientation(); @@ -2936,14 +2937,36 @@ final class ActivityRecord extends ConfigurationContainer { // should be given the aspect ratio. activityWidth = (int) ((activityHeight * maxAspectRatio) + 0.5f); } - } else if (containingRatio < minAspectRatio && minAspectRatio != 0) { - if (containingAppWidth < containingAppHeight) { - // Width is the shorter side, so we use the height to figure-out what the max. width - // should be given the aspect ratio. + } else if (containingRatio < minAspectRatio) { + boolean adjustWidth; + switch (getRequestedConfigurationOrientation()) { + case ORIENTATION_LANDSCAPE: + // Width should be the longer side for this landscape app, so we use the width + // to figure-out what the max. height should be given the aspect ratio. + adjustWidth = false; + break; + case ORIENTATION_PORTRAIT: + // Height should be the longer side for this portrait app, so we use the height + // to figure-out what the max. width should be given the aspect ratio. + adjustWidth = true; + break; + default: + // This app doesn't have a preferred orientation, so we keep the length of the + // longer side, and use it to figure-out the length of the shorter side. + if (containingAppWidth < containingAppHeight) { + // Width is the shorter side, so we use the height to figure-out what the + // max. width should be given the aspect ratio. + adjustWidth = true; + } else { + // Height is the shorter side, so we use the width to figure-out what the + // max. height should be given the aspect ratio. + adjustWidth = false; + } + break; + } + if (adjustWidth) { activityWidth = (int) ((activityHeight / minAspectRatio) + 0.5f); } else { - // Height is the shorter side, so we use the width to figure-out what the max. - // height should be given the aspect ratio. activityHeight = (int) ((activityWidth / minAspectRatio) + 0.5f); } } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index c33a2c179ab7..d40948b305a2 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -830,12 +830,25 @@ class ActivityStarter { new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT, null); - final int flags = intent.getFlags(); Intent newIntent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS); - newIntent.setFlags(flags - | FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_MULTIPLE_TASK - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + + int flags = intent.getFlags(); + flags |= Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; + + /* + * Prevent reuse of review activity: Each app needs their own review activity. By + * default activities launched with NEW_TASK or NEW_DOCUMENT try to reuse activities + * with the same launch parameters (extras are ignored). Hence to avoid possible + * reuse force a new activity via the MULTIPLE_TASK flag. + * + * Activities that are not launched with NEW_TASK or NEW_DOCUMENT are not re-used, + * hence no need to add the flag in this case. + */ + if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0) { + flags |= Intent.FLAG_ACTIVITY_MULTIPLE_TASK; + } + newIntent.setFlags(flags); + newIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName); newIntent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target)); if (resultRecord != null) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 5cfc20b6339f..4795555e8ed2 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1500,8 +1500,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity; final int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity; - mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp); mDisplayPolicy.configure(width, height, shortSizeDp); + mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp); mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo, calculateDisplayCutoutForRotation(mDisplayInfo.rotation)); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 2ee30ac5c8ff..91d573defc16 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -2617,9 +2617,8 @@ public class DisplayPolicy { DisplayCutout displayCutout) { int width = fullWidth; if (hasNavigationBar()) { - // For a basic navigation bar, when we are in landscape mode we place - // the navigation bar to the side. - if (navigationBarCanMove() && fullWidth > fullHeight) { + final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation); + if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) { width -= getNavigationBarWidth(rotation, uiMode); } } @@ -2646,9 +2645,8 @@ public class DisplayPolicy { DisplayCutout displayCutout) { int height = fullHeight; if (hasNavigationBar()) { - // For a basic navigation bar, when we are in portrait mode we place - // the navigation bar to the bottom. - if (!navigationBarCanMove() || fullWidth < fullHeight) { + final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation); + if (navBarPosition == NAV_BAR_BOTTOM) { height -= getNavigationBarHeight(rotation, uiMode); } } diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 5f341ee8002c..543f19655350 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -38,6 +38,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; import android.util.SparseArray; +import android.view.DisplayCutout; import android.view.Surface; import com.android.internal.annotations.VisibleForTesting; @@ -70,6 +71,8 @@ public class DisplayRotation { private final int mDeskDockRotation; private final int mUndockedHdmiRotation; + private final float mCloseToSquareMaxAspectRatio; + private OrientationListener mOrientationListener; private StatusBarManagerInternal mStatusBarManagerInternal; private SettingsObserver mSettingsObserver; @@ -132,6 +135,9 @@ public class DisplayRotation { mUndockedHdmiRotation = readRotation( com.android.internal.R.integer.config_undockedHdmiRotation); + mCloseToSquareMaxAspectRatio = mContext.getResources().getFloat( + com.android.internal.R.dimen.config_closeToSquareDisplayMaxAspectRatio); + if (isDefaultDisplay) { final Handler uiHandler = UiThread.getHandler(); mOrientationListener = new OrientationListener(mContext, uiHandler); @@ -212,10 +218,12 @@ public class DisplayRotation { // so if the orientation is forced, we need to respect that no matter what. final boolean isTv = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_LEANBACK); + final boolean isCloseToSquare = + isNonDecorDisplayCloseToSquare(Surface.ROTATION_0, width, height); final boolean forceDefaultOrientationInRes = res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation); final boolean forceDefaultOrienation = - ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv) + ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv || isCloseToSquare) && forceDefaultOrientationInRes // For debug purposes the next line turns this feature off with: // $ adb shell setprop config.override_forced_orient true @@ -227,6 +235,18 @@ public class DisplayRotation { setFixedToUserRotation(forceDefaultOrienation); } + private boolean isNonDecorDisplayCloseToSquare(int rotation, int width, int height) { + final DisplayCutout displayCutout = + mDisplayContent.calculateDisplayCutoutForRotation(rotation).getDisplayCutout(); + final int uiMode = mService.mPolicy.getUiMode(); + final int w = mDisplayPolicy.getNonDecorDisplayWidth( + width, height, rotation, uiMode, displayCutout); + final int h = mDisplayPolicy.getNonDecorDisplayHeight( + width, height, rotation, uiMode, displayCutout); + final float aspectRatio = Math.max(w, h) / (float) Math.min(w, h); + return aspectRatio <= mCloseToSquareMaxAspectRatio; + } + void setRotation(int rotation) { if (mOrientationListener != null) { mOrientationListener.setCurrentRotation(rotation); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c747c6d95fcd..7beee0e33355 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -22,6 +22,7 @@ import static android.Manifest.permission.MANAGE_APP_TOKENS; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS; import static android.Manifest.permission.RESTRICTED_VR_ACCESS; +import static android.Manifest.permission.WRITE_SECURE_SETTINGS; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; @@ -446,7 +447,8 @@ public class WindowManagerService extends IWindowManager.Stub final boolean mLimitedAlphaCompositing; final int mMaxUiWidth; - final WindowManagerPolicy mPolicy; + @VisibleForTesting + WindowManagerPolicy mPolicy; final IActivityManager mActivityManager; // TODO: Probably not needed once activities are fully in WM. @@ -3758,6 +3760,41 @@ public class WindowManagerService extends IWindowManager.Stub mPolicy.unregisterDisplayFoldListener(listener); } + /** + * Overrides the folded area. + * + * @param area the overriding folded area or an empty {@code Rect} to clear the override. + */ + void setOverrideFoldedArea(@NonNull Rect area) { + if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); + } + + long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + mPolicy.setOverrideFoldedArea(area); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + /** + * Get the display folded area. + */ + @NonNull Rect getFoldedArea() { + long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + return mPolicy.getFoldedArea(); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + @Override public int getPreferredOptionsPanelGravity(int displayId) { synchronized (mGlobalLock) { @@ -4263,9 +4300,12 @@ public class WindowManagerService extends IWindowManager.Stub if (mMaxUiWidth > 0) { mRoot.forAllDisplays(displayContent -> displayContent.setMaxUiWidth(mMaxUiWidth)); } - applyForcedPropertiesForDefaultDisplay(); + final boolean changed = applyForcedPropertiesForDefaultDisplay(); mAnimator.ready(); mDisplayReady = true; + if (changed) { + reconfigureDisplayLocked(getDefaultDisplayContentLocked()); + } mIsTouchDevice = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_TOUCHSCREEN); } @@ -4822,11 +4862,9 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void setForcedDisplaySize(int displayId, int width, int height) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold permission " + - android.Manifest.permission.WRITE_SECURE_SETTINGS); + if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); } final long ident = Binder.clearCallingIdentity(); @@ -4844,11 +4882,9 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void setForcedDisplayScalingMode(int displayId, int mode) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold permission " + - android.Manifest.permission.WRITE_SECURE_SETTINGS); + if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); } final long ident = Binder.clearCallingIdentity(); @@ -4865,7 +4901,8 @@ public class WindowManagerService extends IWindowManager.Stub } /** The global settings only apply to default display. */ - private void applyForcedPropertiesForDefaultDisplay() { + private boolean applyForcedPropertiesForDefaultDisplay() { + boolean changed = false; final DisplayContent displayContent = getDefaultDisplayContentLocked(); // Display size. String sizeStr = Settings.Global.getString(mContext.getContentResolver(), @@ -4885,6 +4922,7 @@ public class WindowManagerService extends IWindowManager.Stub Slog.i(TAG_WM, "FORCED DISPLAY SIZE: " + width + "x" + height); displayContent.updateBaseDisplayMetrics(width, height, displayContent.mBaseDisplayDensity); + changed = true; } } catch (NumberFormatException ex) { } @@ -4893,26 +4931,27 @@ public class WindowManagerService extends IWindowManager.Stub // Display density. final int density = getForcedDisplayDensityForUserLocked(mCurrentUserId); - if (density != 0) { + if (density != 0 && density != displayContent.mBaseDisplayDensity) { displayContent.mBaseDisplayDensity = density; + changed = true; } // Display scaling mode. int mode = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.DISPLAY_SCALING_FORCE, 0); - if (mode != 0) { + if (displayContent.mDisplayScalingDisabled != (mode != 0)) { Slog.i(TAG_WM, "FORCED DISPLAY SCALING DISABLED"); displayContent.mDisplayScalingDisabled = true; + changed = true; } + return changed; } @Override public void clearForcedDisplaySize(int displayId) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold permission " + - android.Manifest.permission.WRITE_SECURE_SETTINGS); + if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); } final long ident = Binder.clearCallingIdentity(); @@ -4953,11 +4992,9 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void setForcedDisplayDensityForUser(int displayId, int density, int userId) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold permission " + - android.Manifest.permission.WRITE_SECURE_SETTINGS); + if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); } final int targetUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), @@ -4978,11 +5015,9 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void clearForcedDisplayDensityForUser(int displayId, int userId) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold permission " + - android.Manifest.permission.WRITE_SECURE_SETTINGS); + if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); } final int callingUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), @@ -5047,11 +5082,9 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void setOverscan(int displayId, int left, int top, int right, int bottom) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold permission " + - android.Manifest.permission.WRITE_SECURE_SETTINGS); + if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); } final long ident = Binder.clearCallingIdentity(); try { @@ -5081,11 +5114,7 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void startWindowTrace(){ - try { - mWindowTracing.startTrace(null /* printwriter */); - } catch (IOException e) { - throw new RuntimeException(e); - } + mWindowTracing.startTrace(null /* printwriter */); } @Override diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index 83e3c71cbee3..d13ee459c115 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -62,6 +62,8 @@ public class WindowManagerShellCommand extends ShellCommand { return runDisplaySize(pw); case "density": return runDisplayDensity(pw); + case "folded-area": + return runDisplayFoldedArea(pw); case "overscan": return runDisplayOverscan(pw); case "scaling": @@ -207,6 +209,40 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } + private void printFoldedArea(PrintWriter pw) { + final Rect foldedArea = mInternal.getFoldedArea(); + if (foldedArea.isEmpty()) { + pw.println("Folded area: none"); + } else { + pw.println("Folded area: " + foldedArea.left + "," + foldedArea.top + "," + + foldedArea.right + "," + foldedArea.bottom); + } + } + + private int runDisplayFoldedArea(PrintWriter pw) { + final String areaStr = getNextArg(); + final Rect rect = new Rect(); + if (areaStr == null) { + printFoldedArea(pw); + return 0; + } else if ("reset".equals(areaStr)) { + rect.setEmpty(); + } else { + final Pattern flattenedPattern = Pattern.compile( + "(-?\\d+),(-?\\d+),(-?\\d+),(-?\\d+)"); + final Matcher matcher = flattenedPattern.matcher(areaStr); + if (!matcher.matches()) { + getErrPrintWriter().println("Error: area should be LEFT,TOP,RIGHT,BOTTOM"); + return -1; + } + rect.set(Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)), + Integer.parseInt(matcher.group(3)), Integer.parseInt(matcher.group(4))); + } + + mInternal.setOverrideFoldedArea(rect); + return 0; + } + private int runDisplayOverscan(PrintWriter pw) throws RemoteException { String overscanStr = getNextArgRequired(); Rect rect = new Rect(); @@ -335,6 +371,8 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println(" width and height in pixels unless suffixed with 'dp'."); pw.println(" density [reset|DENSITY] [-d DISPLAY_ID]"); pw.println(" Return or override display density."); + pw.println(" folded-area [reset|LEFT,TOP,RIGHT,BOTTOM]"); + pw.println(" Return or override folded area."); pw.println(" overscan [reset|LEFT,TOP,RIGHT,BOTTOM] [-d DISPLAY ID]"); pw.println(" Set overscan area for display."); pw.println(" scaling [off|auto] [-d DISPLAY_ID]"); diff --git a/services/core/java/com/android/server/wm/WindowTraceBuffer.java b/services/core/java/com/android/server/wm/WindowTraceBuffer.java index e4461ea90c91..2ce6e6c1d049 100644 --- a/services/core/java/com/android/server/wm/WindowTraceBuffer.java +++ b/services/core/java/com/android/server/wm/WindowTraceBuffer.java @@ -20,7 +20,6 @@ import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER; import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H; import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L; -import android.os.Trace; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; @@ -36,24 +35,30 @@ import java.util.Queue; /** * Buffer used for window tracing. */ -abstract class WindowTraceBuffer { +class WindowTraceBuffer { private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; - final Object mBufferLock = new Object(); - final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>(); - final File mTraceFile; - int mBufferSize; - private final int mBufferCapacity; + private final Object mBufferLock = new Object(); - WindowTraceBuffer(int size, File traceFile) throws IOException { - mBufferCapacity = size; - mTraceFile = traceFile; + private final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>(); + private int mBufferUsedSize; + private int mBufferCapacity; - initTraceFile(); + WindowTraceBuffer(int bufferCapacity) { + mBufferCapacity = bufferCapacity; + resetBuffer(); } int getAvailableSpace() { - return mBufferCapacity - mBufferSize; + return mBufferCapacity - mBufferUsedSize; + } + + int size() { + return mBuffer.size(); + } + + void setCapacity(int capacity) { + mBufferCapacity = capacity; } /** @@ -70,42 +75,37 @@ abstract class WindowTraceBuffer { + mBufferCapacity + " Object size: " + protoLength); } synchronized (mBufferLock) { - boolean canAdd = canAdd(protoLength); - if (canAdd) { - mBuffer.add(proto); - mBufferSize += protoLength; - } + discardOldest(protoLength); + mBuffer.add(proto); + mBufferUsedSize += protoLength; mBufferLock.notify(); } } - /** - * Stops the buffer execution and flush all buffer content to the disk. - * - * @throws IOException if the buffer cannot write its contents to the {@link #mTraceFile} - */ - void dump() throws IOException, InterruptedException { - try { - Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFile"); - writeTraceToFile(); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); - } - } - - @VisibleForTesting boolean contains(byte[] other) { return mBuffer.stream() .anyMatch(p -> Arrays.equals(p.getBytes(), other)); } - private void initTraceFile() throws IOException { - mTraceFile.delete(); - try (OutputStream os = new FileOutputStream(mTraceFile)) { - mTraceFile.setReadable(true, false); - ProtoOutputStream proto = new ProtoOutputStream(os); - proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE); - proto.flush(); + /** + * Writes the trace buffer to disk. + */ + void writeTraceToFile(File traceFile) throws IOException { + synchronized (mBufferLock) { + traceFile.delete(); + traceFile.setReadable(true, false); + try (OutputStream os = new FileOutputStream(traceFile)) { + ProtoOutputStream proto = new ProtoOutputStream(); + proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE); + os.write(proto.getBytes()); + while (!mBuffer.isEmpty()) { + proto = mBuffer.poll(); + mBufferUsedSize -= proto.getRawSize(); + byte[] protoBytes = proto.getBytes(); + os.write(protoBytes); + } + os.flush(); + } } } @@ -114,59 +114,48 @@ abstract class WindowTraceBuffer { * smaller than the overall buffer size. * * @param protoLength byte array representation of the Proto object to add - * @return {@code true} if the element can be added to the buffer or not - */ - abstract boolean canAdd(int protoLength); - - /** - * Flush all buffer content to the disk. - * - * @throws IOException if the buffer cannot write its contents to the {@link #mTraceFile} */ - abstract void writeTraceToFile() throws IOException, InterruptedException; - - /** - * Builder for a {@code WindowTraceBuffer} which creates a {@link WindowTraceRingBuffer} for - * continuous mode or a {@link WindowTraceQueueBuffer} otherwise - */ - static class Builder { - private boolean mContinuous; - private File mTraceFile; - private int mBufferCapacity; - - Builder setContinuousMode(boolean continuous) { - mContinuous = continuous; - return this; - } + private void discardOldest(int protoLength) { + long availableSpace = getAvailableSpace(); - Builder setTraceFile(File traceFile) { - mTraceFile = traceFile; - return this; - } + while (availableSpace < protoLength) { - Builder setBufferCapacity(int size) { - mBufferCapacity = size; - return this; + ProtoOutputStream item = mBuffer.poll(); + if (item == null) { + throw new IllegalStateException("No element to discard from buffer"); + } + mBufferUsedSize -= item.getRawSize(); + availableSpace = getAvailableSpace(); } + } - File getFile() { - return mTraceFile; + /** + * Removes all elements form the buffer + */ + void resetBuffer() { + synchronized (mBufferLock) { + mBuffer.clear(); + mBufferUsedSize = 0; } + } - WindowTraceBuffer build() throws IOException { - if (mBufferCapacity <= 0) { - throw new IllegalStateException("Buffer capacity must be greater than 0."); - } - - if (mTraceFile == null) { - throw new IllegalArgumentException("A valid trace file must be specified."); - } + @VisibleForTesting + int getBufferSize() { + return mBufferUsedSize; + } - if (mContinuous) { - return new WindowTraceRingBuffer(mBufferCapacity, mTraceFile); - } else { - return new WindowTraceQueueBuffer(mBufferCapacity, mTraceFile); - } + String getStatus() { + synchronized (mBufferLock) { + return "Buffer size: " + + mBufferCapacity + + " bytes" + + "\n" + + "Buffer usage: " + + mBufferUsedSize + + " bytes" + + "\n" + + "Elements in the buffer: " + + mBuffer.size(); } } } diff --git a/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java b/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java deleted file mode 100644 index 5888b7a799cf..000000000000 --- a/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static android.os.Build.IS_USER; - -import android.util.Log; -import android.util.proto.ProtoOutputStream; - -import com.android.internal.annotations.VisibleForTesting; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -/** - * A buffer structure backed by a {@link java.util.concurrent.BlockingQueue} to store the first - * {@code #size size} bytes of window trace elements. - * Once the buffer is full it will no longer accepts new elements. - */ -class WindowTraceQueueBuffer extends WindowTraceBuffer { - private static final String TAG = "WindowTracing"; - - private Thread mConsumerThread; - private boolean mCancel; - - @VisibleForTesting - WindowTraceQueueBuffer(int size, File traceFile, boolean startConsumerThread) - throws IOException { - super(size, traceFile); - if (startConsumerThread) { - initializeConsumerThread(); - } - } - - WindowTraceQueueBuffer(int size, File traceFile) throws IOException { - this(size, traceFile, !IS_USER); - } - - private void initializeConsumerThread() { - mCancel = false; - mConsumerThread = new Thread(() -> { - try { - loop(); - } catch (InterruptedException e) { - Log.i(TAG, "Interrupting trace consumer thread"); - } catch (IOException e) { - Log.e(TAG, "Failed to execute trace consumer thread", e); - } - }, "window_tracing"); - mConsumerThread.start(); - } - - private void loop() throws IOException, InterruptedException { - while (!mCancel) { - ProtoOutputStream proto; - synchronized (mBufferLock) { - mBufferLock.wait(); - proto = mBuffer.poll(); - if (proto != null) { - mBufferSize -= proto.getRawSize(); - } - } - if (proto != null) { - try (OutputStream os = new FileOutputStream(mTraceFile, true)) { - byte[] protoBytes = proto.getBytes(); - os.write(protoBytes); - } - } - } - } - - @Override - boolean canAdd(int protoLength) { - long availableSpace = getAvailableSpace(); - return availableSpace >= protoLength; - } - - @Override - void writeTraceToFile() throws InterruptedException { - synchronized (mBufferLock) { - mCancel = true; - mBufferLock.notify(); - } - if (mConsumerThread != null) { - mConsumerThread.join(); - mConsumerThread = null; - } - } -} diff --git a/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java b/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java deleted file mode 100644 index 77d30be816bc..000000000000 --- a/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import android.util.proto.ProtoOutputStream; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -/** - * A ring buffer to store the {@code #size size} bytes of window trace data. - * The buffer operates on a trace entry level, that is, if the new trace data is larger than the - * available buffer space, the buffer will discard as many full trace entries as necessary to fit - * the new trace. - */ -class WindowTraceRingBuffer extends WindowTraceBuffer { - WindowTraceRingBuffer(int size, File traceFile) throws IOException { - super(size, traceFile); - } - - @Override - boolean canAdd(int protoLength) { - long availableSpace = getAvailableSpace(); - - while (availableSpace < protoLength) { - discardOldest(); - availableSpace = getAvailableSpace(); - } - - return true; - } - - @Override - void writeTraceToFile() throws IOException { - synchronized (mBufferLock) { - try (OutputStream os = new FileOutputStream(mTraceFile, true)) { - while (!mBuffer.isEmpty()) { - ProtoOutputStream proto = mBuffer.poll(); - mBufferSize -= proto.getRawSize(); - byte[] protoBytes = proto.getBytes(); - os.write(protoBytes); - } - } - } - } - - private void discardOldest() { - ProtoOutputStream item = mBuffer.poll(); - if (item == null) { - throw new IllegalStateException("No element to discard from buffer"); - } - mBufferSize -= item.getRawSize(); - } -} diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java index abc474d756b7..0ce215c88dad 100644 --- a/services/core/java/com/android/server/wm/WindowTracing.java +++ b/services/core/java/com/android/server/wm/WindowTracing.java @@ -31,8 +31,6 @@ import android.util.Log; import android.util.proto.ProtoOutputStream; import android.view.Choreographer; -import com.android.internal.annotations.VisibleForTesting; - import java.io.File; import java.io.IOException; import java.io.PrintWriter; @@ -47,139 +45,191 @@ class WindowTracing { * Maximum buffer size, currently defined as 512 KB * Size was experimentally defined to fit between 100 to 150 elements. */ - private static final int WINDOW_TRACE_BUFFER_SIZE = 512 * 1024; + private static final int BUFFER_CAPACITY_CRITICAL = 512 * 1024; + private static final int BUFFER_CAPACITY_TRIM = 2048 * 1024; + private static final int BUFFER_CAPACITY_ALL = 4096 * 1024; + private static final String TRACE_FILENAME = "/data/misc/wmtrace/wm_trace.pb"; private static final String TAG = "WindowTracing"; private final WindowManagerService mService; private final Choreographer mChoreographer; private final WindowManagerGlobalLock mGlobalLock; - private final Object mLock = new Object(); - private final WindowTraceBuffer.Builder mBufferBuilder; - - private WindowTraceBuffer mTraceBuffer; + private final Object mEnabledLock = new Object(); + private final File mTraceFile; + private final WindowTraceBuffer mBuffer; + private final Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) -> + log("onFrame" /* where */); - private @WindowTraceLogLevel int mWindowTraceLogLevel = WindowTraceLogLevel.TRIM; - private boolean mContinuousMode; + private @WindowTraceLogLevel int mLogLevel = WindowTraceLogLevel.TRIM; + private boolean mLogOnFrame = false; private boolean mEnabled; private volatile boolean mEnabledLockFree; private boolean mScheduled; - private Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) -> - log("onFrame" /* where */); - private WindowTracing(File file, WindowManagerService service, Choreographer choreographer) { - this(file, service, choreographer, service.mGlobalLock); + static WindowTracing createDefaultAndStartLooper(WindowManagerService service, + Choreographer choreographer) { + File file = new File(TRACE_FILENAME); + return new WindowTracing(file, service, choreographer, BUFFER_CAPACITY_TRIM); } - @VisibleForTesting - WindowTracing(File file, WindowManagerService service, Choreographer choreographer, - WindowManagerGlobalLock globalLock) { - mBufferBuilder = new WindowTraceBuffer.Builder() - .setTraceFile(file) - .setBufferCapacity(WINDOW_TRACE_BUFFER_SIZE); + private WindowTracing(File file, WindowManagerService service, Choreographer choreographer, + int bufferCapacity) { + this(file, service, choreographer, service.mGlobalLock, bufferCapacity); + } + WindowTracing(File file, WindowManagerService service, Choreographer choreographer, + WindowManagerGlobalLock globalLock, int bufferCapacity) { mChoreographer = choreographer; mService = service; mGlobalLock = globalLock; + mTraceFile = file; + mBuffer = new WindowTraceBuffer(bufferCapacity); + setLogLevel(WindowTraceLogLevel.TRIM, null /* pw */); } - void startTrace(@Nullable PrintWriter pw) throws IOException { + void startTrace(@Nullable PrintWriter pw) { if (IS_USER) { logAndPrintln(pw, "Error: Tracing is not supported on user builds."); return; } - synchronized (mLock) { - logAndPrintln(pw, "Start tracing to " + mBufferBuilder.getFile() + "."); - if (mTraceBuffer != null) { - writeTraceToFileLocked(); - } - mTraceBuffer = mBufferBuilder - .setContinuousMode(mContinuousMode) - .build(); + synchronized (mEnabledLock) { + logAndPrintln(pw, "Start tracing to " + mTraceFile + "."); + mBuffer.resetBuffer(); mEnabled = mEnabledLockFree = true; } } - private void logAndPrintln(@Nullable PrintWriter pw, String msg) { - Log.i(TAG, msg); - if (pw != null) { - pw.println(msg); - pw.flush(); - } - } - void stopTrace(@Nullable PrintWriter pw) { if (IS_USER) { logAndPrintln(pw, "Error: Tracing is not supported on user builds."); return; } - synchronized (mLock) { - logAndPrintln(pw, "Stop tracing to " + mBufferBuilder.getFile() - + ". Waiting for traces to flush."); + synchronized (mEnabledLock) { + logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush."); mEnabled = mEnabledLockFree = false; - synchronized (mLock) { - if (mEnabled) { - logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush."); - throw new IllegalStateException("tracing enabled while waiting for flush."); - } - writeTraceToFileLocked(); - mTraceBuffer = null; + if (mEnabled) { + logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush."); + throw new IllegalStateException("tracing enabled while waiting for flush."); } - logAndPrintln(pw, "Trace written to " + mBufferBuilder.getFile() + "."); + writeTraceToFileLocked(); + logAndPrintln(pw, "Trace written to " + mTraceFile + "."); } } - @VisibleForTesting - void setContinuousMode(boolean continuous, PrintWriter pw) { - logAndPrintln(pw, "Setting window tracing continuous mode to " + continuous); + private void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw) { + logAndPrintln(pw, "Setting window tracing log level to " + logLevel); + mLogLevel = logLevel; - if (mEnabled) { - logAndPrintln(pw, "Trace is currently active, change will take effect once the " - + "trace is restarted."); + switch (logLevel) { + case WindowTraceLogLevel.ALL: { + setBufferCapacity(BUFFER_CAPACITY_ALL, pw); + break; + } + case WindowTraceLogLevel.TRIM: { + setBufferCapacity(BUFFER_CAPACITY_TRIM, pw); + break; + } + case WindowTraceLogLevel.CRITICAL: { + setBufferCapacity(BUFFER_CAPACITY_CRITICAL, pw); + break; + } } - mContinuousMode = continuous; - mWindowTraceLogLevel = (continuous) ? WindowTraceLogLevel.CRITICAL : - WindowTraceLogLevel.TRIM; } - boolean isEnabled() { - return mEnabledLockFree; + private void setLogFrequency(boolean onFrame, PrintWriter pw) { + logAndPrintln(pw, "Setting window tracing log frequency to " + + ((onFrame) ? "frame" : "transaction")); + mLogOnFrame = onFrame; } - static WindowTracing createDefaultAndStartLooper(WindowManagerService service, - Choreographer choreographer) { - File file = new File("/data/misc/wmtrace/wm_trace.pb"); - return new WindowTracing(file, service, choreographer); + private void setBufferCapacity(int capacity, PrintWriter pw) { + logAndPrintln(pw, "Setting window tracing buffer capacity to " + capacity + "bytes"); + mBuffer.setCapacity(capacity); + } + + boolean isEnabled() { + return mEnabledLockFree; } int onShellCommand(ShellCommand shell) { PrintWriter pw = shell.getOutPrintWriter(); - try { - String cmd = shell.getNextArgRequired(); - switch (cmd) { - case "start": - startTrace(pw); - return 0; - case "stop": - stopTrace(pw); - return 0; - case "continuous": - setContinuousMode(Boolean.valueOf(shell.getNextArgRequired()), pw); - return 0; - default: - pw.println("Unknown command: " + cmd); - return -1; - } - } catch (IOException e) { - logAndPrintln(pw, e.toString()); - throw new RuntimeException(e); + String cmd = shell.getNextArgRequired(); + switch (cmd) { + case "start": + startTrace(pw); + return 0; + case "stop": + stopTrace(pw); + return 0; + case "status": + logAndPrintln(pw, getStatus()); + return 0; + case "frame": + setLogFrequency(true /* onFrame */, pw); + mBuffer.resetBuffer(); + return 0; + case "transaction": + setLogFrequency(false /* onFrame */, pw); + mBuffer.resetBuffer(); + return 0; + case "level": + String logLevelStr = shell.getNextArgRequired().toLowerCase(); + switch (logLevelStr) { + case "all": { + setLogLevel(WindowTraceLogLevel.ALL, pw); + break; + } + case "trim": { + setLogLevel(WindowTraceLogLevel.TRIM, pw); + break; + } + case "critical": { + setLogLevel(WindowTraceLogLevel.CRITICAL, pw); + break; + } + default: { + setLogLevel(WindowTraceLogLevel.TRIM, pw); + break; + } + } + mBuffer.resetBuffer(); + return 0; + case "size": + setBufferCapacity(Integer.parseInt(shell.getNextArgRequired()) * 1024, pw); + mBuffer.resetBuffer(); + return 0; + default: + pw.println("Unknown command: " + cmd); + pw.println("Window manager trace options:"); + pw.println(" start: Start logging"); + pw.println(" stop: Stop logging"); + pw.println(" frame: Log trace once per frame"); + pw.println(" transaction: Log each transaction"); + pw.println(" size: Set the maximum log size (in KB)"); + pw.println(" level [lvl]: Set the log level between"); + pw.println(" lvl may be one of:"); + pw.println(" critical: Only visible windows with reduced information"); + pw.println(" trim: All windows with reduced"); + pw.println(" all: All window and information"); + return -1; } } + private String getStatus() { + return "Status: " + + ((isEnabled()) ? "Enabled" : "Disabled") + + "\n" + + "Log level: " + + mLogLevel + + "\n" + + mBuffer.getStatus(); + } + /** * If tracing is enabled, log the current state or schedule the next frame to be logged, - * according to {@link #mContinuousMode}. + * according to {@link #mLogOnFrame}. * * @param where Logging point descriptor */ @@ -188,7 +238,7 @@ class WindowTracing { return; } - if (mContinuousMode) { + if (mLogOnFrame) { schedule(); } else { log(where); @@ -215,25 +265,24 @@ class WindowTracing { private void log(String where) { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "traceStateLocked"); try { - synchronized (mGlobalLock) { - ProtoOutputStream os = new ProtoOutputStream(); - long tokenOuter = os.start(ENTRY); - os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos()); - os.write(WHERE, where); + ProtoOutputStream os = new ProtoOutputStream(); + long tokenOuter = os.start(ENTRY); + os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos()); + os.write(WHERE, where); + long tokenInner = os.start(WINDOW_MANAGER_SERVICE); + synchronized (mGlobalLock) { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToProtoLocked"); try { - long tokenInner = os.start(WINDOW_MANAGER_SERVICE); - mService.writeToProtoLocked(os, mWindowTraceLogLevel); - os.end(tokenInner); + mService.writeToProtoLocked(os, mLogLevel); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } - os.end(tokenOuter); - mTraceBuffer.add(os); - - mScheduled = false; } + os.end(tokenInner); + os.end(tokenOuter); + mBuffer.add(os); + mScheduled = false; } catch (Exception e) { Log.wtf(TAG, "Exception while tracing state", e); } finally { @@ -242,31 +291,37 @@ class WindowTracing { } /** - * Writes the trace buffer to disk. This method has no internal synchronization and should be - * externally synchronized + * Writes the trace buffer to new file for the bugreport. + * + * This method is synchronized with {@code #startTrace(PrintWriter)} and + * {@link #stopTrace(PrintWriter)}. */ - private void writeTraceToFileLocked() { - if (mTraceBuffer == null) { - return; + void writeTraceToFile() { + synchronized (mEnabledLock) { + writeTraceToFileLocked(); } + } - try { - mTraceBuffer.dump(); - } catch (IOException e) { - Log.e(TAG, "Unable to write buffer to file", e); - } catch (InterruptedException e) { - Log.e(TAG, "Unable to interrupt window tracing file write thread", e); + private void logAndPrintln(@Nullable PrintWriter pw, String msg) { + Log.i(TAG, msg); + if (pw != null) { + pw.println(msg); + pw.flush(); } } /** - * Writes the trace buffer to disk and clones it into a new file for the bugreport. - * This method is synchronized with {@code #startTrace(PrintWriter)} and - * {@link #stopTrace(PrintWriter)}. + * Writes the trace buffer to disk. This method has no internal synchronization and should be + * externally synchronized */ - void writeTraceToFile() { - synchronized (mLock) { - writeTraceToFileLocked(); + private void writeTraceToFileLocked() { + try { + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFileLocked"); + mBuffer.writeTraceToFile(mTraceFile); + } catch (IOException e) { + Log.e(TAG, "Unable to write buffer to file", e); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } } -} +}
\ No newline at end of file diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp index 5c19ad33617c..9cbb58d35b9f 100644 --- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp +++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "BatteryStatsService" //#define LOG_NDEBUG 0 +#include <climits> #include <errno.h> #include <fcntl.h> #include <inttypes.h> @@ -28,6 +29,7 @@ #include <sys/types.h> #include <unistd.h> #include <unordered_map> +#include <utility> #include <android/hardware/power/1.0/IPower.h> #include <android/hardware/power/1.1/IPower.h> @@ -87,6 +89,15 @@ std::function<void(JNIEnv*, jobject)> gGetLowPowerStatsImpl = {}; std::function<jint(JNIEnv*, jobject)> gGetPlatformLowPowerStatsImpl = {}; std::function<jint(JNIEnv*, jobject)> gGetSubsystemLowPowerStatsImpl = {}; +// Cellular/Wifi power monitor rail information +static jmethodID jupdateRailData = NULL; +static jmethodID jsetRailStatsAvailability = NULL; + +std::function<void(JNIEnv*, jobject)> gGetRailEnergyPowerStatsImpl = {}; + +std::unordered_map<uint32_t, std::pair<std::string, std::string>> gPowerStatsHalRailNames = {}; +static bool power_monitor_available = false; + // The caller must be holding gPowerHalMutex. static void deinitPowerStatsLocked() { gPowerStatsHalV1_0 = nullptr; @@ -258,6 +269,7 @@ static bool initializePowerStats() { gPowerStatsHalStateNames.clear(); gPowerStatsHalPlatformIds.clear(); gPowerStatsHalSubsystemIds.clear(); + gPowerStatsHalRailNames.clear(); Return<void> ret; ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) { @@ -301,6 +313,27 @@ static bool initializePowerStats() { return false; } + // Get Power monitor rails available + ret = gPowerStatsHalV1_0->getRailInfo([](auto rails, auto status) { + if (status != Status::SUCCESS) { + ALOGW("Rail information is not available"); + power_monitor_available = false; + return; + } + + // Fill out rail names/subsystems into gPowerStatsHalRailNames + for (auto rail : rails) { + gPowerStatsHalRailNames.emplace(rail.index, + std::make_pair(rail.railName, rail.subsysName)); + } + if (!gPowerStatsHalRailNames.empty()) { + power_monitor_available = true; + } + }); + if (!checkResultLocked(ret, __func__)) { + return false; + } + return (!gPowerStatsHalEntityNames.empty()) && (!gPowerStatsHalStateNames.empty()); } @@ -517,6 +550,50 @@ static jint getPowerStatsHalSubsystemData(JNIEnv* env, jobject outBuf) { return total_added; } +static void getPowerStatsHalRailEnergyData(JNIEnv* env, jobject jrailStats) { + using android::hardware::power::stats::V1_0::Status; + using android::hardware::power::stats::V1_0::EnergyData; + + if (!getPowerStatsHalLocked()) { + ALOGE("failed to get power stats"); + return; + } + + if (!power_monitor_available) { + env->CallVoidMethod(jrailStats, jsetRailStatsAvailability, false); + ALOGW("Rail energy data is not available"); + return; + } + + // Get power rail energySinceBoot data + Return<void> ret = gPowerStatsHalV1_0->getEnergyData({}, + [&env, &jrailStats](auto energyData, auto status) { + if (status == Status::NOT_SUPPORTED) { + ALOGW("getEnergyData is not supported"); + return; + } + + for (auto data : energyData) { + if (!(data.timestamp > LLONG_MAX || data.energy > LLONG_MAX)) { + env->CallVoidMethod(jrailStats, + jupdateRailData, + data.index, + env->NewStringUTF( + gPowerStatsHalRailNames.at(data.index).first.c_str()), + env->NewStringUTF( + gPowerStatsHalRailNames.at(data.index).second.c_str()), + data.timestamp, + data.energy); + } else { + ALOGE("Java long overflow seen. Rail index %d not updated", data.index); + } + } + }); + if (!checkResultLocked(ret, __func__)) { + ALOGE("getEnergyData failed"); + } +} + // The caller must be holding powerHalMutex. static void getPowerHalLowPowerData(JNIEnv* env, jobject jrpmStats) { sp<IPowerV1_0> powerHalV1_0 = getPowerHalV1_0(); @@ -761,11 +838,13 @@ static void setUpPowerStatsLocked() { gGetLowPowerStatsImpl = getPowerStatsHalLowPowerData; gGetPlatformLowPowerStatsImpl = getPowerStatsHalPlatformData; gGetSubsystemLowPowerStatsImpl = getPowerStatsHalSubsystemData; + gGetRailEnergyPowerStatsImpl = getPowerStatsHalRailEnergyData; } else if (android::hardware::power::V1_0::IPower::getService() != nullptr) { ALOGI("Using power HAL"); gGetLowPowerStatsImpl = getPowerHalLowPowerData; gGetPlatformLowPowerStatsImpl = getPowerHalPlatformData; gGetSubsystemLowPowerStatsImpl = getPowerHalSubsystemData; + gGetRailEnergyPowerStatsImpl = NULL; } } @@ -835,11 +914,44 @@ static jint getSubsystemLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject return -1; } +static void getRailEnergyPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrailStats) { + if (jrailStats == NULL) { + jniThrowException(env, "java/lang/NullPointerException", + "The railstats jni input jobject jrailStats is null."); + return; + } + if (jupdateRailData == NULL) { + ALOGE("A railstats jni jmethodID is null."); + return; + } + + std::lock_guard<std::mutex> lock(gPowerHalMutex); + + if (!gGetRailEnergyPowerStatsImpl) { + setUpPowerStatsLocked(); + } + + if (gGetRailEnergyPowerStatsImpl) { + gGetRailEnergyPowerStatsImpl(env, jrailStats); + return; + } + + if (jsetRailStatsAvailability == NULL) { + ALOGE("setRailStatsAvailability jni jmethodID is null."); + return; + } + env->CallVoidMethod(jrailStats, jsetRailStatsAvailability, false); + ALOGE("Unable to load Power.Stats.HAL. Setting rail availability to false"); + return; +} + static const JNINativeMethod method_table[] = { { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup }, { "getLowPowerStats", "(Lcom/android/internal/os/RpmStats;)V", (void*)getLowPowerStats }, { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats }, { "getSubsystemLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getSubsystemLowPowerStats }, + { "getRailEnergyPowerStats", "(Lcom/android/internal/os/RailStats;)V", + (void*)getRailEnergyPowerStats }, }; int register_android_server_BatteryStatsService(JNIEnv *env) @@ -850,8 +962,9 @@ int register_android_server_BatteryStatsService(JNIEnv *env) env->FindClass("com/android/internal/os/RpmStats$PowerStatePlatformSleepState"); jclass clsPowerStateSubsystem = env->FindClass("com/android/internal/os/RpmStats$PowerStateSubsystem"); + jclass clsRailStats = env->FindClass("com/android/internal/os/RailStats"); if (clsRpmStats == NULL || clsPowerStatePlatformSleepState == NULL - || clsPowerStateSubsystem == NULL) { + || clsPowerStateSubsystem == NULL || clsRailStats == NULL) { ALOGE("A rpmstats jni jclass is null."); } else { jgetAndUpdatePlatformState = env->GetMethodID(clsRpmStats, "getAndUpdatePlatformState", @@ -862,6 +975,10 @@ int register_android_server_BatteryStatsService(JNIEnv *env) "(Ljava/lang/String;JI)V"); jputState = env->GetMethodID(clsPowerStateSubsystem, "putState", "(Ljava/lang/String;JI)V"); + jupdateRailData = env->GetMethodID(clsRailStats, "updateRailData", + "(JLjava/lang/String;Ljava/lang/String;JJ)V"); + jsetRailStatsAvailability = env->GetMethodID(clsRailStats, "setRailStatsAvailability", + "(Z)V"); } return jniRegisterNativeMethods(env, "com/android/server/am/BatteryStatsService", diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index a6017f2c1e86..aae159c2edcb 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -36,6 +36,7 @@ import android.content.res.Configuration; import android.content.res.Resources.Theme; import android.database.sqlite.SQLiteCompatibilityWalFlags; import android.database.sqlite.SQLiteGlobal; +import android.net.NetworkStackClient; import android.os.BaseBundle; import android.os.Binder; import android.os.Build; @@ -1350,9 +1351,7 @@ public final class SystemServer { traceBeginAndSlog("StartNetworkStack"); try { - final android.net.NetworkStack networkStack = - context.getSystemService(android.net.NetworkStack.class); - networkStack.start(context); + NetworkStackClient.getInstance().start(context); } catch (Throwable e) { reportWtf("starting Network Stack", e); } diff --git a/services/net/Android.bp b/services/net/Android.bp index 638ec95ec544..8ad4d7679107 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -1,6 +1,10 @@ java_library_static { name: "services.net", srcs: ["java/**/*.java"], + static_libs: [ + "netd_aidl_interface-java", + "networkstack-aidl-interfaces-java", + ] } filegroup { diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java new file mode 100644 index 000000000000..1eb7b98d801a --- /dev/null +++ b/services/net/java/android/net/NetworkStackClient.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.net; + +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; +import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH; +import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.net.dhcp.DhcpServingParamsParcel; +import android.net.dhcp.IDhcpServerCallbacks; +import android.net.ip.IIpClientCallbacks; +import android.os.Binder; +import android.os.IBinder; +import android.os.Process; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; + +/** + * Service used to communicate with the network stack, which is running in a separate module. + * @hide + */ +public class NetworkStackClient { + private static final String TAG = NetworkStackClient.class.getSimpleName(); + + private static final int NETWORKSTACK_TIMEOUT_MS = 10_000; + + private static NetworkStackClient sInstance; + + @NonNull + @GuardedBy("mPendingNetStackRequests") + private final ArrayList<NetworkStackCallback> mPendingNetStackRequests = new ArrayList<>(); + @Nullable + @GuardedBy("mPendingNetStackRequests") + private INetworkStackConnector mConnector; + + private volatile boolean mNetworkStackStartRequested = false; + + private interface NetworkStackCallback { + void onNetworkStackConnected(INetworkStackConnector connector); + } + + private NetworkStackClient() { } + + /** + * Get the NetworkStackClient singleton instance. + */ + public static synchronized NetworkStackClient getInstance() { + if (sInstance == null) { + sInstance = new NetworkStackClient(); + } + return sInstance; + } + + /** + * Create a DHCP server according to the specified parameters. + * + * <p>The server will be returned asynchronously through the provided callbacks. + */ + public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params, + final IDhcpServerCallbacks cb) { + requestConnector(connector -> { + try { + connector.makeDhcpServer(ifName, params, cb); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + }); + } + + /** + * Create an IpClient on the specified interface. + * + * <p>The IpClient will be returned asynchronously through the provided callbacks. + */ + public void makeIpClient(String ifName, IIpClientCallbacks cb) { + requestConnector(connector -> { + try { + connector.makeIpClient(ifName, cb); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + }); + } + + /** + * Create a NetworkMonitor. + * + * <p>The INetworkMonitor will be returned asynchronously through the provided callbacks. + */ + public void makeNetworkMonitor( + NetworkParcelable network, String name, INetworkMonitorCallbacks cb) { + requestConnector(connector -> { + try { + connector.makeNetworkMonitor(network, name, cb); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + }); + } + + private class NetworkStackConnection implements ServiceConnection { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + registerNetworkStackService(service); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + // TODO: crash/reboot the system ? + Slog.wtf(TAG, "Lost network stack connector"); + } + }; + + private void registerNetworkStackService(@NonNull IBinder service) { + final INetworkStackConnector connector = INetworkStackConnector.Stub.asInterface(service); + + ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */, + DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL); + + final ArrayList<NetworkStackCallback> requests; + synchronized (mPendingNetStackRequests) { + requests = new ArrayList<>(mPendingNetStackRequests); + mPendingNetStackRequests.clear(); + mConnector = connector; + } + + for (NetworkStackCallback r : requests) { + r.onNetworkStackConnected(connector); + } + } + + /** + * Start the network stack. Should be called only once on device startup. + * + * <p>This method will start the network stack either in the network stack process, or inside + * the system server on devices that do not support the network stack module. The network stack + * connector will then be delivered asynchronously to clients that requested it before it was + * started. + */ + public void start(Context context) { + mNetworkStackStartRequested = true; + // Try to bind in-process if the library is available + IBinder connector = null; + try { + final Class service = Class.forName( + "com.android.server.NetworkStackService", + true /* initialize */, + context.getClassLoader()); + connector = (IBinder) service.getMethod("makeConnector", Context.class) + .invoke(null, context); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService"); + // TODO: crash/reboot system here ? + return; + } catch (ClassNotFoundException e) { + // Normal behavior if stack is provided by the app: fall through + } + + // In-process network stack. Add the service to the service manager here. + if (connector != null) { + registerNetworkStackService(connector); + return; + } + // Start the network stack process. The service will be added to the service manager in + // NetworkStackConnection.onServiceConnected(). + final Intent intent = new Intent(INetworkStackConnector.class.getName()); + final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0); + intent.setComponent(comp); + + if (comp == null) { + Slog.wtf(TAG, "Could not resolve the network stack with " + intent); + // TODO: crash/reboot system server ? + return; + } + final PackageManager pm = context.getPackageManager(); + int uid = -1; + try { + uid = pm.getPackageUid(comp.getPackageName(), UserHandle.USER_SYSTEM); + } catch (PackageManager.NameNotFoundException e) { + Slog.wtf("Network stack package not found", e); + // Fall through + } + if (uid != Process.NETWORK_STACK_UID) { + throw new SecurityException("Invalid network stack UID: " + uid); + } + + final int hasPermission = + pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName()); + if (hasPermission != PERMISSION_GRANTED) { + throw new SecurityException( + "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK); + } + + if (!context.bindServiceAsUser(intent, new NetworkStackConnection(), + Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) { + Slog.wtf(TAG, + "Could not bind to network stack in-process, or in app with " + intent); + // TODO: crash/reboot system server if no network stack after a timeout ? + } + } + + /** + * For non-system server clients, get the connector registered by the system server. + */ + private INetworkStackConnector getRemoteConnector() { + // Block until the NetworkStack connector is registered in ServiceManager. + // <p>This is only useful for non-system processes that do not have a way to be notified of + // registration completion. Adding a callback system would be too heavy weight considering + // that the connector is registered on boot, so it is unlikely that a client would request + // it before it is registered. + // TODO: consider blocking boot on registration and simplify much of the logic in this class + IBinder connector; + try { + final long before = System.currentTimeMillis(); + while ((connector = ServiceManager.getService(Context.NETWORK_STACK_SERVICE)) == null) { + Thread.sleep(20); + if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) { + Slog.e(TAG, "Timeout waiting for NetworkStack connector"); + return null; + } + } + } catch (InterruptedException e) { + Slog.e(TAG, "Error waiting for NetworkStack connector", e); + return null; + } + + return INetworkStackConnector.Stub.asInterface(connector); + } + + private void requestConnector(@NonNull NetworkStackCallback request) { + // TODO: PID check. + final int caller = Binder.getCallingUid(); + if (caller != Process.SYSTEM_UID && !UserHandle.isSameApp(caller, Process.BLUETOOTH_UID)) { + // Don't even attempt to obtain the connector and give a nice error message + throw new SecurityException( + "Only the system server should try to bind to the network stack."); + } + + if (!mNetworkStackStartRequested) { + // The network stack is not being started in this process, e.g. this process is not + // the system server. Get a remote connector registered by the system server. + final INetworkStackConnector connector = getRemoteConnector(); + synchronized (mPendingNetStackRequests) { + mConnector = connector; + } + request.onNetworkStackConnected(connector); + return; + } + + final INetworkStackConnector connector; + synchronized (mPendingNetStackRequests) { + connector = mConnector; + if (connector == null) { + mPendingNetStackRequests.add(request); + return; + } + } + + request.onNetworkStackConnected(connector); + } +} diff --git a/core/java/android/net/dhcp/DhcpServerCallbacks.java b/services/net/java/android/net/dhcp/DhcpServerCallbacks.java index bb56876c77f5..bb56876c77f5 100644 --- a/core/java/android/net/dhcp/DhcpServerCallbacks.java +++ b/services/net/java/android/net/dhcp/DhcpServerCallbacks.java diff --git a/core/java/android/net/ip/IpClientCallbacks.java b/services/net/java/android/net/ip/IpClientCallbacks.java index db01ae4d4d9c..db01ae4d4d9c 100644 --- a/core/java/android/net/ip/IpClientCallbacks.java +++ b/services/net/java/android/net/ip/IpClientCallbacks.java diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java index 2a2a67a92a86..bf917bf88b2d 100644 --- a/services/net/java/android/net/ip/IpClientUtil.java +++ b/services/net/java/android/net/ip/IpClientUtil.java @@ -23,8 +23,7 @@ import android.content.Context; import android.net.DhcpResultsParcelable; import android.net.LinkProperties; import android.net.LinkPropertiesParcelable; -import android.net.NetworkStack; -import android.net.ip.IIpClientCallbacks; +import android.net.NetworkStackClient; import android.os.ConditionVariable; import java.io.FileDescriptor; @@ -76,30 +75,17 @@ public class IpClientUtil { * * <p>This is a convenience method to allow clients to use {@link IpClientCallbacks} instead of * {@link IIpClientCallbacks}. - * @see {@link NetworkStack#makeIpClient(String, IIpClientCallbacks)} + * @see {@link NetworkStackClient#makeIpClient(String, IIpClientCallbacks)} */ public static void makeIpClient(Context context, String ifName, IpClientCallbacks callback) { - context.getSystemService(NetworkStack.class) - .makeIpClient(ifName, new IpClientCallbacksProxy(callback)); - } - - /** - * Create a new IpClient. - * - * <p>This is a convenience method to allow clients to use {@link IpClientCallbacksProxy} - * instead of {@link IIpClientCallbacks}. - * @see {@link NetworkStack#makeIpClient(String, IIpClientCallbacks)} - */ - public static void makeIpClient( - Context context, String ifName, IpClientCallbacksProxy callback) { - context.getSystemService(NetworkStack.class) - .makeIpClient(ifName, callback); + // TODO: migrate clients and remove context argument + NetworkStackClient.getInstance().makeIpClient(ifName, new IpClientCallbacksProxy(callback)); } /** * Wrapper to relay calls from {@link IIpClientCallbacks} to {@link IpClientCallbacks}. */ - public static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub { + private static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub { protected final IpClientCallbacks mCb; /** diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java index 7910c9a69310..34fc7354d63e 100644 --- a/services/net/java/android/net/ip/IpServer.java +++ b/services/net/java/android/net/ip/IpServer.java @@ -22,7 +22,6 @@ import static android.net.util.NetworkConstants.FF; import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH; import static android.net.util.NetworkConstants.asByte; -import android.content.Context; import android.net.ConnectivityManager; import android.net.INetd; import android.net.INetworkStackStatusCallback; @@ -31,7 +30,7 @@ import android.net.InterfaceConfiguration; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; -import android.net.NetworkStack; +import android.net.NetworkStackClient; import android.net.RouteInfo; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; @@ -132,10 +131,6 @@ public class IpServer extends StateMachine { } public static class Dependencies { - private final Context mContext; - public Dependencies(Context context) { - mContext = context; - } public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) { return new RouterAdvertisementDaemon(ifParams); } @@ -153,7 +148,7 @@ public class IpServer extends StateMachine { */ public void makeDhcpServer(String ifName, DhcpServingParamsParcel params, DhcpServerCallbacks cb) { - mContext.getSystemService(NetworkStack.class).makeDhcpServer(ifName, params, cb); + NetworkStackClient.getInstance().makeDhcpServer(ifName, params, cb); } } diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java index 4bac200a22c6..ebbebcb02923 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java @@ -16,12 +16,12 @@ package com.android.server; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW; +import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; +import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; +import static android.net.INetd.FIREWALL_CHAIN_STANDBY; +import static android.net.INetd.FIREWALL_RULE_ALLOW; +import static android.net.INetd.FIREWALL_RULE_DENY; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY; import static android.util.DebugUtils.valueToString; import static org.junit.Assert.assertEquals; diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java index a3f36b720398..3e5ce46e8e3a 100644 --- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java @@ -57,6 +57,8 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.server.backup.utils.RandomAccessFileUtils; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -125,6 +127,7 @@ public class TrampolineTest { private File mTestDir; private File mSuppressFile; private File mActivatedFile; + private File mRememberActivatedFile; @Before public void setUp() throws Exception { @@ -153,6 +156,8 @@ public class TrampolineTest { mActivatedFile = new File(mTestDir, "activate-" + NON_USER_SYSTEM); TrampolineTestable.sActivatedFiles.append(NON_USER_SYSTEM, mActivatedFile); + mRememberActivatedFile = new File(mTestDir, "rem-activate-" + NON_USER_SYSTEM); + TrampolineTestable.sRememberActivatedFiles.append(NON_USER_SYSTEM, mRememberActivatedFile); mTrampoline = new TrampolineTestable(mContextMock); } @@ -411,6 +416,34 @@ public class TrampolineTest { } @Test + public void setBackupServiceActive_forNonSystemUser_remembersActivated() { + mTrampoline.initializeService(); + + mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true); + + assertTrue(RandomAccessFileUtils.readBoolean(mRememberActivatedFile, false)); + } + + @Test + public void setBackupServiceActiveFalse_forNonSystemUser_remembersActivated() { + mTrampoline.initializeService(); + + mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, false); + + assertFalse(RandomAccessFileUtils.readBoolean(mRememberActivatedFile, true)); + } + + @Test + public void setBackupServiceActiveTwice_forNonSystemUser_remembersLastActivated() { + mTrampoline.initializeService(); + + mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true); + mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, false); + + assertFalse(RandomAccessFileUtils.readBoolean(mRememberActivatedFile, true)); + } + + @Test public void dataChanged_calledBeforeInitialize_ignored() throws Exception { mTrampoline.dataChanged(PACKAGE_NAME); verifyNoMoreInteractions(mBackupManagerServiceMock); @@ -1291,6 +1324,7 @@ public class TrampolineTest { static BackupManagerService sBackupManagerServiceMock = null; static File sSuppressFile = null; static SparseArray<File> sActivatedFiles = new SparseArray<>(); + static SparseArray<File> sRememberActivatedFiles = new SparseArray<>(); static UserManager sUserManagerMock = null; private int mCreateServiceCallsCount = 0; @@ -1314,6 +1348,11 @@ public class TrampolineTest { } @Override + protected File getRememberActivatedFileForNonSystemUser(int userId) { + return sRememberActivatedFiles.get(userId); + } + + @Override protected File getActivatedFileForNonSystemUser(int userId) { return sActivatedFiles.get(userId); } diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java index 0355e84d884d..5cb6cbb93b5b 100644 --- a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java +++ b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java @@ -556,16 +556,6 @@ public class IPackageManagerStub implements IPackageManager { } @Override - public byte[] getPermissionGrantBackup(int userId) throws RemoteException { - return new byte[0]; - } - - @Override - public void restorePermissionGrants(byte[] backup, int userId) throws RemoteException { - - } - - @Override public ComponentName getHomeActivities(List<ResolveInfo> outHomeCandidates) throws RemoteException { return null; diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java new file mode 100644 index 000000000000..eaa9c4520979 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java @@ -0,0 +1,80 @@ +/* + * 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.backup.utils; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.google.common.io.Files; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.IOException; + +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class FileUtilsTest { + private static File sTemporaryDir; + private File mTemporaryFile; + + @BeforeClass + public static void setUpClass() { + sTemporaryDir = Files.createTempDir(); + } + + @AfterClass + public static void tearDownClass() { + if (sTemporaryDir != null) { + sTemporaryDir.delete(); + } + } + + @Before + public void setUp() throws Exception { + mTemporaryFile = new File(sTemporaryDir, "fileutilstest.txt"); + } + + /** Test that if file does not exist, {@link FileUtils#createNewFile()} creates the file. */ + @Test + public void testEnsureFileExists_fileDoesNotAlreadyExist_getsCreated() { + assertThat(!mTemporaryFile.exists()); + + FileUtils.createNewFile(mTemporaryFile); + + assertThat(mTemporaryFile.exists()); + } + + /** Test that if file does exist, {@link FileUtils#createNewFile()} does not error out. */ + @Test + public void testEnsureFileExists_fileAlreadyExists_doesNotErrorOut() throws IOException { + mTemporaryFile.createNewFile(); + + FileUtils.createNewFile(mTemporaryFile); + + assertThat(mTemporaryFile.exists()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java new file mode 100644 index 000000000000..ca699bd7b85c --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java @@ -0,0 +1,114 @@ +/* + * 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.backup.utils; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; + +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class RandomAccessFileUtilsTest { + private File mTemporaryFile; + + @Before + public void setUp() throws Exception { + mTemporaryFile = File.createTempFile("fileutilstest", ".txt"); + } + + @After + public void tearDown() throws Exception { + if (mTemporaryFile != null) { + mTemporaryFile.delete(); + } + } + + /** + * Test that if we write true, we read back true. + */ + @Test + public void testWriteTrue_readReturnsTrue() { + RandomAccessFileUtils.writeBoolean(mTemporaryFile, true); + + assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, false)).isEqualTo(true); + } + + /** + * Test that if we write false, we read back false. + */ + @Test + public void testWriteFalse_readReturnsFalse() { + RandomAccessFileUtils.writeBoolean(mTemporaryFile, false); + + assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, true)).isEqualTo(false); + } + + /** + * Test that if we write true twice, we read back true. + */ + @Test + public void testWriteTrueTwice_readReturnsTrue() { + RandomAccessFileUtils.writeBoolean(mTemporaryFile, true); + RandomAccessFileUtils.writeBoolean(mTemporaryFile, true); + + assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, false)).isEqualTo(true); + } + + /** + * Test that if we write false twice, we read back false. + */ + @Test + public void testWriteFalseTwice_readReturnsFalse() { + RandomAccessFileUtils.writeBoolean(mTemporaryFile, false); + RandomAccessFileUtils.writeBoolean(mTemporaryFile, false); + + assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, true)).isEqualTo(false); + } + + /** + * Test that if we write true and then false, we read back false. + */ + @Test + public void testWriteTrueFalse_readReturnsFalse() { + RandomAccessFileUtils.writeBoolean(mTemporaryFile, true); + RandomAccessFileUtils.writeBoolean(mTemporaryFile, false); + + assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, true)).isEqualTo(false); + } + + /** + * Test that if we write false and then true, we read back true. + */ + @Test + public void testWriteFalseTrue_readReturnsTrue() { + RandomAccessFileUtils.writeBoolean(mTemporaryFile, false); + RandomAccessFileUtils.writeBoolean(mTemporaryFile, true); + + assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, false)).isEqualTo(true); + } +} diff --git a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java index 5900fc57296c..01759d2e8f4a 100644 --- a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java @@ -98,6 +98,8 @@ public class ColorDisplayServiceTest { mColorDisplayService = new ColorDisplayService(mContext); mBinderService = mColorDisplayService.new BinderService(); + LocalServices.addService(ColorDisplayService.ColorDisplayServiceInternal.class, + mColorDisplayService.new ColorDisplayServiceInternal()); } @After @@ -110,6 +112,8 @@ public class ColorDisplayServiceTest { mUserId = UserHandle.USER_NULL; mContext = null; + + LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class); } @AfterClass @@ -979,6 +983,99 @@ public class ColorDisplayServiceTest { assertActiveColorMode(ColorDisplayManager.COLOR_MODE_NATURAL); } + @Test + public void displayWhiteBalance_enable() { + setWhiteBalance(true /* Enable DWB Setting */); + setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */); + mBinderService.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL); + startService(); + assertDwbActive(true); + } + + @Test + public void displayWhiteBalance_disableAfterNightDisplayEnable() { + setWhiteBalance(true /* Enable DWB Setting */); + + startService(); + /* Enable nightlight */ + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */); + + /* Since we are using FakeSettingsProvider which could not trigger observer change, + * force an update here.*/ + mColorDisplayService.updateDisplayWhiteBalanceStatus(); + assertDwbActive(false); + } + + @Test + public void displayWhiteBalance_enableAfterNightDisplayDisable() { + setWhiteBalance(true /* Enable DWB Setting */); + startService(); + /* Enable nightlight */ + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */); + + mColorDisplayService.updateDisplayWhiteBalanceStatus(); + assertDwbActive(false); + + /* Disable nightlight */ + setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */); + mColorDisplayService.updateDisplayWhiteBalanceStatus(); + assertDwbActive(true); + } + + @Test + public void displayWhiteBalance_enableAfterLinearColorMode() { + setWhiteBalance(true /* Enable DWB Setting */); + setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */); + startService(); + mBinderService.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL); + + mColorDisplayService.updateDisplayWhiteBalanceStatus(); + assertDwbActive(true); + } + + @Test + public void displayWhiteBalance_setTemperatureOverMax() { + int max = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMax; + + ColorDisplayService.ColorDisplayServiceInternal cdsInternal = LocalServices.getService( + ColorDisplayService.ColorDisplayServiceInternal.class); + cdsInternal.setDisplayWhiteBalanceColorTemperature(max+1); + + assertWithMessage("Unexpected temperature set") + .that(mColorDisplayService.mDisplayWhiteBalanceTintController.mCurrentColorTemperature) + .isEqualTo(max); + } + + @Test + public void displayWhiteBalance_setTemperatureBelowMin() { + int min = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMin; + + ColorDisplayService.ColorDisplayServiceInternal cdsInternal = LocalServices.getService( + ColorDisplayService.ColorDisplayServiceInternal.class); + cdsInternal.setDisplayWhiteBalanceColorTemperature(min - 1); + + assertWithMessage("Unexpected temperature set") + .that(mColorDisplayService.mDisplayWhiteBalanceTintController.mCurrentColorTemperature) + .isEqualTo(min); + } + + @Test + public void displayWhiteBalance_setValidTemperature() { + int min = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMin; + int max = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMax; + int valToSet = (min + max) / 2; + + ColorDisplayService.ColorDisplayServiceInternal cdsInternal = LocalServices.getService( + ColorDisplayService.ColorDisplayServiceInternal.class); + cdsInternal.setDisplayWhiteBalanceColorTemperature(valToSet); + + assertWithMessage("Unexpected temperature set") + .that(mColorDisplayService.mDisplayWhiteBalanceTintController.mCurrentColorTemperature) + .isEqualTo(valToSet); + } + /** * Configures Night display to use a custom schedule. * @@ -1041,6 +1138,16 @@ public class ColorDisplayServiceTest { } /** + * Configures the Display White Balance setting state. + * + * @param state {@code true} if display white balance should be enabled + */ + private void setWhiteBalance(boolean state) { + Secure.putIntForUser(mContext.getContentResolver(), + Secure.DISPLAY_WHITE_BALANCE_ENABLED, state ? 1 : 0, mUserId); + } + + /** * Configures color mode. */ private void setColorMode(int colorMode) { @@ -1111,6 +1218,17 @@ public class ColorDisplayServiceTest { } /** + * Convenience method for asserting that the DWB active status matches expectation. + * + * @param enabled the expected active status. + */ + private void assertDwbActive(boolean enabled) { + assertWithMessage("Incorrect Display White Balance state") + .that(mColorDisplayService.mDisplayWhiteBalanceTintController.isActivated()) + .isEqualTo(enabled); + } + + /** * Convenience for making a {@link LocalTime} instance with an offset relative to now. * * @param offsetMinutes the offset relative to now (in minutes) diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index 6d28ed19af4f..50734efacac9 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -55,8 +55,8 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.AtomicFile; import com.android.server.LocalServices; -import com.android.server.pm.permission.PermissionManagerInternal; import com.android.server.pm.permission.PermissionManagerService; +import com.android.server.pm.permission.PermissionManagerServiceInternal; import org.junit.After; import org.junit.Before; @@ -88,7 +88,8 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock); + PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null, + lock); Settings settings = new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); @@ -103,7 +104,8 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock); + PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null, + lock); Settings settings = new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); @@ -120,7 +122,8 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock); + PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null, + lock); Settings settings = new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); @@ -143,7 +146,8 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock); + PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null, + lock); Settings settings = new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); @@ -313,7 +317,8 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock); + PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null, + lock); Settings settings = new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); @@ -507,7 +512,8 @@ public class PackageManagerSettingsTests { public void testUpdatePackageSetting03() { final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock); + PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null, + lock); final Settings testSettings01 = new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock); final SharedUserSetting testUserSetting01 = createSharedUserSetting( @@ -625,7 +631,8 @@ public class PackageManagerSettingsTests { public void testCreateNewSetting03() { final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock); + PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null, + lock); final Settings testSettings01 = new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock); final SharedUserSetting testUserSetting01 = createSharedUserSetting( diff --git a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java index 9f1cbcd7ec27..6a937fabd3ec 100644 --- a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java +++ b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java @@ -98,6 +98,15 @@ public class AttentionDetectorTest extends AndroidTestCase { } @Test + public void testUpdateUserActivity_schedulesTheNextCheck() { + long now = SystemClock.uptimeMillis(); + mNextDimming = now; + mAttentionDetector.onUserActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH); + long nextTimeout = mAttentionDetector.updateUserActivity(mNextDimming + 5000L); + assertThat(nextTimeout).isEqualTo(mNextDimming + 5000L); + } + + @Test public void testOnUserActivity_ignoresAfterMaximumExtension() { long now = SystemClock.uptimeMillis(); mAttentionDetector.onUserActivity(now - 15000L, PowerManager.USER_ACTIVITY_EVENT_TOUCH); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java index a7520dcbcff9..2627ec762d7a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java @@ -30,6 +30,7 @@ import static org.junit.Assert.assertTrue; import android.graphics.Rect; import android.os.IBinder; +import android.platform.test.annotations.Presubmit; import android.view.Display; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; @@ -48,6 +49,7 @@ import org.junit.Test; * atest WmTests:AppChangeTransitionTests */ @SmallTest +@Presubmit public class AppChangeTransitionTests extends WindowTestsBase { private TaskStack mStack; diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index cd1320986972..1dd72ec4fd71 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -51,7 +51,6 @@ import android.platform.test.annotations.Presubmit; import android.view.Surface; import android.view.WindowManager; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Before; @@ -142,7 +141,6 @@ public class AppWindowTokenTests extends WindowTestsBase { mToken.removeImmediately(); } - @FlakyTest(detail = "Promote to presubmit when shown to be stable.") @Test public void testLandscapeSeascapeRotationByApp() { // Some plumbing to get the service ready for rotation updates. @@ -303,7 +301,6 @@ public class AppWindowTokenTests extends WindowTestsBase { } @Test - @FlakyTest(detail = "Promote once confirmed non-flaky") public void testStuckExitingWindow() { final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, "closingWindow"); @@ -346,7 +343,6 @@ public class AppWindowTokenTests extends WindowTestsBase { assertNoStartingWindow(mToken); } - @FlakyTest(detail = "Promote to presubmit when shown to be stable.") @Test public void testAddRemoveRace() { // There was once a race condition between adding and removing starting windows diff --git a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java index 3f83caea613a..1e02a12e83db 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java @@ -46,6 +46,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.platform.test.annotations.Presubmit; import android.util.Log; import android.view.IWindowManager; @@ -71,6 +72,7 @@ import java.util.concurrent.TimeUnit; * atest WmTests:AssistDataRequesterTest */ @MediumTest +@Presubmit public class AssistDataRequesterTest extends ActivityTestsBase { private static final String TAG = AssistDataRequesterTest.class.getSimpleName(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java index 198e7ce63f52..b15e99aaa8c2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -60,6 +60,7 @@ import com.android.server.LocalServices; import com.android.server.UiThread; import com.android.server.policy.WindowManagerPolicy; import com.android.server.statusbar.StatusBarManagerInternal; +import com.android.server.wm.utils.WmDisplayCutout; import org.junit.After; import org.junit.Before; @@ -113,6 +114,7 @@ public class DisplayRotationTests { public static void setUpOnce() { sMockWm = mock(WindowManagerService.class); sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class); + sMockWm.mPolicy = mock(WindowManagerPolicy.class); } @Before @@ -807,6 +809,8 @@ public class DisplayRotationTests { mMockDisplayContent = mock(WindowTestUtils.TestDisplayContent.class); mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay; + when(mMockDisplayContent.calculateDisplayCutoutForRotation(anyInt())) + .thenReturn(WmDisplayCutout.NO_CUTOUT); mMockDisplayPolicy = mock(DisplayPolicy.class); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index bc62e8c5ab24..2d906d1683d5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.view.InsetsState.TYPE_IME; import static android.view.InsetsState.TYPE_NAVIGATION_BAR; import static android.view.InsetsState.TYPE_TOP_BAR; +import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static org.junit.Assert.assertEquals; @@ -28,15 +29,33 @@ import static org.junit.Assert.assertNull; import android.platform.test.annotations.Presubmit; import android.view.InsetsSourceControl; import android.view.InsetsState; +import android.view.ViewRootImpl; import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; +import org.junit.AfterClass; +import org.junit.BeforeClass; import org.junit.Test; @SmallTest @Presubmit public class InsetsStateControllerTest extends WindowTestsBase { + private static int sPreviousNewInsetsMode; + + @BeforeClass + public static void setUpOnce() { + // TODO: Make use of SettingsSession when it becomes feasible for this. + sPreviousNewInsetsMode = ViewRootImpl.sNewInsetsMode; + // To let the insets provider control the insets visibility, the insets mode has to be + // NEW_INSETS_MODE_FULL. + ViewRootImpl.sNewInsetsMode = NEW_INSETS_MODE_FULL; + } + + @AfterClass + public static void tearDownOnce() { + ViewRootImpl.sNewInsetsMode = sPreviousNewInsetsMode; + } @Test public void testStripForDispatch_notOwn() { @@ -47,7 +66,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertNotNull(getController().getInsetsForDispatch(app).getSource(TYPE_TOP_BAR)); } - @FlakyTest(bugId = 69229402) + @FlakyTest(detail = "Promote to pre-submit once confirmed stable.") @Test public void testStripForDispatch_own() { final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); @@ -57,7 +76,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertEquals(new InsetsState(), getController().getInsetsForDispatch(topBar)); } - @FlakyTest(bugId = 124088319) + @FlakyTest(detail = "Promote to pre-submit once confirmed stable.") @Test public void testStripForDispatch_navBar() { final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); @@ -69,7 +88,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertEquals(new InsetsState(), getController().getInsetsForDispatch(navBar)); } - @FlakyTest(bugId = 124088319) + @FlakyTest(detail = "Promote to pre-submit once confirmed stable.") @Test public void testBarControllingWinChanged() { final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); @@ -82,7 +101,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertEquals(2, controls.length); } - @FlakyTest(bugId = 124088319) + @FlakyTest(detail = "Promote to pre-submit once confirmed stable.") @Test public void testControlRevoked() { final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); diff --git a/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java b/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java index ce22788f7cb9..df26679dc1fb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java @@ -34,6 +34,7 @@ import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.UserHandle; +import android.platform.test.annotations.Presubmit; import android.util.SparseBooleanArray; import com.android.server.wm.LockTaskController.LockTaskToken; @@ -43,6 +44,7 @@ import org.junit.Test; import java.lang.reflect.Constructor; +@Presubmit public class KeyguardDisableHandlerTest { private KeyguardDisableHandler mKeyguardDisable; diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index cc6a58a81635..763ea6293fcc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -112,8 +112,8 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { } } - @FlakyTest(bugId = 117117823) @Test + @FlakyTest(bugId = 117117823) public void testIncludedApps_expectTargetAndVisible() { mWm.setRecentsAnimationController(mController); final AppWindowToken homeAppWindow = createAppWindowToken(mDisplayContent, diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java index c595868db484..2377df406fbc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java @@ -34,10 +34,12 @@ import static org.junit.Assert.assertTrue; import android.app.IActivityTaskManager; import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Before; @@ -50,6 +52,8 @@ import org.junit.Test; * atest WmTests:TaskPositionerTests */ @SmallTest +@Presubmit +@FlakyTest public class TaskPositionerTests extends WindowTestsBase { private static final boolean DEBUGGING = false; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java index 2b8e307e23b7..b299f0dd7253 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java @@ -36,11 +36,10 @@ import org.junit.Before; import org.junit.Test; import java.io.File; -import java.io.IOException; /** - * Test class for {@link WindowTraceBuffer} and {@link WindowTraceQueueBuffer}. + * Test class for {@link WindowTraceBuffer}. * * Build/Install/Run: * atest WmTests:WindowTraceBufferTest @@ -49,12 +48,15 @@ import java.io.IOException; @Presubmit public class WindowTraceBufferTest { private File mFile; + private WindowTraceBuffer mBuffer; @Before public void setUp() throws Exception { final Context testContext = getInstrumentation().getContext(); mFile = testContext.getFileStreamPath("tracing_test.dat"); mFile.delete(); + + mBuffer = new WindowTraceBuffer(10); } @After @@ -63,145 +65,112 @@ public class WindowTraceBufferTest { } @Test - public void testTraceQueueBuffer_addItem() throws Exception { - ProtoOutputStream toWrite1 = getDummy(1); - ProtoOutputStream toWrite2 = getDummy(2); - ProtoOutputStream toWrite3 = getDummy(3); - final int objectSize = toWrite1.getRawSize(); - final int bufferCapacity = objectSize * 2; - - final WindowTraceBuffer buffer = buildQueueBuffer(bufferCapacity); - - buffer.add(toWrite1); - byte[] toWrite1Bytes = toWrite1.getBytes(); - assertTrue("First element should be in the list", - buffer.contains(toWrite1Bytes)); - - buffer.add(toWrite2); - byte[] toWrite2Bytes = toWrite2.getBytes(); - assertTrue("First element should be in the list", - buffer.contains(toWrite1Bytes)); - assertTrue("Second element should be in the list", - buffer.contains(toWrite2Bytes)); - - buffer.add(toWrite3); - byte[] toWrite3Bytes = toWrite3.getBytes(); - assertTrue("First element should be in the list", - buffer.contains(toWrite1Bytes)); - assertTrue("Second element should be in the list", - buffer.contains(toWrite2Bytes)); - assertTrue("Third element should not be in the list", - !buffer.contains(toWrite3Bytes)); - - assertEquals("Buffer should have 2 elements", buffer.mBuffer.size(), 2); - assertEquals(String.format("Buffer is full, used space should be %d", bufferCapacity), - buffer.mBufferSize, bufferCapacity); - assertEquals("Buffer is full, available space should be 0", - buffer.getAvailableSpace(), 0); - } - - @Test - public void testTraceRingBuffer_addItem() throws Exception { + public void test_addItem() { ProtoOutputStream toWrite = getDummy(1); final int objectSize = toWrite.getRawSize(); + mBuffer.setCapacity(objectSize); + mBuffer.resetBuffer(); - final WindowTraceBuffer buffer = buildRingBuffer(objectSize); - - Preconditions.checkArgument(buffer.mBuffer.isEmpty()); + Preconditions.checkArgument(mBuffer.size() == 0); - buffer.add(toWrite); + mBuffer.add(toWrite); - assertEquals("Item was not added to the buffer", buffer.mBuffer.size(), 1); + assertEquals("Item was not added to the buffer", 1, mBuffer.size()); assertEquals("Total buffer getSize differs from inserted object", - buffer.mBufferSize, objectSize); - assertEquals("Available buffer space does not match used one", - buffer.getAvailableSpace(), 0); + mBuffer.getBufferSize(), objectSize); + assertEquals("Available buffer space does not match used one", 0, + mBuffer.getAvailableSpace()); } @Test - public void testTraceRingBuffer_addItemMustOverwriteOne() throws Exception { + public void test_addItemMustOverwriteOne() { ProtoOutputStream toWrite1 = getDummy(1); ProtoOutputStream toWrite2 = getDummy(2); ProtoOutputStream toWrite3 = getDummy(3); final int objectSize = toWrite1.getRawSize(); - final int bufferCapacity = objectSize * 2 + 1; - final WindowTraceBuffer buffer = buildRingBuffer(bufferCapacity); + mBuffer.setCapacity(bufferCapacity); + mBuffer.resetBuffer(); - buffer.add(toWrite1); + mBuffer.add(toWrite1); byte[] toWrite1Bytes = toWrite1.getBytes(); assertTrue("First element should be in the list", - buffer.contains(toWrite1Bytes)); + mBuffer.contains(toWrite1Bytes)); - buffer.add(toWrite2); + mBuffer.add(toWrite2); byte[] toWrite2Bytes = toWrite2.getBytes(); assertTrue("First element should be in the list", - buffer.contains(toWrite1Bytes)); + mBuffer.contains(toWrite1Bytes)); assertTrue("Second element should be in the list", - buffer.contains(toWrite2Bytes)); + mBuffer.contains(toWrite2Bytes)); - buffer.add(toWrite3); + mBuffer.add(toWrite3); byte[] toWrite3Bytes = toWrite3.getBytes(); assertTrue("First element should not be in the list", - !buffer.contains(toWrite1Bytes)); + !mBuffer.contains(toWrite1Bytes)); assertTrue("Second element should be in the list", - buffer.contains(toWrite2Bytes)); + mBuffer.contains(toWrite2Bytes)); assertTrue("Third element should be in the list", - buffer.contains(toWrite3Bytes)); - assertEquals("Buffer should have 2 elements", buffer.mBuffer.size(), 2); + mBuffer.contains(toWrite3Bytes)); + assertEquals("Buffer should have 2 elements", 2, mBuffer.size()); assertEquals(String.format("Buffer is full, used space should be %d", bufferCapacity), - buffer.mBufferSize, bufferCapacity - 1); - assertEquals(" Buffer is full, available space should be 0", - buffer.getAvailableSpace(), 1); + mBuffer.getBufferSize(), bufferCapacity - 1); + assertEquals(" Buffer is full, available space should be 0", 1, + mBuffer.getAvailableSpace()); } @Test - public void testTraceRingBuffer_addItemMustOverwriteMultiple() throws Exception { + public void test_addItemMustOverwriteMultiple() { ProtoOutputStream toWriteSmall1 = getDummy(1); ProtoOutputStream toWriteSmall2 = getDummy(2); final int objectSize = toWriteSmall1.getRawSize(); - final int bufferCapacity = objectSize * 2; - final WindowTraceBuffer buffer = buildRingBuffer(bufferCapacity); + mBuffer.setCapacity(bufferCapacity); + mBuffer.resetBuffer(); ProtoOutputStream toWriteBig = new ProtoOutputStream(); toWriteBig.write(MAGIC_NUMBER, 1); toWriteBig.write(MAGIC_NUMBER, 2); - buffer.add(toWriteSmall1); + mBuffer.add(toWriteSmall1); byte[] toWriteSmall1Bytes = toWriteSmall1.getBytes(); assertTrue("First element should be in the list", - buffer.contains(toWriteSmall1Bytes)); + mBuffer.contains(toWriteSmall1Bytes)); - buffer.add(toWriteSmall2); + mBuffer.add(toWriteSmall2); byte[] toWriteSmall2Bytes = toWriteSmall2.getBytes(); assertTrue("First element should be in the list", - buffer.contains(toWriteSmall1Bytes)); + mBuffer.contains(toWriteSmall1Bytes)); assertTrue("Second element should be in the list", - buffer.contains(toWriteSmall2Bytes)); + mBuffer.contains(toWriteSmall2Bytes)); - buffer.add(toWriteBig); + mBuffer.add(toWriteBig); byte[] toWriteBigBytes = toWriteBig.getBytes(); assertTrue("Third element should overwrite all others", - !buffer.contains(toWriteSmall1Bytes)); + !mBuffer.contains(toWriteSmall1Bytes)); assertTrue("Third element should overwrite all others", - !buffer.contains(toWriteSmall2Bytes)); + !mBuffer.contains(toWriteSmall2Bytes)); assertTrue("Third element should overwrite all others", - buffer.contains(toWriteBigBytes)); + mBuffer.contains(toWriteBigBytes)); - assertEquals(" Buffer should have only 1 big element", buffer.mBuffer.size(), 1); + assertEquals(" Buffer should have only 1 big element", 1, mBuffer.size()); assertEquals(String.format(" Buffer is full, used space should be %d", bufferCapacity), - buffer.mBufferSize, bufferCapacity); - assertEquals(" Buffer is full, available space should be 0", - buffer.getAvailableSpace(), 0); + mBuffer.getBufferSize(), bufferCapacity); + assertEquals(" Buffer is full, available space should be 0", 0, + mBuffer.getAvailableSpace()); } - private WindowTraceBuffer buildRingBuffer(int capacity) throws IOException { - return new WindowTraceBuffer.Builder() - .setContinuousMode(true) - .setBufferCapacity(capacity) - .setTraceFile(mFile) - .build(); + @Test + public void test_startResetsBuffer() { + ProtoOutputStream toWrite = getDummy(1); + mBuffer.resetBuffer(); + Preconditions.checkArgument(mBuffer.size() == 0); + + mBuffer.add(toWrite); + assertEquals("Item was not added to the buffer", 1, mBuffer.size()); + mBuffer.resetBuffer(); + assertEquals("Buffer should be empty after reset", 0, mBuffer.size()); + assertEquals("Buffer size should be 0 after reset", 0, mBuffer.getBufferSize()); } private ProtoOutputStream getDummy(int value) { @@ -212,7 +181,4 @@ public class WindowTraceBufferTest { return toWrite; } - private WindowTraceBuffer buildQueueBuffer(int size) throws IOException { - return new WindowTraceQueueBuffer(size, mFile, false); - } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java index 3c6e2405adff..8358fdd18e0e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java @@ -88,8 +88,7 @@ public class WindowTracingTest { mFile.delete(); mWindowTracing = new WindowTracing(mFile, mWmMock, mChoreographer, - new WindowManagerGlobalLock()); - mWindowTracing.setContinuousMode(false /* continuous */, null /* pw */); + new WindowManagerGlobalLock(), 1024); } @After @@ -103,13 +102,13 @@ public class WindowTracingTest { } @Test - public void isEnabled_returnsTrueAfterStart() throws Exception { + public void isEnabled_returnsTrueAfterStart() { mWindowTracing.startTrace(mock(PrintWriter.class)); assertTrue(mWindowTracing.isEnabled()); } @Test - public void isEnabled_returnsFalseAfterStop() throws Exception { + public void isEnabled_returnsFalseAfterStop() { mWindowTracing.startTrace(mock(PrintWriter.class)); mWindowTracing.stopTrace(mock(PrintWriter.class)); assertFalse(mWindowTracing.isEnabled()); @@ -133,6 +132,8 @@ public class WindowTracingTest { mWindowTracing.startTrace(mock(PrintWriter.class)); mWindowTracing.stopTrace(mock(PrintWriter.class)); + assertTrue("Trace file should exist", mFile.exists()); + byte[] header = new byte[MAGIC_HEADER.length]; try (InputStream is = new FileInputStream(mFile)) { assertEquals(MAGIC_HEADER.length, is.read(header)); diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java index 649b785c992b..99ceb2011db3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java @@ -31,6 +31,7 @@ import static org.junit.Assert.*; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.PointF; +import android.platform.test.annotations.Presubmit; import android.view.DisplayInfo; import org.junit.Before; @@ -42,6 +43,7 @@ import org.junit.rules.ErrorCollector; * Build/Install/Run: * atest WmTests:CoordinateTransformsTest */ +@Presubmit public class CoordinateTransformsTest { private static final int W = 200; diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java index f1ddfe4cd0d5..8feed7fdb785 100644 --- a/services/usage/java/com/android/server/usage/IntervalStats.java +++ b/services/usage/java/com/android/server/usage/IntervalStats.java @@ -15,7 +15,6 @@ */ package com.android.server.usage; -import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED; import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED; import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED; import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED; @@ -302,27 +301,6 @@ public class IntervalStats { UsageStats usageStats = packageStats.valueAt(i); usageStats.update(null, timeStamp, eventType, instanceId); } - } else if (eventType == ACTIVITY_DESTROYED) { - UsageStats usageStats = packageStats.get(packageName); - if (usageStats != null) { - // If previous event is not ACTIVITY_STOPPED, convert ACTIVITY_DESTROYED - // to ACTIVITY_STOPPED and add to event list. - // Otherwise do not add anything to event list. (Because we want to save space - // and we do not want a ACTIVITY_STOPPED followed by - // ACTIVITY_DESTROYED in event list). - final int index = usageStats.mActivities.indexOfKey(instanceId); - if (index >= 0) { - final int type = usageStats.mActivities.valueAt(index); - if (type != ACTIVITY_STOPPED) { - Event event = new Event(ACTIVITY_STOPPED, timeStamp); - event.mPackage = packageName; - event.mClass = className; - event.mInstanceId = instanceId; - addEvent(event); - } - } - usageStats.update(className, timeStamp, ACTIVITY_DESTROYED, instanceId); - } } else { UsageStats usageStats = getOrCreateUsageStats(packageName); usageStats.update(className, timeStamp, eventType, instanceId); diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index af5278f222b8..ebb0210cb553 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -145,8 +145,16 @@ public class UsageStatsService extends SystemService implements AppTimeLimitController mAppTimeLimit; final SparseArray<ArraySet<String>> mUsageReporters = new SparseArray(); - final SparseArray<String> mVisibleActivities = new SparseArray(); + final SparseArray<ActivityData> mVisibleActivities = new SparseArray(); + private static class ActivityData { + private final String mTaskRootPackage; + private final String mTaskRootClass; + private ActivityData(String taskRootPackage, String taskRootClass) { + mTaskRootPackage = taskRootPackage; + mTaskRootClass = taskRootClass; + } + } private UsageStatsManagerInternal.AppIdleStateChangeListener mStandbyChangeListener = new UsageStatsManagerInternal.AppIdleStateChangeListener() { @@ -464,47 +472,57 @@ public class UsageStatsService extends SystemService implements final long elapsedRealtime = SystemClock.elapsedRealtime(); convertToSystemTimeLocked(event); - if (event.getPackageName() != null - && mPackageManagerInternal.isPackageEphemeral(userId, event.getPackageName())) { + if (event.mPackage != null + && mPackageManagerInternal.isPackageEphemeral(userId, event.mPackage)) { event.mFlags |= Event.FLAG_IS_PACKAGE_INSTANT_APP; } - final UserUsageStatsService service = - getUserDataAndInitializeIfNeededLocked(userId, timeNow); - service.reportEvent(event); - - mAppStandby.reportEvent(event, elapsedRealtime, userId); - - String packageName; - - switch(mUsageSource) { - case USAGE_SOURCE_CURRENT_ACTIVITY: - packageName = event.getPackageName(); - break; - case USAGE_SOURCE_TASK_ROOT_ACTIVITY: - default: - packageName = event.getTaskRootPackageName(); - if (packageName == null) { - packageName = event.getPackageName(); - } - break; - } - switch (event.mEventType) { case Event.ACTIVITY_RESUMED: - synchronized (mVisibleActivities) { - // check if this activity has already been resumed - if (mVisibleActivities.get(event.mInstanceId) != null) break; - mVisibleActivities.put(event.mInstanceId, event.getClassName()); - try { - mAppTimeLimit.noteUsageStart(packageName, userId); - } catch (IllegalArgumentException iae) { - Slog.e(TAG, "Failed to note usage start", iae); + // check if this activity has already been resumed + if (mVisibleActivities.get(event.mInstanceId) != null) break; + mVisibleActivities.put(event.mInstanceId, + new ActivityData(event.mTaskRootPackage, event.mTaskRootClass)); + try { + switch(mUsageSource) { + case USAGE_SOURCE_CURRENT_ACTIVITY: + mAppTimeLimit.noteUsageStart(event.mPackage, userId); + break; + case USAGE_SOURCE_TASK_ROOT_ACTIVITY: + default: + mAppTimeLimit.noteUsageStart(event.mTaskRootPackage, userId); + break; + } + } catch (IllegalArgumentException iae) { + Slog.e(TAG, "Failed to note usage start", iae); + } + break; + case Event.ACTIVITY_PAUSED: + if (event.mTaskRootPackage == null) { + // Task Root info is missing. Repair the event based on previous data + final ActivityData prevData = mVisibleActivities.get(event.mInstanceId); + if (prevData == null) { + Slog.w(TAG, "Unexpected activity event reported! (" + event.mPackage + + "/" + event.mClass + " event : " + event.mEventType + + " instanceId : " + event.mInstanceId + ")"); + } else { + event.mTaskRootPackage = prevData.mTaskRootPackage; + event.mTaskRootClass = prevData.mTaskRootClass; } } break; - case Event.ACTIVITY_STOPPED: case Event.ACTIVITY_DESTROYED: + // Treat activity destroys like activity stops. + event.mEventType = Event.ACTIVITY_STOPPED; + // Fallthrough + case Event.ACTIVITY_STOPPED: + final ActivityData prevData = + mVisibleActivities.removeReturnOld(event.mInstanceId); + if (prevData == null) { + // The activity stop was already handled. + return; + } + ArraySet<String> tokens; synchronized (mUsageReporters) { tokens = mUsageReporters.removeReturnOld(event.mInstanceId); @@ -517,7 +535,7 @@ public class UsageStatsService extends SystemService implements final String token = tokens.valueAt(i); try { mAppTimeLimit.noteUsageStop( - buildFullToken(event.getPackageName(), token), userId); + buildFullToken(event.mPackage, token), userId); } catch (IllegalArgumentException iae) { Slog.w(TAG, "Failed to stop usage for during reporter death: " + iae); @@ -525,18 +543,32 @@ public class UsageStatsService extends SystemService implements } } } - - synchronized (mVisibleActivities) { - if (mVisibleActivities.removeReturnOld(event.mInstanceId) != null) { - try { - mAppTimeLimit.noteUsageStop(packageName, userId); - } catch (IllegalArgumentException iae) { - Slog.w(TAG, "Failed to note usage stop", iae); - } + if (event.mTaskRootPackage == null) { + // Task Root info is missing. Repair the event based on previous data + event.mTaskRootPackage = prevData.mTaskRootPackage; + event.mTaskRootClass = prevData.mTaskRootClass; + } + try { + switch(mUsageSource) { + case USAGE_SOURCE_CURRENT_ACTIVITY: + mAppTimeLimit.noteUsageStop(event.mPackage, userId); + break; + case USAGE_SOURCE_TASK_ROOT_ACTIVITY: + default: + mAppTimeLimit.noteUsageStop(event.mTaskRootPackage, userId); + break; } + } catch (IllegalArgumentException iae) { + Slog.w(TAG, "Failed to note usage stop", iae); } break; } + + final UserUsageStatsService service = + getUserDataAndInitializeIfNeededLocked(userId, timeNow); + service.reportEvent(event); + + mAppStandby.reportEvent(event, elapsedRealtime, userId); } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 93f758c2a9ad..b9440ebd88f6 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -1193,7 +1193,7 @@ public class VoiceInteractionManagerService extends SystemService { } @Override - public void setTranscription(IVoiceInteractionService service, String transcription) { + public void setUiHints(IVoiceInteractionService service, Bundle hints) { synchronized (this) { enforceIsCurrentVoiceInteractionService(service); @@ -1202,47 +1202,9 @@ public class VoiceInteractionManagerService extends SystemService { final IVoiceInteractionSessionListener listener = mVoiceInteractionSessionListeners.getBroadcastItem(i); try { - listener.onTranscriptionUpdate(transcription); + listener.onSetUiHints(hints); } catch (RemoteException e) { - Slog.e(TAG, "Error delivering voice transcription.", e); - } - } - mVoiceInteractionSessionListeners.finishBroadcast(); - } - } - - @Override - public void clearTranscription(IVoiceInteractionService service, boolean immediate) { - synchronized (this) { - enforceIsCurrentVoiceInteractionService(service); - - final int size = mVoiceInteractionSessionListeners.beginBroadcast(); - for (int i = 0; i < size; ++i) { - final IVoiceInteractionSessionListener listener = - mVoiceInteractionSessionListeners.getBroadcastItem(i); - try { - listener.onTranscriptionComplete(immediate); - } catch (RemoteException e) { - Slog.e(TAG, "Error delivering transcription complete event.", e); - } - } - mVoiceInteractionSessionListeners.finishBroadcast(); - } - } - - @Override - public void setVoiceState(IVoiceInteractionService service, int state) { - synchronized (this) { - enforceIsCurrentVoiceInteractionService(service); - - final int size = mVoiceInteractionSessionListeners.beginBroadcast(); - for (int i = 0; i < size; ++i) { - final IVoiceInteractionSessionListener listener = - mVoiceInteractionSessionListeners.getBroadcastItem(i); - try { - listener.onVoiceStateChange(state); - } catch (RemoteException e) { - Slog.e(TAG, "Error delivering voice state change.", e); + Slog.e(TAG, "Error delivering UI hints.", e); } } mVoiceInteractionSessionListeners.finishBroadcast(); diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index bd0d4ae27800..ae12a17e1e2f 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -623,7 +623,7 @@ public abstract class Connection extends Conferenceable { "android.telecom.event.HANDOVER_FAILED"; /** - * Connection extra key used to store SIP invite fields for an incoming call for IMS calls + * String Connection extra key used to store SIP invite fields for an incoming call for IMS call */ public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE"; diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java index a1c32b5f80a3..2462beeb28a8 100644 --- a/telephony/java/android/provider/Telephony.java +++ b/telephony/java/android/provider/Telephony.java @@ -2123,6 +2123,11 @@ public final class Telephony { * @hide - not meant for public use */ public interface RcsColumns { + // TODO(sahinc): Turn this to true once the schema finalizes, so that people can update + // their messaging databases. NOTE: move the switch/case update in MmsSmsDatabaseHelper to + // the latest version of the database before turning this flag to true. + boolean IS_RCS_TABLE_SCHEMA_CODE_COMPLETE = false; + /** * The authority for the content provider */ diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index 099015fd3d0e..2aa4768d3f00 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -981,4 +981,13 @@ public class SmsMessage { return false; } + + /** + * {@hide} + * Returns the recipient address(receiver) of this SMS message in String form or null if + * unavailable. + */ + public String getRecipientAddress() { + return mWrappedSmsMessage.getRecipientAddress(); + } } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 836a50bf05c7..94f26a8a8d61 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1236,7 +1236,7 @@ public class SubscriptionManager { @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public List<SubscriptionInfo> getActiveSubscriptionInfoList() { - return getActiveSubscriptionInfoList(false); + return getActiveSubscriptionInfoList(/* userVisibleonly */true); } /** @@ -2858,15 +2858,24 @@ public class SubscriptionManager { /** * Whether system UI should hide a subscription. If it's a bundled opportunistic * subscription, it shouldn't show up in anywhere in Settings app, dialer app, - * or status bar. + * or status bar. Exception is if caller is carrier app, in which case they will + * want to see their own hidden subscriptions. * * @param info the subscriptionInfo to check against. * @return true if this subscription should be hidden. * * @hide */ - public static boolean shouldHideSubscription(SubscriptionInfo info) { - return (info != null && !TextUtils.isEmpty(info.getGroupUuid()) && info.isOpportunistic()); + private boolean shouldHideSubscription(SubscriptionInfo info) { + if (info == null) return false; + + // If hasCarrierPrivileges or canManageSubscription returns true, it means caller + // has carrier privilege. + boolean hasCarrierPrivilegePermission = (info.isEmbedded() && canManageSubscription(info)) + || TelephonyManager.from(mContext).hasCarrierPrivileges(info.getSubscriptionId()); + + return (!TextUtils.isEmpty(info.getGroupUuid()) && info.isOpportunistic() + && !hasCarrierPrivilegePermission); } /** diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java index 73f055649b3f..d5c70793d3bc 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -266,6 +266,11 @@ public final class ImsCallProfile implements Parcelable { public static final String EXTRA_DISPLAY_TEXT = "DisplayText"; public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo"; public static final String EXTRA_IS_CALL_PULL = "CallPull"; + + /** + * String extra property + * Containing fields from the SIP INVITE message for an IMS call + */ public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS = "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS"; diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java index 190eac4d8c02..ffdc4b676f90 100644 --- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java +++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java @@ -41,6 +41,9 @@ public abstract class SmsMessageBase { @UnsupportedAppUsage protected SmsAddress mOriginatingAddress; + /** {@hide} The address of the receiver */ + protected SmsAddress mRecipientAddress; + /** {@hide} The message body as a string. May be null if the message isn't text */ @UnsupportedAppUsage protected String mMessageBody; @@ -457,4 +460,17 @@ public abstract class SmsMessageBase { return ted; } + + /** + * {@hide} + * Returns the receiver address of this SMS message in String + * form or null if unavailable + */ + public String getRecipientAddress() { + if (mRecipientAddress == null) { + return null; + } + + return mRecipientAddress.getAddressString(); + } } diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java index 1da5eac27002..a31fa0b6a725 100644 --- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -601,18 +601,24 @@ public class SmsMessage extends SmsMessageBase { } else if (addr.numberMode == CdmaSmsAddress.NUMBER_MODE_DATA_NETWORK) { if (numberType == 2) - Rlog.e(LOG_TAG, "TODO: Originating Addr is email id"); + Rlog.e(LOG_TAG, "TODO: Addr is email id"); else Rlog.e(LOG_TAG, - "TODO: Originating Addr is data network address"); + "TODO: Addr is data network address"); } else { - Rlog.e(LOG_TAG, "Originating Addr is of incorrect type"); + Rlog.e(LOG_TAG, "Addr is of incorrect type"); } } else { Rlog.e(LOG_TAG, "Incorrect Digit mode"); } addr.origBytes = data; - Rlog.i(LOG_TAG, "Originating Addr=" + addr.toString()); + Rlog.pii(LOG_TAG, "Addr=" + addr.toString()); + mOriginatingAddress = addr; + if (parameterId == DESTINATION_ADDRESS) { + // Original address awlays indicates one sender's address for 3GPP2 + // Here add recipient address support along with 3GPP + mRecipientAddress = addr; + } break; case ORIGINATING_SUB_ADDRESS: case DESTINATION_SUB_ADDRESS: @@ -667,7 +673,7 @@ public class SmsMessage extends SmsMessageBase { } /** - * Parses a SMS message from its BearerData stream. (mobile-terminated only) + * Parses a SMS message from its BearerData stream. */ public void parseSms() { // Message Waiting Info Record defined in 3GPP2 C.S-0005, 3.7.5.6 @@ -697,16 +703,15 @@ public class SmsMessage extends SmsMessageBase { } if (mOriginatingAddress != null) { - mOriginatingAddress.address = new String(mOriginatingAddress.origBytes); - if (mOriginatingAddress.ton == CdmaSmsAddress.TON_INTERNATIONAL_OR_IP) { - if (mOriginatingAddress.address.charAt(0) != '+') { - mOriginatingAddress.address = "+" + mOriginatingAddress.address; - } - } + decodeSmsDisplayAddress(mOriginatingAddress); if (VDBG) Rlog.v(LOG_TAG, "SMS originating address: " + mOriginatingAddress.address); } + if (mRecipientAddress != null) { + decodeSmsDisplayAddress(mRecipientAddress); + } + if (mBearerData.msgCenterTimeStamp != null) { mScTimeMillis = mBearerData.msgCenterTimeStamp.toMillis(true); } @@ -731,7 +736,8 @@ public class SmsMessage extends SmsMessageBase { status = mBearerData.errorClass << 8; status |= mBearerData.messageStatus; } - } else if (mBearerData.messageType != BearerData.MESSAGE_TYPE_DELIVER) { + } else if (mBearerData.messageType != BearerData.MESSAGE_TYPE_DELIVER + && mBearerData.messageType != BearerData.MESSAGE_TYPE_SUBMIT) { throw new RuntimeException("Unsupported message type: " + mBearerData.messageType); } @@ -743,6 +749,16 @@ public class SmsMessage extends SmsMessageBase { } } + private void decodeSmsDisplayAddress(SmsAddress addr) { + addr.address = new String(addr.origBytes); + if (addr.ton == CdmaSmsAddress.TON_INTERNATIONAL_OR_IP) { + if (addr.address.charAt(0) != '+') { + addr.address = "+" + addr.address; + } + } + Rlog.pii(LOG_TAG, " decodeSmsDisplayAddress = " + addr.address); + } + /** * Parses a broadcast SMS, possibly containing a CMAS alert. * diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java index 015efa6a0c7d..19465a44e4e8 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java @@ -71,9 +71,6 @@ public class SmsMessage extends SmsMessageBase { // e.g. 23.040 9.2.2.1 private boolean mReplyPathPresent = false; - /** The address of the receiver. */ - private GsmSmsAddress mRecipientAddress; - /** * TP-Status - status of a previously submitted SMS. * This field applies to SMS-STATUS-REPORT messages. 0 indicates success; diff --git a/test-mock/api/test-current.txt b/test-mock/api/test-current.txt index ab10800ac534..0cb8f22d8070 100644 --- a/test-mock/api/test-current.txt +++ b/test-mock/api/test-current.txt @@ -9,10 +9,12 @@ package android.test.mock { method public java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int); method public String[] getNamesForUids(int[]); method public String getPermissionControllerPackageName(); + method public int getPermissionFlags(String, String, android.os.UserHandle); method @NonNull public String getServicesSystemSharedLibraryPackageName(); method @NonNull public String getSharedSystemSharedLibraryPackageName(); method public void grantRuntimePermission(String, String, android.os.UserHandle); method public void revokeRuntimePermission(String, String, android.os.UserHandle); + method public void updatePermissionFlags(String, String, int, int, android.os.UserHandle); } } diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index 1a4ec94d77b4..7b8c154dea1e 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -1028,8 +1028,28 @@ </activity> <activity - android:name="PositionListenerActivity" - android:label="RenderNode/PositionListener" + android:name="PositionListenerActivity" + android:label="RenderNode/PositionListener" + android:screenOrientation="fullSensor"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.android.test.hwui.TEST" /> + </intent-filter> + </activity> + + <activity + android:name="CustomRenderer" + android:label="HardwareRenderer/HelloTakeSurface" + android:screenOrientation="fullSensor"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.android.test.hwui.TEST" /> + </intent-filter> + </activity> + + <activity + android:name="MyLittleTextureView" + android:label="HardwareRenderer/MyLittleTextureView" android:screenOrientation="fullSensor"> <intent-filter> <action android:name="android.intent.action.MAIN" /> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java new file mode 100644 index 000000000000..60bd60f0bfd1 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java @@ -0,0 +1,76 @@ +/* + * 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.test.hwui; + +import android.app.Activity; +import android.graphics.Color; +import android.graphics.HardwareRenderer; +import android.graphics.Paint; +import android.graphics.RecordingCanvas; +import android.graphics.RenderNode; +import android.os.Bundle; +import android.util.Log; +import android.view.SurfaceHolder; + +public class CustomRenderer extends Activity { + private RenderNode mContent = new RenderNode("CustomRenderer"); + private HardwareRenderer mRenderer = new HardwareRenderer(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().takeSurface(mSurfaceCallbacks); + } + + private SurfaceHolder.Callback2 mSurfaceCallbacks = new SurfaceHolder.Callback2() { + + @Override + public void surfaceRedrawNeeded(SurfaceHolder holder) { + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + mContent.setLeftTopRightBottom(0, 0, width, height); + RecordingCanvas canvas = mContent.startRecording(); + canvas.drawColor(Color.WHITE); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setColor(Color.BLACK); + paint.setTextAlign(Paint.Align.CENTER); + paint.setTextSize(Math.min(width, height) * .05f); + canvas.drawText("Hello custom renderer!", width / 2, height / 2, paint); + mContent.endRecording(); + + mRenderer.setContentRoot(mContent); + mRenderer.setSurface(holder.getSurface()); + mRenderer.createRenderRequest() + .setVsyncTime(System.nanoTime()) + .setFrameCommitCallback(Runnable::run, () -> { + Log.d("CustomRenderer", "Frame committed!"); + }) + .syncAndDraw(); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + mRenderer.destroy(); + } + }; +} diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java new file mode 100644 index 000000000000..8bd7d797aea3 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.hwui; + +import android.app.Activity; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorSpace; +import android.graphics.HardwareRenderer; +import android.graphics.Outline; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.RenderNode; +import android.hardware.HardwareBuffer; +import android.media.Image; +import android.media.ImageReader; +import android.os.Bundle; +import android.widget.ImageView; + +public class MyLittleTextureView extends Activity { + private RenderNode mContent = new RenderNode("CustomRenderer"); + private HardwareRenderer mRenderer = new HardwareRenderer(); + private ImageView mImageView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mImageView = new ImageView(this); + mImageView.setScaleType(ImageView.ScaleType.FIT_CENTER); + setContentView(mImageView); + + ImageReader reader = ImageReader.newInstance(100, 100, PixelFormat.RGBA_8888, 3, + HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT); + mRenderer.setSurface(reader.getSurface()); + mRenderer.setLightSourceAlpha(0.0f, 1.0f); + mRenderer.setLightSourceGeometry(100 / 2f, 0f, 800.0f, 20.0f); + mContent.setLeftTopRightBottom(0, 0, 100, 100); + + Rect childRect = new Rect(25, 25, 65, 65); + RenderNode childNode = new RenderNode("shadowCaster"); + childNode.setLeftTopRightBottom(childRect.left, childRect.top, + childRect.right, childRect.bottom); + Outline outline = new Outline(); + outline.setRect(new Rect(0, 0, childRect.width(), childRect.height())); + outline.setAlpha(1f); + childNode.setOutline(outline); + { + Canvas canvas = childNode.startRecording(); + canvas.drawColor(Color.BLUE); + } + childNode.endRecording(); + childNode.setElevation(20f); + + { + Canvas canvas = mContent.startRecording(); + canvas.drawColor(Color.WHITE); + canvas.enableZ(); + canvas.drawRenderNode(childNode); + canvas.disableZ(); + } + mContent.endRecording(); + mRenderer.setContentRoot(mContent); + mRenderer.createRenderRequest() + .setWaitForPresent(true) + .syncAndDraw(); + Image image = reader.acquireNextImage(); + Bitmap bitmap = Bitmap.wrapHardwareBuffer(image.getHardwareBuffer(), + ColorSpace.get(ColorSpace.Named.SRGB)); + mImageView.setImageBitmap(bitmap); + image.close(); + } +} diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index a7c95c78d05d..a10fb4ee1305 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -20,6 +20,7 @@ import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; +import static android.net.ConnectivityManager.NETID_UNSET; import static android.net.ConnectivityManager.TYPE_ETHERNET; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA; @@ -123,7 +124,7 @@ import android.net.NetworkMisc; import android.net.NetworkParcelable; import android.net.NetworkRequest; import android.net.NetworkSpecifier; -import android.net.NetworkStack; +import android.net.NetworkStackClient; import android.net.NetworkUtils; import android.net.ProxyInfo; import android.net.RouteInfo; @@ -245,7 +246,7 @@ public class ConnectivityServiceTest { @Mock INetworkStatsService mStatsService; @Mock INetworkPolicyManager mNpm; @Mock INetd mMockNetd; - @Mock NetworkStack mNetworkStack; + @Mock NetworkStackClient mNetworkStack; private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class); @@ -901,11 +902,14 @@ public class ConnectivityServiceTest { public void setUids(Set<UidRange> uids) { mNetworkCapabilities.setUids(uids); - updateCapabilities(); + updateCapabilities(null /* defaultNetwork */); } @Override public int getNetId() { + if (mMockNetworkAgent == null) { + return NETID_UNSET; + } return mMockNetworkAgent.getNetwork().netId; } @@ -927,12 +931,13 @@ public class ConnectivityServiceTest { } @Override - public void updateCapabilities() { - if (!mConnected) return; - super.updateCapabilities(); - // Because super.updateCapabilities will update the capabilities of the agent but not - // the mock agent, the mock agent needs to know about them. + public NetworkCapabilities updateCapabilities(Network defaultNetwork) { + if (!mConnected) return null; + super.updateCapabilities(defaultNetwork); + // Because super.updateCapabilities will update the capabilities of the agent but + // not the mock agent, the mock agent needs to know about them. copyCapabilitiesToNetworkAgent(); + return new NetworkCapabilities(mNetworkCapabilities); } private void copyCapabilitiesToNetworkAgent() { @@ -1077,6 +1082,11 @@ public class ConnectivityServiceTest { } @Override + protected NetworkStackClient getNetworkStack() { + return mNetworkStack; + } + + @Override public WakeupMessage makeWakeupMessage( Context context, Handler handler, String cmdName, int cmd, Object obj) { return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj); @@ -3817,11 +3827,14 @@ public class ConnectivityServiceTest { } @Test - public void testNattSocketKeepalives() throws Exception { + public void testNattSocketKeepalives_SingleThreadExecutor() throws Exception { final ExecutorService executorSingleThread = Executors.newSingleThreadExecutor(); doTestNattSocketKeepalivesWithExecutor(executorSingleThread); executorSingleThread.shutdown(); + } + @Test + public void testNattSocketKeepalives_InlineExecutor() throws Exception { final Executor executorInline = (Runnable r) -> r.run(); doTestNattSocketKeepalivesWithExecutor(executorInline); } @@ -3963,6 +3976,7 @@ public class ConnectivityServiceTest { testSocket2.close(); mWiFiNetworkAgent.disconnect(); + waitFor(mWiFiNetworkAgent.getDisconnectedCV()); } @Test @@ -4686,6 +4700,7 @@ public class ConnectivityServiceTest { vpnNetworkAgent.connect(false); mMockVpn.connect(); + mMockVpn.setUnderlyingNetworks(new Network[0]); genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent); genericNotVpnNetworkCallback.assertNoCallback(); @@ -4718,6 +4733,7 @@ public class ConnectivityServiceTest { ranges.add(new UidRange(uid, uid)); mMockVpn.setUids(ranges); + vpnNetworkAgent.setUids(ranges); genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent); genericNotVpnNetworkCallback.assertNoCallback(); @@ -4751,12 +4767,11 @@ public class ConnectivityServiceTest { } @Test - public void testVpnWithAndWithoutInternet() { + public void testVpnWithoutInternet() { final int uid = Process.myUid(); final TestNetworkCallback defaultCallback = new TestNetworkCallback(); mCm.registerDefaultNetworkCallback(defaultCallback); - defaultCallback.assertNoCallback(); mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); @@ -4778,11 +4793,30 @@ public class ConnectivityServiceTest { vpnNetworkAgent.disconnect(); defaultCallback.assertNoCallback(); - vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); + mCm.unregisterNetworkCallback(defaultCallback); + } + + @Test + public void testVpnWithInternet() { + final int uid = Process.myUid(); + + final TestNetworkCallback defaultCallback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(defaultCallback); + + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + + defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); + final ArraySet<UidRange> ranges = new ArraySet<>(); + ranges.add(new UidRange(uid, uid)); mMockVpn.setNetworkAgent(vpnNetworkAgent); mMockVpn.setUids(ranges); vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */); mMockVpn.connect(); + defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); @@ -4790,14 +4824,6 @@ public class ConnectivityServiceTest { defaultCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent); defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); - vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); - ranges.clear(); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */); - mMockVpn.connect(); - defaultCallback.assertNoCallback(); - mCm.unregisterNetworkCallback(defaultCallback); } @@ -4900,6 +4926,70 @@ public class ConnectivityServiceTest { } @Test + public void testNullUnderlyingNetworks() { + final int uid = Process.myUid(); + + final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback(); + final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder() + .removeCapability(NET_CAPABILITY_NOT_VPN) + .addTransportType(TRANSPORT_VPN) + .build(); + NetworkCapabilities nc; + mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); + vpnNetworkCallback.assertNoCallback(); + + final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); + final ArraySet<UidRange> ranges = new ArraySet<>(); + ranges.add(new UidRange(uid, uid)); + mMockVpn.setNetworkAgent(vpnNetworkAgent); + mMockVpn.connect(); + mMockVpn.setUids(ranges); + vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */); + + vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent); + nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork()); + assertTrue(nc.hasTransport(TRANSPORT_VPN)); + assertFalse(nc.hasTransport(TRANSPORT_CELLULAR)); + assertFalse(nc.hasTransport(TRANSPORT_WIFI)); + // By default, VPN is set to track default network (i.e. its underlying networks is null). + // In case of no default network, VPN is considered metered. + assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED)); + + // Connect to Cell; Cell is the default network. + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + + vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN) + && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) + && !caps.hasCapability(NET_CAPABILITY_NOT_METERED), + vpnNetworkAgent); + + // Connect to WiFi; WiFi is the new default. + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + mWiFiNetworkAgent.connect(true); + + vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN) + && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) + && caps.hasCapability(NET_CAPABILITY_NOT_METERED), + vpnNetworkAgent); + + // Disconnect Cell. The default network did not change, so there shouldn't be any changes in + // the capabilities. + mCellNetworkAgent.disconnect(); + + // Disconnect wifi too. Now we have no default network. + mWiFiNetworkAgent.disconnect(); + + vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN) + && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) + && !caps.hasCapability(NET_CAPABILITY_NOT_METERED), + vpnNetworkAgent); + + mMockVpn.disconnect(); + } + + @Test public void testNetworkBlockedStatus() { final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); final NetworkRequest cellRequest = new NetworkRequest.Builder() diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java index e877a8f7e6c1..5057443eee07 100644 --- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java @@ -38,7 +38,6 @@ import android.net.NetworkCapabilities; import android.net.NetworkFactory; import android.net.NetworkInfo; import android.net.NetworkMisc; -import android.net.NetworkStack; import android.os.INetworkManagementService; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -75,16 +74,12 @@ public class LingerMonitorTest { @Mock NetworkMisc mMisc; @Mock NetworkNotificationManager mNotifier; @Mock Resources mResources; - @Mock NetworkStack mNetworkStack; @Before public void setUp() { MockitoAnnotations.initMocks(this); when(mCtx.getResources()).thenReturn(mResources); when(mCtx.getPackageName()).thenReturn("com.android.server.connectivity"); - when(mCtx.getSystemServiceName(NetworkStack.class)) - .thenReturn(Context.NETWORK_STACK_SERVICE); - when(mCtx.getSystemService(Context.NETWORK_STACK_SERVICE)).thenReturn(mNetworkStack); mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, HIGH_RATE_LIMIT); } diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java index a4a735d1a89d..533d7ad2a472 100644 --- a/tests/net/java/com/android/server/connectivity/TetheringTest.java +++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java @@ -195,10 +195,6 @@ public class TetheringTest { } public class MockIpServerDependencies extends IpServer.Dependencies { - MockIpServerDependencies() { - super(null); - } - @Override public RouterAdvertisementDaemon getRouterAdvertisementDaemon( InterfaceParams ifParams) { @@ -266,7 +262,7 @@ public class TetheringTest { } @Override - public IpServer.Dependencies getIpServerDependencies(Context context) { + public IpServer.Dependencies getIpServerDependencies() { return mIpServerDependencies; } diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index 46de3d0608ff..f169d6b5bee3 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -566,7 +566,7 @@ public class VpnTest { final NetworkCapabilities caps = new NetworkCapabilities(); - Vpn.updateCapabilities( + Vpn.applyUnderlyingCapabilities( mConnectivityManager, new Network[] {}, caps, false /* isAlwaysMetered */); assertTrue(caps.hasTransport(TRANSPORT_VPN)); assertFalse(caps.hasTransport(TRANSPORT_CELLULAR)); @@ -577,7 +577,7 @@ public class VpnTest { assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); - Vpn.updateCapabilities( + Vpn.applyUnderlyingCapabilities( mConnectivityManager, new Network[] {mobile}, caps, @@ -591,7 +591,7 @@ public class VpnTest { assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); - Vpn.updateCapabilities( + Vpn.applyUnderlyingCapabilities( mConnectivityManager, new Network[] {wifi}, caps, false /* isAlwaysMetered */); assertTrue(caps.hasTransport(TRANSPORT_VPN)); assertFalse(caps.hasTransport(TRANSPORT_CELLULAR)); @@ -602,7 +602,7 @@ public class VpnTest { assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); - Vpn.updateCapabilities( + Vpn.applyUnderlyingCapabilities( mConnectivityManager, new Network[] {wifi}, caps, true /* isAlwaysMetered */); assertTrue(caps.hasTransport(TRANSPORT_VPN)); assertFalse(caps.hasTransport(TRANSPORT_CELLULAR)); @@ -613,7 +613,7 @@ public class VpnTest { assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); - Vpn.updateCapabilities( + Vpn.applyUnderlyingCapabilities( mConnectivityManager, new Network[] {mobile, wifi}, caps, diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp index 5f664f5fdd5c..98324850f3f5 100644 --- a/tools/aapt2/Debug.cpp +++ b/tools/aapt2/Debug.cpp @@ -435,7 +435,7 @@ void Debug::DumpResStringPool(const android::ResStringPool* pool, text::Printer* const size_t NS = pool->size(); for (size_t s=0; s<NS; s++) { String8 str = pool->string8ObjectAt(s); - printer->Print(StringPrintf("String #%zd: %s\n", s, str.string())); + printer->Print(StringPrintf("String #%zd : %s\n", s, str.string())); } } diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index 2f8ca2d62061..c1887822ce7d 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -1084,7 +1084,7 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource // Create a overlayable entry grouping that represents this <overlayable> auto overlayable = std::make_shared<Overlayable>( overlayable_name.value(), (overlayable_actor) ? overlayable_actor.value() : "", - out_resource->source); + source_); bool error = false; std::string comment; @@ -1113,6 +1113,13 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource const std::string& element_name = parser->element_name(); const std::string& element_namespace = parser->element_namespace(); if (element_namespace.empty() && element_name == "item") { + if (current_policies == OverlayableItem::Policy::kNone) { + diag_->Error(DiagMessage(element_source) + << "<item> within an <overlayable> must be inside a <policy> block"); + error = true; + continue; + } + // Items specify the name and type of resource that should be overlayable Maybe<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name"); if (!item_name) { @@ -1169,6 +1176,8 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource current_policies |= OverlayableItem::Policy::kSystem; } else if (trimmed_part == "vendor") { current_policies |= OverlayableItem::Policy::kVendor; + } else if (trimmed_part == "signature") { + current_policies |= OverlayableItem::Policy::kSignature; } else { diag_->Error(DiagMessage(element_source) << "<policy> has unsupported type '" << trimmed_part << "'"); @@ -1176,6 +1185,11 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource continue; } } + } else { + diag_->Error(DiagMessage(element_source) + << "<policy> must have a 'type' attribute"); + error = true; + continue; } } else if (!ShouldIgnoreElement(element_namespace, element_name)) { diag_->Error(DiagMessage(element_source) << "invalid element <" << element_name << "> " diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp index 827c7deaf452..25b76b0c1fa0 100644 --- a/tools/aapt2/ResourceParser_test.cpp +++ b/tools/aapt2/ResourceParser_test.cpp @@ -894,8 +894,10 @@ TEST_F(ResourceParserTest, ParsePlatformIndependentNewline) { TEST_F(ResourceParserTest, ParseOverlayable) { std::string input = R"( <overlayable name="Name" actor="overlay://theme"> - <item type="string" name="foo" /> - <item type="drawable" name="bar" /> + <policy type="signature"> + <item type="string" name="foo" /> + <item type="drawable" name="bar" /> + </policy> </overlayable>)"; ASSERT_TRUE(TestParse(input)); @@ -906,7 +908,7 @@ TEST_F(ResourceParserTest, ParseOverlayable) { OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme")); - EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone)); + EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature)); search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar")); ASSERT_TRUE(search_result); @@ -915,7 +917,7 @@ TEST_F(ResourceParserTest, ParseOverlayable) { result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme")); - EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone)); + EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature)); } TEST_F(ResourceParserTest, ParseOverlayableRequiresName) { @@ -931,7 +933,6 @@ TEST_F(ResourceParserTest, ParseOverlayableBadActorFail) { TEST_F(ResourceParserTest, ParseOverlayablePolicy) { std::string input = R"( <overlayable name="Name"> - <item type="string" name="foo" /> <policy type="product"> <item type="string" name="bar" /> </policy> @@ -944,23 +945,18 @@ TEST_F(ResourceParserTest, ParseOverlayablePolicy) { <policy type="public"> <item type="string" name="faz" /> </policy> + <policy type="signature"> + <item type="string" name="foz" /> + </policy> </overlayable>)"; ASSERT_TRUE(TestParse(input)); - auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo")); + auto search_result = table_.FindResource(test::ParseNameOrDie("string/bar")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable_item); OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); - EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone)); - - search_result = table_.FindResource(test::ParseNameOrDie("string/bar")); - ASSERT_TRUE(search_result); - ASSERT_THAT(search_result.value().entry, NotNull()); - ASSERT_TRUE(search_result.value().entry->overlayable_item); - result_overlayable_item = search_result.value().entry->overlayable_item.value(); - EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct)); search_result = table_.FindResource(test::ParseNameOrDie("string/fiz")); @@ -986,6 +982,30 @@ TEST_F(ResourceParserTest, ParseOverlayablePolicy) { result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic)); + + search_result = table_.FindResource(test::ParseNameOrDie("string/foz")); + ASSERT_TRUE(search_result); + ASSERT_THAT(search_result.value().entry, NotNull()); + ASSERT_TRUE(search_result.value().entry->overlayable_item); + result_overlayable_item = search_result.value().entry->overlayable_item.value(); + EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); + EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature)); +} + +TEST_F(ResourceParserTest, ParseOverlayableNoPolicyError) { + std::string input = R"( + <overlayable name="Name"> + <item type="string" name="foo" /> + </overlayable>)"; + EXPECT_FALSE(TestParse(input)); + + input = R"( + <overlayable name="Name"> + <policy> + <item name="foo" /> + </policy> + </overlayable>)"; + EXPECT_FALSE(TestParse(input)); } TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) { diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h index 7ca99ea42b50..32dfd260e53c 100644 --- a/tools/aapt2/ResourceTable.h +++ b/tools/aapt2/ResourceTable.h @@ -92,6 +92,9 @@ struct OverlayableItem { // The resource can be overlaid by any overlay on the product partition. kProduct = 0x08, + + // The resource can be overlaid by any overlay signed with the same signature as its actor. + kSignature = 0x010, }; std::shared_ptr<Overlayable> overlayable; diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index ab4805f626a5..0032960ff93e 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -727,7 +727,7 @@ std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const Config // This must be a FileReference. std::unique_ptr<FileReference> file_ref = util::make_unique<FileReference>(dst_pool->MakeRef( - str, StringPool::Context(StringPool::Context::kHighPriority, config), data)); + str, StringPool::Context(StringPool::Context::kHighPriority, config))); if (type == ResourceType::kRaw) { file_ref->type = ResourceFile::Type::kUnknown; } else if (util::EndsWith(*file_ref->path, ".xml")) { @@ -739,7 +739,7 @@ std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const Config } // There are no styles associated with this string, so treat it as a simple string. - return util::make_unique<String>(dst_pool->MakeRef(str, StringPool::Context(config), data)); + return util::make_unique<String>(dst_pool->MakeRef(str, StringPool::Context(config))); } } break; diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto index 9a1d94288363..a2fd7c664b04 100644 --- a/tools/aapt2/Resources.proto +++ b/tools/aapt2/Resources.proto @@ -138,10 +138,10 @@ message AllowNew { // Represents a set of overlayable resources. message Overlayable { - // The name of the <overlyabale>. + // The name of the <overlayable>. string name = 1; - // The location of the <overlyabale> declaration in the source. + // The location of the <overlayable> declaration in the source. Source source = 2; // The component responsible for enabling and disabling overlays targeting this <overlayable>. diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp index a8c26668b3a5..8eabd3225d87 100644 --- a/tools/aapt2/StringPool.cpp +++ b/tools/aapt2/StringPool.cpp @@ -165,13 +165,12 @@ StringPool::Ref StringPool::MakeRef(const StringPiece& str) { return MakeRefImpl(str, Context{}, true); } -StringPool::Ref StringPool::MakeRef(const StringPiece& str, const Context& context, - Maybe<size_t> index) { - return MakeRefImpl(str, context, true, index); +StringPool::Ref StringPool::MakeRef(const StringPiece& str, const Context& context) { + return MakeRefImpl(str, context, true); } StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str, const Context& context, - bool unique, Maybe<size_t> index) { + bool unique) { if (unique) { auto range = indexed_strings_.equal_range(str); for (auto iter = range.first; iter != range.second; ++iter) { @@ -181,26 +180,15 @@ StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str, const Context& c } } - const size_t size = strings_.size(); - // Insert the string at the end of the string vector if no index is specified - const size_t insertion_index = index ? index.value() : size; - std::unique_ptr<Entry> entry(new Entry()); entry->value = str.to_string(); entry->context = context; - entry->index_ = insertion_index; + entry->index_ = strings_.size(); entry->ref_ = 0; entry->pool_ = this; Entry* borrow = entry.get(); - if (insertion_index == size) { - strings_.emplace_back(std::move(entry)); - } else { - // Allocate enough space for the string at the index - strings_.resize(std::max(insertion_index + 1, size)); - strings_[insertion_index] = std::move(entry); - } - + strings_.emplace_back(std::move(entry)); indexed_strings_.insert(std::make_pair(StringPiece(borrow->value), borrow)); return Ref(borrow); } diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h index 115d5d315b8f..1006ca970dc5 100644 --- a/tools/aapt2/StringPool.h +++ b/tools/aapt2/StringPool.h @@ -166,8 +166,7 @@ class StringPool { // Adds a string to the pool, unless it already exists, with a context object that can be used // when sorting the string pool. Returns a reference to the string in the pool. - Ref MakeRef(const android::StringPiece& str, const Context& context, - Maybe<size_t> index = {}); + Ref MakeRef(const android::StringPiece& str, const Context& context); // Adds a string from another string pool. Returns a reference to the string in the string pool. Ref MakeRef(const Ref& ref); @@ -211,8 +210,7 @@ class StringPool { static bool Flatten(BigBuffer* out, const StringPool& pool, bool utf8, IDiagnostics* diag); - Ref MakeRefImpl(const android::StringPiece& str, const Context& context, bool unique, - Maybe<size_t> index = {}); + Ref MakeRefImpl(const android::StringPiece& str, const Context& context, bool unique); void ReAssignIndices(); std::vector<std::unique_ptr<Entry>> strings_; diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp index 648be7d33754..9a7238b584ba 100644 --- a/tools/aapt2/StringPool_test.cpp +++ b/tools/aapt2/StringPool_test.cpp @@ -84,24 +84,6 @@ TEST(StringPoolTest, MaintainInsertionOrderIndex) { EXPECT_THAT(ref_c.index(), Eq(2u)); } -TEST(StringPoolTest, AssignStringIndex) { - StringPool pool; - - StringPool::Ref ref_a = pool.MakeRef("0", StringPool::Context{}, 0u); - StringPool::Ref ref_b = pool.MakeRef("1", StringPool::Context{}, 1u); - StringPool::Ref ref_c = pool.MakeRef("5", StringPool::Context{}, 5u); - StringPool::Ref ref_d = pool.MakeRef("2", StringPool::Context{}, 2u); - StringPool::Ref ref_e = pool.MakeRef("4", StringPool::Context{}, 4u); - StringPool::Ref ref_f = pool.MakeRef("3", StringPool::Context{}, 3u); - - EXPECT_THAT(ref_a.index(), Eq(0u)); - EXPECT_THAT(ref_b.index(), Eq(1u)); - EXPECT_THAT(ref_d.index(), Eq(2u)); - EXPECT_THAT(ref_f.index(), Eq(3u)); - EXPECT_THAT(ref_e.index(), Eq(4u)); - EXPECT_THAT(ref_c.index(), Eq(5u)); -} - TEST(StringPoolTest, PruneStringsWithNoReferences) { StringPool pool; diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp index 7a74ba925ba0..0cf86ccdd59f 100644 --- a/tools/aapt2/cmd/Convert.cpp +++ b/tools/aapt2/cmd/Convert.cpp @@ -43,7 +43,8 @@ namespace aapt { class IApkSerializer { public: - IApkSerializer(IAaptContext* context, const Source& source) : context_(context), source_(source) {} + IApkSerializer(IAaptContext* context, const Source& source) : context_(context), + source_(source) {} virtual bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16, IArchiveWriter* writer, uint32_t compression_flags) = 0; @@ -167,7 +168,7 @@ class ProtoApkSerializer : public IApkSerializer { std::unique_ptr<io::IData> data = file->file->OpenAsData(); if (!data) { context_->GetDiagnostics()->Error(DiagMessage(source_) - << "failed to open file " << *file->path); + << "failed to open file " << *file->path); return false; } @@ -175,7 +176,7 @@ class ProtoApkSerializer : public IApkSerializer { std::unique_ptr<xml::XmlResource> xml = xml::Inflate(data->data(), data->size(), &error); if (xml == nullptr) { context_->GetDiagnostics()->Error(DiagMessage(source_) << "failed to parse binary XML: " - << error); + << error); return false; } @@ -256,9 +257,6 @@ class Context : public IAaptContext { int Convert(IAaptContext* context, LoadedApk* apk, IArchiveWriter* output_writer, ApkFormat output_format, TableFlattenerOptions table_flattener_options, XmlFlattenerOptions xml_flattener_options) { - // Do not change the ordering of strings in the values string pool - table_flattener_options.sort_stringpool_entries = false; - unique_ptr<IApkSerializer> serializer; if (output_format == ApkFormat::kBinary) { serializer.reset(new BinaryApkSerializer(context, apk->GetSource(), table_flattener_options, @@ -274,7 +272,7 @@ int Convert(IAaptContext* context, LoadedApk* apk, IArchiveWriter* output_writer io::IFile* manifest = apk->GetFileCollection()->FindFile(kAndroidManifestPath); if (!serializer->SerializeXml(apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, output_writer, (manifest != nullptr && manifest->WasCompressed()) - ? ArchiveEntry::kCompress : 0u)) { + ? ArchiveEntry::kCompress : 0u)) { context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) << "failed to serialize AndroidManifest.xml"); return 1; @@ -303,8 +301,7 @@ int Convert(IAaptContext* context, LoadedApk* apk, IArchiveWriter* output_writer if (files_written.insert(*file->path).second) { if (!serializer->SerializeFile(file, output_writer)) { context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) - << "failed to serialize file " - << *file->path); + << "failed to serialize file " << *file->path); return 1; } } @@ -338,7 +335,7 @@ int Convert(IAaptContext* context, LoadedApk* apk, IArchiveWriter* output_writer if (!io::CopyFileToArchivePreserveCompression(context, file, path, output_writer)) { context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) - << "failed to copy file " << path); + << "failed to copy file " << path); return 1; } } diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index 8463046a80eb..4961aa51dc16 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -400,7 +400,8 @@ static bool IsTransitionElement(const std::string& name) { static bool IsVectorElement(const std::string& name) { return name == "vector" || name == "animated-vector" || name == "pathInterpolator" || - name == "objectAnimator" || name == "gradient" || name == "animated-selector"; + name == "objectAnimator" || name == "gradient" || name == "animated-selector" || + name == "set"; } template <typename T> diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp index 40aaa05c2b30..59eb9ec2d418 100644 --- a/tools/aapt2/format/binary/BinaryResourceParser.cpp +++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp @@ -401,7 +401,6 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package, if (entry->flags & ResTable_entry::FLAG_PUBLIC) { Visibility visibility; visibility.level = Visibility::Level::kPublic; - visibility.source = source_.WithLine(0); if (!table_->SetVisibilityWithIdMangled(name, visibility, res_id, diag_)) { return false; } @@ -448,7 +447,6 @@ bool BinaryResourceParser::ParseOverlayable(const ResChunk_header* chunk) { arraysize(header->name))); overlayable->actor = util::Utf16ToUtf8(strcpy16_dtoh((const char16_t*)header->actor, arraysize(header->name))); - overlayable->source = source_.WithLine(0); ResChunkPullParser parser(GetChunkData(chunk), GetChunkDataLen(chunk)); @@ -473,6 +471,10 @@ bool BinaryResourceParser::ParseOverlayable(const ResChunk_header* chunk) { & ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) { policies |= OverlayableItem::Policy::kProduct; } + if (policy_header->policy_flags + & ResTable_overlayable_policy_header::POLICY_SIGNATURE) { + policies |= OverlayableItem::Policy::kSignature; + } const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>( ((uint8_t *)policy_header) + util::DeviceToHost32(policy_header->header.headerSize)); @@ -491,7 +493,6 @@ bool BinaryResourceParser::ParseOverlayable(const ResChunk_header* chunk) { } OverlayableItem overlayable_item(overlayable); - overlayable_item.source = source_.WithLine(0); overlayable_item.policies = policies; if (!table_->SetOverlayable(iter->second, overlayable_item, diag_)) { return false; diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp index 9d341cc1ca4a..d677317dc98d 100644 --- a/tools/aapt2/format/binary/TableFlattener.cpp +++ b/tools/aapt2/format/binary/TableFlattener.cpp @@ -274,7 +274,9 @@ class PackageFlattener { FlattenLibrarySpec(buffer); } - FlattenOverlayable(buffer); + if (!FlattenOverlayable(buffer)) { + return false; + } pkg_writer.Finish(); return true; @@ -468,23 +470,29 @@ class PackageFlattener { overlayable_chunk = &chunk; } + if (item.policies == 0) { + context_->GetDiagnostics()->Error(DiagMessage(item.overlayable->source) + << "overlayable " + << entry->name + << " does not specify policy"); + return false; + } + uint32_t policy_flags = 0; - if (item.policies == OverlayableItem::Policy::kNone) { - // Encode overlayable entries defined without a policy as publicly overlayable + if (item.policies & OverlayableItem::Policy::kPublic) { policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC; - } else { - if (item.policies & OverlayableItem::Policy::kPublic) { - policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC; - } - if (item.policies & OverlayableItem::Policy::kSystem) { - policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION; - } - if (item.policies & OverlayableItem::Policy::kVendor) { - policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION; - } - if (item.policies & OverlayableItem::Policy::kProduct) { - policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION; - } + } + if (item.policies & OverlayableItem::Policy::kSystem) { + policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION; + } + if (item.policies & OverlayableItem::Policy::kVendor) { + policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION; + } + if (item.policies & OverlayableItem::Policy::kProduct) { + policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION; + } + if (item.policies & OverlayableItem::Policy::kSignature) { + policy_flags |= ResTable_overlayable_policy_header::POLICY_SIGNATURE; } auto policy = overlayable_chunk->policy_ids.find(policy_flags); @@ -702,17 +710,15 @@ class PackageFlattener { } // namespace bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) { - if (options_.sort_stringpool_entries) { - // We must do this before writing the resources, since the string pool IDs may change. - table->string_pool.Prune(); - table->string_pool.Sort([](const StringPool::Context &a, const StringPool::Context &b) -> int { - int diff = util::compare(a.priority, b.priority); - if (diff == 0) { - diff = a.config.compare(b.config); - } - return diff; - }); - } + // We must do this before writing the resources, since the string pool IDs may change. + table->string_pool.Prune(); + table->string_pool.Sort([](const StringPool::Context& a, const StringPool::Context& b) -> int { + int diff = util::compare(a.priority, b.priority); + if (diff == 0) { + diff = a.config.compare(b.config); + } + return diff; + }); // Write the ResTable header. ChunkWriter table_writer(buffer_); diff --git a/tools/aapt2/format/binary/TableFlattener.h b/tools/aapt2/format/binary/TableFlattener.h index 71330e3fb74f..73c17295556b 100644 --- a/tools/aapt2/format/binary/TableFlattener.h +++ b/tools/aapt2/format/binary/TableFlattener.h @@ -44,9 +44,6 @@ struct TableFlattenerOptions { // Set of whitelisted resource names to avoid altering in key stringpool std::set<std::string> whitelisted_resources; - // When true, sort the entries in the values string pool by priority and configuration. - bool sort_stringpool_entries = true; - // Map from original resource paths to shortened resource paths. std::map<std::string, std::string> shortened_path_map; }; diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp index ddc117399390..4c5dbec8ade8 100644 --- a/tools/aapt2/format/binary/TableFlattener_test.cpp +++ b/tools/aapt2/format/binary/TableFlattener_test.cpp @@ -671,9 +671,6 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) { overlayable_item_two.policies |= OverlayableItem::Policy::kSystem; overlayable_item_two.policies |= OverlayableItem::Policy::kVendor; - std::string name_three = "com.app.test:integer/overlayable_three_item"; - OverlayableItem overlayable_item_three(overlayable); - std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() .SetPackageId("com.app.test", 0x7f) @@ -683,8 +680,6 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) { .SetOverlayable(name_one, overlayable_item_one) .AddSimple(name_two, ResourceId(0x7f020002)) .SetOverlayable(name_two, overlayable_item_two) - .AddSimple(name_three, ResourceId(0x7f020003)) - .SetOverlayable(name_three, overlayable_item_three) .Build(); ResourceTable output_table; @@ -713,16 +708,6 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) { EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem | OverlayableItem::Policy::kProduct | OverlayableItem::Policy::kVendor); - - search_result = output_table.FindResource(test::ParseNameOrDie(name_three)); - ASSERT_TRUE(search_result); - ASSERT_THAT(search_result.value().entry, NotNull()); - ASSERT_TRUE(search_result.value().entry->overlayable_item); - overlayable_item = search_result.value().entry->overlayable_item.value(); - EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic); - EXPECT_EQ(overlayable_item.overlayable->name, "TestName"); - EXPECT_EQ(overlayable_item.overlayable->actor, "overlay://theme"); - EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic); } TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) { @@ -745,6 +730,8 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) { std::string name_three = "com.app.test:integer/overlayable_three"; OverlayableItem overlayable_item_three(group_one); + overlayable_item_three.policies |= OverlayableItem::Policy::kSignature; + std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() .SetPackageId("com.app.test", 0x7f) @@ -793,7 +780,22 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) { result_overlayable = search_result.value().entry->overlayable_item.value(); EXPECT_EQ(result_overlayable.overlayable->name, "OtherName"); EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization"); - EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kPublic); + EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kSignature); +} + +TEST_F(TableFlattenerTest, FlattenOverlayableNoPolicyFails) { + auto group = std::make_shared<Overlayable>("TestName", "overlay://theme"); + std::string name_zero = "com.app.test:integer/overlayable_zero"; + OverlayableItem overlayable_item_zero(group); + + std::unique_ptr<ResourceTable> table = + test::ResourceTableBuilder() + .SetPackageId("com.app.test", 0x7f) + .AddSimple(name_zero, ResourceId(0x7f020000)) + .SetOverlayable(name_zero, overlayable_item_zero) + .Build(); + ResourceTable output_table; + ASSERT_FALSE(Flatten(context_.get(), {}, table.get(), &output_table)); } } // namespace aapt diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp index aff1b391f861..06f1bf74d7e7 100644 --- a/tools/aapt2/format/proto/ProtoDeserialize.cpp +++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp @@ -390,6 +390,9 @@ bool DeserializeOverlayableItemFromPb(const pb::OverlayableItem& pb_overlayable, case pb::OverlayableItem::PRODUCT: out_overlayable->policies |= OverlayableItem::Policy::kProduct; break; + case pb::OverlayableItem::SIGNATURE: + out_overlayable->policies |= OverlayableItem::Policy::kSignature; + break; default: *out_error = "unknown overlayable policy"; return false; diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp index b549e2369f98..eb2b1a2f35d3 100644 --- a/tools/aapt2/format/proto/ProtoSerialize.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize.cpp @@ -309,6 +309,9 @@ static void SerializeOverlayableItemToPb(const OverlayableItem& overlayable_item if (overlayable_item.policies & OverlayableItem::Policy::kVendor) { pb_overlayable_item->add_policy(pb::OverlayableItem::VENDOR); } + if (overlayable_item.policies & OverlayableItem::Policy::kSignature) { + pb_overlayable_item->add_policy(pb::OverlayableItem::SIGNATURE); + } SerializeSourceToPb(overlayable_item.source, source_pool, pb_overlayable_item->mutable_source()); diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp index cce3939704cf..d369ac4c8816 100644 --- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp @@ -526,6 +526,10 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) { "FontPack", "overlay://theme")); overlayable_item_baz.policies |= OverlayableItem::Policy::kPublic; + OverlayableItem overlayable_item_boz(std::make_shared<Overlayable>( + "IconPack", "overlay://theme")); + overlayable_item_boz.policies |= OverlayableItem::Policy::kSignature; + OverlayableItem overlayable_item_biz(std::make_shared<Overlayable>( "Other", "overlay://customization")); overlayable_item_biz.comment ="comment"; @@ -536,6 +540,7 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) { .SetOverlayable("com.app.a:bool/foo", overlayable_item_foo) .SetOverlayable("com.app.a:bool/bar", overlayable_item_bar) .SetOverlayable("com.app.a:bool/baz", overlayable_item_baz) + .SetOverlayable("com.app.a:bool/boz", overlayable_item_boz) .SetOverlayable("com.app.a:bool/biz", overlayable_item_biz) .AddValue("com.app.a:bool/fiz", ResourceUtils::TryParseBool("true")) .Build(); @@ -576,6 +581,14 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) { EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme")); EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic)); + search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/boz")); + ASSERT_TRUE(search_result); + ASSERT_TRUE(search_result.value().entry->overlayable_item); + overlayable_item = search_result.value().entry->overlayable_item.value(); + EXPECT_THAT(overlayable_item.overlayable->name, Eq("IconPack")); + EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme")); + EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature)); + search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz")); ASSERT_TRUE(search_result); ASSERT_TRUE(search_result.value().entry->overlayable_item); diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index 488de8789178..af9fdfbd364d 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -276,14 +276,19 @@ public class WifiInfo implements Parcelable { /** * Returns the service set identifier (SSID) of the current 802.11 network. + * <p> * If the SSID can be decoded as UTF-8, it will be returned surrounded by double - * quotation marks. Otherwise, it is returned as a string of hex digits. The - * SSID may be <unknown ssid> if there is no network currently connected, - * or if the caller has insufficient permissions to access the SSID. - * + * quotation marks. Otherwise, it is returned as a string of hex digits. + * The SSID may be + * <lt><unknown ssid>, if there is no network currently connected or if the caller has + * insufficient permissions to access the SSID.<lt> + * </p> + * <p> * Prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method * always returned the SSID with no quotes around it. - * @return the SSID + * </p> + * + * @return the SSID. */ public String getSSID() { if (mWifiSsid != null) { @@ -312,7 +317,13 @@ public class WifiInfo implements Parcelable { /** * Return the basic service set identifier (BSSID) of the current access point. - * The BSSID may be {@code null} if there is no network currently connected. + * <p> + * The BSSID may be + * <lt>{@code null}, if there is no network currently connected.</lt> + * <lt>{@code "02:00:00:00:00:00"}, if the caller has insufficient permissions to access the + * BSSID.<lt> + * </p> + * * @return the BSSID, in the form of a six-byte MAC address: {@code XX:XX:XX:XX:XX:XX} */ public String getBSSID() { @@ -511,9 +522,13 @@ public class WifiInfo implements Parcelable { /** * Each configured network has a unique small integer ID, used to identify - * the network when performing operations on the supplicant. This method - * returns the ID for the currently connected network. - * @return the network ID, or -1 if there is no currently connected network + * the network. This method returns the ID for the currently connected network. + * <p> + * The networkId may be {@code -1} if there is no currently connected network or if the caller + * has insufficient permissions to access the network ID. + * </p> + * + * @return the network ID. */ public int getNetworkId() { return mNetworkId; diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java index 72e57a16712b..8a1b21c990c7 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java @@ -67,7 +67,7 @@ public class WifiP2pGroup implements Parcelable { /** The network id in the wpa_supplicant */ private int mNetId; - /** The frequency used by this group */ + /** The frequency (in MHz) used by this group */ private int mFrequency; /** P2P group started string pattern */ @@ -273,7 +273,7 @@ public class WifiP2pGroup implements Parcelable { this.mNetId = netId; } - /** Get the operating frequency of the p2p group */ + /** Get the operating frequency (in MHz) of the p2p group */ public int getFrequency() { return mFrequency; } |